Release Desktop #262
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Desktop | |
| on: | |
| push: | |
| tags: | |
| - "v*.*.*" | |
| - "!v*-nightly.*" | |
| schedule: | |
| - cron: "0 */3 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release version (for example 1.2.3 or v1.2.3)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| id-token: none | |
| env: | |
| T3CODE_RELEASE_REPOSITORY: aaditagrawal/t3code | |
| T3CODE_DESKTOP_UPDATE_REPOSITORY: aaditagrawal/t3code | |
| jobs: | |
| check_changes: | |
| name: Check for changes since last nightly | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| has_changes: ${{ steps.check.outputs.has_changes }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - id: check | |
| name: Compare HEAD to last nightly tag | |
| run: | | |
| if [[ "${GITHUB_EVENT_NAME}" != "schedule" ]]; then | |
| echo "Manual or tag release. Proceeding without nightly change detection." | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| last_nightly_tag=$(git tag --list 'v*-nightly.*' 'nightly-v*' --sort=-creatordate | head -n 1) | |
| if [[ -z "$last_nightly_tag" ]]; then | |
| echo "No previous nightly tag found. Proceeding with release." | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| last_nightly_sha=$(git rev-parse "$last_nightly_tag^{commit}") | |
| head_sha=$(git rev-parse HEAD) | |
| if [[ "$last_nightly_sha" == "$head_sha" ]]; then | |
| echo "No changes on main since last nightly release ($last_nightly_tag). Skipping." | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Changes detected on main since $last_nightly_tag ($last_nightly_sha → $head_sha). Proceeding." | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| preflight: | |
| name: Preflight | |
| needs: [check_changes] | |
| if: | | |
| !failure() && !cancelled() && | |
| (github.event_name != 'schedule' || needs.check_changes.outputs.has_changes == 'true') | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| outputs: | |
| version: ${{ steps.release_meta.outputs.version }} | |
| tag: ${{ steps.release_meta.outputs.tag }} | |
| release_name: ${{ steps.release_meta.outputs.release_name }} | |
| release_channel: ${{ steps.release_meta.outputs.release_channel }} | |
| release_repository: ${{ steps.release_meta.outputs.release_repository }} | |
| is_prerelease: ${{ steps.release_meta.outputs.is_prerelease }} | |
| make_latest: ${{ steps.release_meta.outputs.make_latest }} | |
| ref: ${{ github.sha }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - id: release_meta | |
| name: Resolve release version | |
| shell: bash | |
| run: | | |
| release_repository="${T3CODE_RELEASE_REPOSITORY:?T3CODE_RELEASE_REPOSITORY is required}" | |
| desktop_update_repository="${T3CODE_DESKTOP_UPDATE_REPOSITORY:?T3CODE_DESKTOP_UPDATE_REPOSITORY is required}" | |
| for repository in "$release_repository" "$desktop_update_repository"; do | |
| if [[ ! "$repository" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then | |
| echo "Invalid release repository: $repository" >&2 | |
| exit 1 | |
| fi | |
| done | |
| if [[ "$GITHUB_REPOSITORY" != "$release_repository" || "$GITHUB_REPOSITORY" != "$desktop_update_repository" ]]; then | |
| echo "This fork release workflow is configured for release repository $release_repository and desktop updater repository $desktop_update_repository, but it is running in $GITHUB_REPOSITORY." >&2 | |
| exit 1 | |
| fi | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then | |
| raw="${{ github.event.inputs.version }}" | |
| elif [[ "${GITHUB_EVENT_NAME}" == "schedule" ]]; then | |
| # Nightly: derive a unique prerelease version from the current | |
| # package.json base and today's date + run counter so every | |
| # scheduled run produces a valid semver like | |
| # `0.0.21-nightly.20260420.3`. | |
| base_version=$(node -e "console.log(require('./apps/desktop/package.json').version)") | |
| base_version="${base_version%%-*}" | |
| date_stamp=$(date -u +%Y%m%d) | |
| raw="${base_version}-nightly.${date_stamp}.${GITHUB_RUN_NUMBER}" | |
| else | |
| raw="${GITHUB_REF_NAME}" | |
| fi | |
| version="${raw#v}" | |
| if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ && ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+-nightly\.[0-9]{8}\.[0-9]+$ ]]; then | |
| echo "Invalid release version: $raw" >&2 | |
| exit 1 | |
| fi | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$version" >> "$GITHUB_OUTPUT" | |
| echo "release_repository=$release_repository" >> "$GITHUB_OUTPUT" | |
| if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "release_name=T3 Code v$version" >> "$GITHUB_OUTPUT" | |
| echo "release_channel=stable" >> "$GITHUB_OUTPUT" | |
| echo "is_prerelease=false" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=true" >> "$GITHUB_OUTPUT" | |
| elif [[ "$version" =~ -nightly\.[0-9]{8}\.[0-9]+$ ]]; then | |
| echo "release_name=T3 Code Nightly $version (${GITHUB_SHA::12})" >> "$GITHUB_OUTPUT" | |
| echo "release_channel=nightly" >> "$GITHUB_OUTPUT" | |
| echo "is_prerelease=true" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Unsupported prerelease version: $version" >&2 | |
| exit 1 | |
| fi | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Lint | |
| run: bun run lint | |
| - name: Typecheck | |
| run: bun run typecheck | |
| - name: Test | |
| run: bun run test | |
| build: | |
| name: Build ${{ matrix.label }} | |
| needs: preflight | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - label: macOS arm64 | |
| runner: macos-14 | |
| platform: mac | |
| target: dmg | |
| arch: arm64 | |
| - label: macOS x64 | |
| runner: macos-15-intel | |
| platform: mac | |
| target: dmg | |
| arch: x64 | |
| - label: Linux x64 | |
| runner: ubuntu-24.04 | |
| platform: linux | |
| target: AppImage | |
| arch: x64 | |
| - label: Windows x64 | |
| runner: windows-2022 | |
| platform: win | |
| target: nsis | |
| arch: x64 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| fetch-depth: 0 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Align package versions to release version | |
| run: bun run scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | |
| - name: Prepare Azure Trusted Signing | |
| if: matrix.platform == 'win' | |
| shell: pwsh | |
| env: | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | |
| AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | |
| AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | |
| AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $requiredSecrets = @( | |
| $env:AZURE_TENANT_ID, | |
| $env:AZURE_CLIENT_ID, | |
| $env:AZURE_CLIENT_SECRET, | |
| $env:AZURE_TRUSTED_SIGNING_ENDPOINT, | |
| $env:AZURE_TRUSTED_SIGNING_ACCOUNT_NAME, | |
| $env:AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME, | |
| $env:AZURE_TRUSTED_SIGNING_PUBLISHER_NAME | |
| ) | |
| if ($requiredSecrets | Where-Object { [string]::IsNullOrWhiteSpace($_) }) { | |
| Write-Host "Azure Trusted Signing disabled; skipping TrustedSigning module preparation." | |
| exit 0 | |
| } | |
| try { | |
| Install-PackageProvider ` | |
| -Name NuGet ` | |
| -MinimumVersion 2.8.5.201 ` | |
| -Force ` | |
| -Scope CurrentUser ` | |
| -ErrorAction Stop | |
| } catch { | |
| Write-Warning "Could not bootstrap NuGet package provider. Continuing because the runner may already have a usable provider. $($_.Exception.Message)" | |
| } | |
| Install-Module ` | |
| -Name TrustedSigning ` | |
| -MinimumVersion 0.5.0 ` | |
| -Force ` | |
| -AllowClobber ` | |
| -Repository PSGallery ` | |
| -Scope CurrentUser ` | |
| -ErrorAction Stop | |
| Import-Module TrustedSigning -MinimumVersion 0.5.0 -Force | |
| Get-Command Invoke-TrustedSigning -ErrorAction Stop | |
| $moduleRoots = @( | |
| [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "PowerShell", "Modules"), | |
| [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "WindowsPowerShell", "Modules"), | |
| [System.IO.Path]::Combine($env:ProgramFiles, "PowerShell", "Modules"), | |
| [System.IO.Path]::Combine($env:ProgramFiles, "WindowsPowerShell", "Modules") | |
| ) | |
| $modulePathEntries = @($moduleRoots + ($env:PSModulePath -split ";")) | | |
| Where-Object { $_ -and (Test-Path $_) } | | |
| Select-Object -Unique | |
| "PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV | |
| - name: Build desktop artifact | |
| shell: bash | |
| env: | |
| CSC_LINK: ${{ secrets.CSC_LINK }} | |
| CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | |
| APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | |
| AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | |
| AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | |
| AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | |
| run: | | |
| args=( | |
| --platform "${{ matrix.platform }}" | |
| --target "${{ matrix.target }}" | |
| --arch "${{ matrix.arch }}" | |
| --build-version "${{ needs.preflight.outputs.version }}" | |
| --verbose | |
| ) | |
| has_all() { | |
| for value in "$@"; do | |
| if [[ -z "$value" ]]; then | |
| return 1 | |
| fi | |
| done | |
| return 0 | |
| } | |
| if [[ "${{ matrix.platform }}" == "mac" ]]; then | |
| if has_all "$CSC_LINK" "$CSC_KEY_PASSWORD" "$APPLE_API_KEY" "$APPLE_API_KEY_ID" "$APPLE_API_ISSUER"; then | |
| key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8" | |
| printf '%s' "$APPLE_API_KEY" > "$key_path" | |
| export APPLE_API_KEY="$key_path" | |
| echo "macOS signing enabled." | |
| args+=(--signed) | |
| else | |
| echo "macOS signing disabled (missing one or more Apple signing secrets)." | |
| fi | |
| elif [[ "${{ matrix.platform }}" == "win" ]]; then | |
| if has_all \ | |
| "$AZURE_TENANT_ID" \ | |
| "$AZURE_CLIENT_ID" \ | |
| "$AZURE_CLIENT_SECRET" \ | |
| "$AZURE_TRUSTED_SIGNING_ENDPOINT" \ | |
| "$AZURE_TRUSTED_SIGNING_ACCOUNT_NAME" \ | |
| "$AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME" \ | |
| "$AZURE_TRUSTED_SIGNING_PUBLISHER_NAME"; then | |
| echo "Windows signing enabled (Azure Trusted Signing)." | |
| args+=(--signed) | |
| else | |
| echo "Windows signing disabled (missing one or more Azure Trusted Signing secrets)." | |
| fi | |
| else | |
| echo "Signing disabled for ${{ matrix.platform }}." | |
| fi | |
| bun run dist:desktop:artifact -- "${args[@]}" | |
| - name: Collect release assets | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release-publish | |
| shopt -s nullglob | |
| for pattern in \ | |
| "release/*.dmg" \ | |
| "release/*.zip" \ | |
| "release/*.AppImage" \ | |
| "release/*.exe" \ | |
| "release/*.blockmap" \ | |
| "release/latest*.yml" \ | |
| "release/nightly*.yml"; do | |
| for file in $pattern; do | |
| cp "$file" release-publish/ | |
| done | |
| done | |
| if [[ "${{ matrix.platform }}" == "mac" && "${{ matrix.arch }}" != "arm64" ]]; then | |
| for channel in latest nightly; do | |
| if [[ -f "release-publish/${channel}-mac.yml" ]]; then | |
| mv "release-publish/${channel}-mac.yml" "release-publish/${channel}-mac-${{ matrix.arch }}.yml" | |
| fi | |
| done | |
| fi | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: desktop-${{ matrix.platform }}-${{ matrix.arch }} | |
| path: release-publish/* | |
| if-no-files-found: error | |
| release: | |
| name: Publish GitHub Release | |
| needs: [preflight, build] | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| id-token: none | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| registry-url: https://registry.npmjs.org | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Download all desktop artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: desktop-* | |
| merge-multiple: true | |
| path: release-assets | |
| - name: Merge macOS updater manifests | |
| run: | | |
| set -euo pipefail | |
| shopt -s nullglob | |
| found_mac_manifest=false | |
| for x64_manifest in release-assets/*-mac-x64.yml; do | |
| arm64_manifest="${x64_manifest/-x64.yml/.yml}" | |
| if [[ ! -f "$arm64_manifest" ]]; then | |
| echo "Missing matching arm64 macOS manifest for $x64_manifest" >&2 | |
| exit 1 | |
| fi | |
| found_mac_manifest=true | |
| bun run scripts/merge-update-manifests.ts --platform mac \ | |
| "$arm64_manifest" \ | |
| "$x64_manifest" | |
| rm -f "$x64_manifest" | |
| done | |
| if [[ "$found_mac_manifest" != true ]]; then | |
| echo "No macOS updater manifests found to merge." >&2 | |
| exit 1 | |
| fi | |
| - name: Publish release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.preflight.outputs.tag }} | |
| target_commitish: ${{ needs.preflight.outputs.ref }} | |
| name: ${{ needs.preflight.outputs.release_name }} | |
| repository: ${{ needs.preflight.outputs.release_repository }} | |
| generate_release_notes: true | |
| prerelease: ${{ needs.preflight.outputs.is_prerelease }} | |
| make_latest: ${{ needs.preflight.outputs.make_latest }} | |
| files: | | |
| release-assets/*.dmg | |
| release-assets/*.zip | |
| release-assets/*.AppImage | |
| release-assets/*.exe | |
| release-assets/*.blockmap | |
| release-assets/*.yml | |
| fail_on_unmatched_files: true | |
| deploy_web: | |
| name: Deploy hosted web app | |
| needs: [preflight, release] | |
| # Fork: enable once this fork has its own hosted app deployment configured. | |
| if: false | |
| runs-on: blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| env: | |
| VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| T3CODE_WEB_ROUTER_URL: ${{ vars.T3CODE_WEB_ROUTER_URL }} | |
| T3CODE_WEB_LATEST_DOMAIN: ${{ vars.T3CODE_WEB_LATEST_DOMAIN }} | |
| T3CODE_WEB_NIGHTLY_DOMAIN: ${{ vars.T3CODE_WEB_NIGHTLY_DOMAIN }} | |
| VERCEL_TEAM_SLUG: ${{ vars.VERCEL_TEAM_SLUG }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - name: Install release tooling dependencies | |
| run: bun install --frozen-lockfile --filter=@t3tools/scripts --filter=@t3tools/web | |
| - name: Align package versions to release version | |
| run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | |
| - name: Refresh release lockfile | |
| run: bun install --lockfile-only --ignore-scripts | |
| - name: Deploy and alias channel | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ -z "${VERCEL_TOKEN:-}" || -z "${VERCEL_ORG_ID:-}" || -z "${VERCEL_PROJECT_ID:-}" ]]; then | |
| echo "Missing one or more required Vercel secrets: VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID." >&2 | |
| exit 1 | |
| fi | |
| router_url="${T3CODE_WEB_ROUTER_URL:-https://app.t3.codes}" | |
| latest_domain="${T3CODE_WEB_LATEST_DOMAIN:-latest.app.t3.codes}" | |
| nightly_domain="${T3CODE_WEB_NIGHTLY_DOMAIN:-nightly.app.t3.codes}" | |
| router_domain="${router_url#http://}" | |
| router_domain="${router_domain#https://}" | |
| router_domain="${router_domain%%/*}" | |
| if [[ "${{ needs.preflight.outputs.release_channel }}" == "stable" ]]; then | |
| channel_domain="$latest_domain" | |
| channel_name="latest" | |
| else | |
| channel_domain="$nightly_domain" | |
| channel_name="nightly" | |
| fi | |
| vercel_scope="${VERCEL_TEAM_SLUG:-$VERCEL_ORG_ID}" | |
| vercel_scope_args=(--scope "$vercel_scope") | |
| echo "Deploying hosted web app for $channel_name channel." | |
| deployment_url="$( | |
| bunx vercel@53.1.1 deploy \ | |
| --prod \ | |
| --skip-domain \ | |
| --yes \ | |
| --token "$VERCEL_TOKEN" \ | |
| "${vercel_scope_args[@]}" \ | |
| --build-env "APP_VERSION=${{ needs.preflight.outputs.version }}" \ | |
| --build-env "VITE_HOSTED_APP_URL=$router_url" \ | |
| --build-env "VITE_HOSTED_APP_CHANNEL=$channel_name" | |
| )" | |
| echo "Aliasing $deployment_url to $channel_domain." | |
| bunx vercel@53.1.1 alias set "$deployment_url" "$channel_domain" \ | |
| --token "$VERCEL_TOKEN" \ | |
| "${vercel_scope_args[@]}" | |
| if [[ "$channel_name" == "latest" && -n "$router_domain" && "$router_domain" != "$channel_domain" ]]; then | |
| echo "Aliasing $deployment_url to router domain $router_domain." | |
| bunx vercel@53.1.1 alias set "$deployment_url" "$router_domain" \ | |
| --token "$VERCEL_TOKEN" \ | |
| "${vercel_scope_args[@]}" | |
| fi | |
| finalize: | |
| name: Finalize release | |
| needs: [preflight, release] | |
| # secrets context is not available in job-level `if`. Use an env/output | |
| # from a prior job, or simply always run and let the token step fail-fast. | |
| if: false # Fork: enable once RELEASE_APP_ID secret is configured | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - id: app_token | |
| name: Mint release app token | |
| uses: actions/create-github-app-token@v2 | |
| with: | |
| app-id: ${{ secrets.RELEASE_APP_ID }} | |
| private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| owner: ${{ github.repository_owner }} | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| token: ${{ steps.app_token.outputs.token }} | |
| persist-credentials: true | |
| - id: app_bot | |
| name: Resolve GitHub App bot identity | |
| env: | |
| GH_TOKEN: ${{ steps.app_token.outputs.token }} | |
| APP_SLUG: ${{ steps.app_token.outputs.app-slug }} | |
| run: | | |
| user_id="$(gh api "/users/${APP_SLUG}[bot]" --jq .id)" | |
| echo "name=${APP_SLUG}[bot]" >> "$GITHUB_OUTPUT" | |
| echo "email=${user_id}+${APP_SLUG}[bot]@users.noreply.github.com" >> "$GITHUB_OUTPUT" | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - id: update_versions | |
| name: Update version strings | |
| env: | |
| RELEASE_VERSION: ${{ needs.preflight.outputs.version }} | |
| run: bun run scripts/update-release-package-versions.ts "$RELEASE_VERSION" --github-output | |
| - name: Format package.json files | |
| if: steps.update_versions.outputs.changed == 'true' | |
| run: bunx oxfmt apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json | |
| - name: Refresh lockfile | |
| if: steps.update_versions.outputs.changed == 'true' | |
| run: bun install --lockfile-only --ignore-scripts | |
| - name: Commit and push version bump | |
| if: steps.update_versions.outputs.changed == 'true' | |
| shell: bash | |
| env: | |
| RELEASE_TAG: ${{ needs.preflight.outputs.tag }} | |
| run: | | |
| if git diff --quiet -- apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json bun.lock; then | |
| echo "No version changes to commit." | |
| exit 0 | |
| fi | |
| git config user.name "${{ steps.app_bot.outputs.name }}" | |
| git config user.email "${{ steps.app_bot.outputs.email }}" | |
| git add apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json bun.lock | |
| git commit -m "chore(release): prepare $RELEASE_TAG" | |
| git push origin HEAD:main |