| author | |
| committer | |
| log | c57a04b722f2172e3be2c8f73c936d10b4582431 |
| tree | 9b7785bdee58a951c97529873769f6d491ec3466 |
| parent | 8630e1ac7a94cdae6f3014161db65385931cf3a2 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>4 files changed, 85 insertions(+), 22 deletions(-)
.github/workflows/desktop-macos-preview-publish.yml+4| ... | @@ -284,6 +284,10 @@ jobs: | ... | @@ -284,6 +284,10 @@ jobs: |
| 284 | concurrency: | 284 | concurrency: |
| 285 | group: desktop-macos-preview-${{ needs.resolve.outputs.pr_number }}-build | 285 | group: desktop-macos-preview-${{ needs.resolve.outputs.pr_number }}-build |
| 286 | cancel-in-progress: true | 286 | cancel-in-progress: true |
| 287 | # release-desktop.yml asks for actions: read (its Windows builds list artifacts). | ||
| 288 | permissions: | ||
| 289 | actions: read | ||
| 290 | contents: read | ||
| 287 | uses: ./.github/workflows/release-desktop.yml | 291 | uses: ./.github/workflows/release-desktop.yml |
| 288 | secrets: | 292 | secrets: |
| 289 | CSC_LINK: ${{ secrets.CSC_LINK }} | 293 | CSC_LINK: ${{ secrets.CSC_LINK }} |
.github/workflows/release-desktop.yml+66-10| ... | @@ -93,13 +93,17 @@ on: | ... | @@ -93,13 +93,17 @@ on: |
| 93 | type: string | 93 | type: string |
| 94 | 94 | ||
| 95 | permissions: | 95 | permissions: |
| 96 | # Windows builds read this run's artifact list to wait for the Linux CLI archive. | ||
| 97 | actions: read | ||
| 96 | contents: read | 98 | contents: read |
| 97 | 99 | ||
| 98 | jobs: | 100 | jobs: |
| 99 | build: | 101 | build: |
| 100 | name: Build ${{ inputs.label }} | 102 | name: Build ${{ inputs.label }} |
| 101 | runs-on: ${{ inputs.runner }} | 103 | runs-on: ${{ inputs.runner }} |
| 102 | timeout-minutes: 30 | 104 | # Windows waits for the Linux CLI archive inside its own budget (see |
| 105 | # "Wait for Linux CLI archive"), so it gets that wait on top of the usual 30. | ||
| 106 | timeout-minutes: ${{ inputs.platform == 'win' && 70 || 30 }} | ||
| 103 | env: | 107 | env: |
| 104 | T3CODE_CLERK_PUBLISHABLE_KEY: ${{ inputs.clerk_publishable_key }} | 108 | T3CODE_CLERK_PUBLISHABLE_KEY: ${{ inputs.clerk_publishable_key }} |
| 105 | T3CODE_CLERK_JWT_TEMPLATE: ${{ inputs.clerk_jwt_template }} | 109 | T3CODE_CLERK_JWT_TEMPLATE: ${{ inputs.clerk_jwt_template }} |
| ... | @@ -204,15 +208,6 @@ jobs: | ... | @@ -204,15 +208,6 @@ jobs: |
| 204 | name: js-bundle | 208 | name: js-bundle |
| 205 | path: apps | 209 | path: apps |
| 206 | 210 | ||
| 207 | # The WSL backend runs the Linux CLI archive inside the distro, so the | ||
| 208 | # Windows desktop embeds the same-arch archive the release attaches. | ||
| 209 | - name: Download Linux CLI archive for WSL | ||
| 210 | if: inputs.platform == 'win' | ||
| 211 | uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | ||
| 212 | with: | ||
| 213 | name: cli-linux-${{ inputs.arch }} | ||
| 214 | path: wsl-runtime | ||
| 215 | |||
| 216 | # Only the resource monitor build needs these libraries. With a cached | 211 | # Only the resource monitor build needs these libraries. With a cached |
| 217 | # monitor, the desktop build's preflight skips its MSVC check, and node-pty | 212 | # monitor, the desktop build's preflight skips its MSVC check, and node-pty |
| 218 | # ships prebuilt Windows binaries, so the 30-60s install is skipped too. | 213 | # ships prebuilt Windows binaries, so the 30-60s install is skipped too. |
| ... | @@ -314,6 +309,67 @@ jobs: | ... | @@ -314,6 +309,67 @@ jobs: |
| 314 | Select-Object -Unique | 309 | Select-Object -Unique |
| 315 | "PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV | 310 | "PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV |
| 316 | 311 | ||
| 312 | # The WSL backend runs the Linux CLI archive inside the distro, so the | ||
| 313 | # Windows desktop embeds the same-arch archive the release attaches. | ||
| 314 | # This job starts alongside that Linux job instead of after it, and the | ||
| 315 | # Linux job uploads the archive partway through, so the wait is placed | ||
| 316 | # after all other setup. It stops early if the Linux job ended without | ||
| 317 | # uploading the archive. | ||
| 318 | - name: Wait for Linux CLI archive | ||
| 319 | if: inputs.platform == 'win' | ||
| 320 | shell: pwsh | ||
| 321 | timeout-minutes: 40 | ||
| 322 | env: | ||
| 323 | GITHUB_TOKEN: ${{ github.token }} | ||
| 324 | ARTIFACT: cli-linux-${{ inputs.arch }} | ||
| 325 | LINUX_JOB: Build Linux ${{ inputs.arch }} | ||
| 326 | run: | | ||
| 327 | $headers = @{ Authorization = "Bearer $env:GITHUB_TOKEN"; Accept = "application/vnd.github+json" } | ||
| 328 | $run = "$env:GITHUB_API_URL/repos/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID" | ||
| 329 | function Test-Archive { | ||
| 330 | (Invoke-RestMethod -Headers $headers -Uri "$run/artifacts?name=$env:ARTIFACT").total_count -gt 0 | ||
| 331 | } | ||
| 332 | $missingSince = $null | ||
| 333 | while ($true) { | ||
| 334 | try { | ||
| 335 | if (Test-Archive) { | ||
| 336 | Write-Host "$env:ARTIFACT is ready." | ||
| 337 | exit 0 | ||
| 338 | } | ||
| 339 | $jobs = Invoke-RestMethod -Headers $headers -Uri "$run/attempts/$env:GITHUB_RUN_ATTEMPT/jobs?per_page=100" | ||
| 340 | $linux = $jobs.jobs | Where-Object { $_.name.EndsWith($env:LINUX_JOB) } | Select-Object -First 1 | ||
| 341 | if (-not $linux) { | ||
| 342 | # Both jobs start together, so a Linux job still missing after a | ||
| 343 | # few minutes means a renamed or skipped job, not a slow one. | ||
| 344 | if (-not $missingSince) { | ||
| 345 | $missingSince = Get-Date | ||
| 346 | } elseif (((Get-Date) - $missingSince).TotalMinutes -ge 5) { | ||
| 347 | Write-Host "::error::No job named '$env:LINUX_JOB' in this run, so $env:ARTIFACT will not arrive." | ||
| 348 | exit 1 | ||
| 349 | } | ||
| 350 | } elseif ($linux.conclusion) { | ||
| 351 | # The upload may have landed between the two requests above. | ||
| 352 | if (Test-Archive) { | ||
| 353 | Write-Host "$env:ARTIFACT is ready." | ||
| 354 | exit 0 | ||
| 355 | } | ||
| 356 | Write-Host "::error::$env:LINUX_JOB ended ($($linux.conclusion)) without uploading $env:ARTIFACT." | ||
| 357 | exit 1 | ||
| 358 | } | ||
| 359 | } catch { | ||
| 360 | Write-Warning "Could not read this run's artifacts or jobs: $($_.Exception.Message)" | ||
| 361 | } | ||
| 362 | Write-Host "Waiting for $env:ARTIFACT..." | ||
| 363 | Start-Sleep -Seconds 10 | ||
| 364 | } | ||
| 365 | |||
| 366 | - name: Download Linux CLI archive for WSL | ||
| 367 | if: inputs.platform == 'win' | ||
| 368 | uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | ||
| 369 | with: | ||
| 370 | name: cli-linux-${{ inputs.arch }} | ||
| 371 | path: wsl-runtime | ||
| 372 | |||
| 317 | - name: Build desktop artifact | 373 | - name: Build desktop artifact |
| 318 | shell: bash | 374 | shell: bash |
| 319 | env: | 375 | env: |
.github/workflows/release.yml+14-11| ... | @@ -37,6 +37,9 @@ concurrency: | ... | @@ -37,6 +37,9 @@ concurrency: |
| 37 | queue: max | 37 | queue: max |
| 38 | 38 | ||
| 39 | permissions: | 39 | permissions: |
| 40 | # The Windows desktop jobs read this run's artifact list to wait for the | ||
| 41 | # Linux CLI archive they embed (see release-desktop.yml). | ||
| 42 | actions: read | ||
| 40 | contents: read | 43 | contents: read |
| 41 | id-token: none | 44 | id-token: none |
| 42 | 45 | ||
| ... | @@ -531,11 +534,12 @@ jobs: | ... | @@ -531,11 +534,12 @@ jobs: |
| 531 | retention-days: 1 | 534 | retention-days: 1 |
| 532 | 535 | ||
| 533 | # One job per platform and architecture (see release-desktop.yml), each on | 536 | # One job per platform and architecture (see release-desktop.yml), each on |
| 534 | # hardware of its own architecture, and each gated only on what it consumes: | 537 | # hardware of its own architecture, and each gated only on the JS bundle. |
| 535 | # every platform needs the JS bundle, and the Windows jobs also need the | 538 | # The Windows jobs also embed the same-arch Linux CLI archive as the WSL |
| 536 | # same-arch Linux job, whose CLI archive they embed as the WSL runtime. Every | 539 | # runtime; they wait for that artifact partway through instead of for the |
| 537 | # job builds the desktop app; all but macOS x64 also build the CLI archive | 540 | # whole Linux job. Every job builds the desktop app; all but macOS x64 also |
| 538 | # for their platform, so a target either ships fully or not at all. | 541 | # build the CLI archive for their platform, so a target either ships fully or |
| 542 | # not at all. | ||
| 539 | desktop_mac_arm64: | 543 | desktop_mac_arm64: |
| 540 | name: Desktop macOS arm64 | 544 | name: Desktop macOS arm64 |
| 541 | needs: [preflight, relay_public_config, build_bundle] | 545 | needs: [preflight, relay_public_config, build_bundle] |
| ... | @@ -633,13 +637,12 @@ jobs: | ... | @@ -633,13 +637,12 @@ jobs: |
| 633 | resource_key: linux-arm64 | 637 | resource_key: linux-arm64 |
| 634 | cli_archive: true | 638 | cli_archive: true |
| 635 | 639 | ||
| 636 | # The Windows jobs embed the same-arch Linux CLI archive as the WSL runtime. | 640 | # The Windows jobs start with the other platforms. Their checkout, install, |
| 637 | # `!cancelled()` (not `!failure()`) still lets them start when that Linux job | 641 | # and toolchain setup take longer than the same-arch Linux job needs to |
| 638 | # failed; the download step inside then fails this single platform if the | 642 | # upload its CLI archive, so the wait inside is usually instant. |
| 639 | # archive is missing. | ||
| 640 | desktop_win_x64: | 643 | desktop_win_x64: |
| 641 | name: Desktop Windows x64 | 644 | name: Desktop Windows x64 |
| 642 | needs: [preflight, relay_public_config, build_bundle, desktop_linux_x64] | 645 | needs: [preflight, relay_public_config, build_bundle] |
| 643 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} | 646 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| 644 | uses: ./.github/workflows/release-desktop.yml | 647 | uses: ./.github/workflows/release-desktop.yml |
| 645 | secrets: inherit | 648 | secrets: inherit |
| ... | @@ -662,7 +665,7 @@ jobs: | ... | @@ -662,7 +665,7 @@ jobs: |
| 662 | 665 | ||
| 663 | desktop_win_arm64: | 666 | desktop_win_arm64: |
| 664 | name: Desktop Windows arm64 | 667 | name: Desktop Windows arm64 |
| 665 | needs: [preflight, relay_public_config, build_bundle, desktop_linux_arm64] | 668 | needs: [preflight, relay_public_config, build_bundle] |
| 666 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} | 669 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| 667 | uses: ./.github/workflows/release-desktop.yml | 670 | uses: ./.github/workflows/release-desktop.yml |
| 668 | secrets: inherit | 671 | secrets: inherit |
docs/operations/release.md+1-1| ... | @@ -26,7 +26,7 @@ This document covers the unified release workflow for stable and nightly desktop | ... | @@ -26,7 +26,7 @@ This document covers the unified release workflow for stable and nightly desktop |
| 26 | - Runs lint, typecheck, and tests alongside artifact builds. Publishing waits for every check. | 26 | - Runs lint, typecheck, and tests alongside artifact builds. Publishing waits for every check. |
| 27 | - Reads the shared production T3 Connect relay URL and Clerk client configuration before packaging clients. | 27 | - Reads the shared production T3 Connect relay URL and Clerk client configuration before packaging clients. |
| 28 | - Builds the platform-independent JS (server bundle, web client, Electron main) once in the `build_bundle` job and hands it to every platform job as the `js-bundle` artifact; the platform jobs only package it, so no runner rebuilds it. | 28 | - Builds the platform-independent JS (server bundle, web client, Electron main) once in the `build_bundle` job and hands it to every platform job as the `js-bundle` artifact; the platform jobs only package it, so no runner rebuilds it. |
| 29 | - Builds six desktop artifacts in parallel for both channels, each as its own job (`desktop_<platform>_<arch>`, one call of `release-desktop.yml`) on hardware of its own architecture, gated only on the bundle (the Windows jobs also wait for the same-arch Linux job, whose CLI archive they embed as the WSL runtime): | 29 | - Builds six desktop artifacts in parallel for both channels, each as its own job (`desktop_<platform>_<arch>`, one call of `release-desktop.yml`) on hardware of its own architecture, gated only on the bundle. The Windows jobs embed the same-arch Linux CLI archive as the WSL runtime and wait for that artifact partway through, not for the whole Linux job: |
| 30 | - macOS `arm64` DMG | 30 | - macOS `arm64` DMG |
| 31 | - macOS `x64` DMG | 31 | - macOS `x64` DMG |
| 32 | - Linux `x64` and `arm64` AppImage and `.deb`, from one electron-builder run. The `.deb` updates in the app through electron-updater, which installs it with `dpkg`. | 32 | - Linux `x64` and `arm64` AppImage and `.deb`, from one electron-builder run. The `.deb` updates in the app through electron-updater, which installs it with `dpkg`. |