|
23 | 23 | type: string |
24 | 24 |
|
25 | 25 | permissions: |
26 | | - contents: write |
27 | | - id-token: write |
| 26 | + contents: read |
| 27 | + id-token: none |
28 | 28 |
|
29 | 29 | jobs: |
30 | 30 | check_changes: |
@@ -244,6 +244,68 @@ jobs: |
244 | 244 | exit $code |
245 | 245 | } |
246 | 246 |
|
| 247 | + - name: Prepare Azure Trusted Signing |
| 248 | + if: matrix.platform == 'win' |
| 249 | + shell: pwsh |
| 250 | + env: |
| 251 | + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} |
| 252 | + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} |
| 253 | + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} |
| 254 | + AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} |
| 255 | + AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} |
| 256 | + AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} |
| 257 | + AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} |
| 258 | + run: | |
| 259 | + $ErrorActionPreference = "Stop" |
| 260 | +
|
| 261 | + $requiredSecrets = @( |
| 262 | + $env:AZURE_TENANT_ID, |
| 263 | + $env:AZURE_CLIENT_ID, |
| 264 | + $env:AZURE_CLIENT_SECRET, |
| 265 | + $env:AZURE_TRUSTED_SIGNING_ENDPOINT, |
| 266 | + $env:AZURE_TRUSTED_SIGNING_ACCOUNT_NAME, |
| 267 | + $env:AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME, |
| 268 | + $env:AZURE_TRUSTED_SIGNING_PUBLISHER_NAME |
| 269 | + ) |
| 270 | + if ($requiredSecrets | Where-Object { [string]::IsNullOrWhiteSpace($_) }) { |
| 271 | + Write-Host "Azure Trusted Signing disabled; skipping TrustedSigning module preparation." |
| 272 | + exit 0 |
| 273 | + } |
| 274 | +
|
| 275 | + try { |
| 276 | + Install-PackageProvider ` |
| 277 | + -Name NuGet ` |
| 278 | + -MinimumVersion 2.8.5.201 ` |
| 279 | + -Force ` |
| 280 | + -Scope CurrentUser ` |
| 281 | + -ErrorAction Stop |
| 282 | + } catch { |
| 283 | + Write-Warning "Could not bootstrap NuGet package provider. Continuing because the runner may already have a usable provider. $($_.Exception.Message)" |
| 284 | + } |
| 285 | +
|
| 286 | + Install-Module ` |
| 287 | + -Name TrustedSigning ` |
| 288 | + -MinimumVersion 0.5.0 ` |
| 289 | + -Force ` |
| 290 | + -AllowClobber ` |
| 291 | + -Repository PSGallery ` |
| 292 | + -Scope CurrentUser ` |
| 293 | + -ErrorAction Stop |
| 294 | +
|
| 295 | + Import-Module TrustedSigning -MinimumVersion 0.5.0 -Force |
| 296 | + Get-Command Invoke-TrustedSigning -ErrorAction Stop |
| 297 | +
|
| 298 | + $moduleRoots = @( |
| 299 | + [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "PowerShell", "Modules"), |
| 300 | + [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "WindowsPowerShell", "Modules"), |
| 301 | + [System.IO.Path]::Combine($env:ProgramFiles, "PowerShell", "Modules"), |
| 302 | + [System.IO.Path]::Combine($env:ProgramFiles, "WindowsPowerShell", "Modules") |
| 303 | + ) |
| 304 | + $modulePathEntries = @($moduleRoots + ($env:PSModulePath -split ";")) | |
| 305 | + Where-Object { $_ -and (Test-Path $_) } | |
| 306 | + Select-Object -Unique |
| 307 | + "PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV |
| 308 | +
|
247 | 309 | - name: Build desktop artifact |
248 | 310 | shell: bash |
249 | 311 | env: |
@@ -358,6 +420,9 @@ jobs: |
358 | 420 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' }} |
359 | 421 | runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 |
360 | 422 | timeout-minutes: 10 |
| 423 | + permissions: |
| 424 | + contents: read |
| 425 | + id-token: write |
361 | 426 | steps: |
362 | 427 | - name: Checkout |
363 | 428 | uses: actions/checkout@v6 |
@@ -511,6 +576,98 @@ jobs: |
511 | 576 | fail_on_unmatched_files: true |
512 | 577 | token: ${{ steps.app_token.outputs.token }} |
513 | 578 |
|
| 579 | + deploy_web: |
| 580 | + name: Deploy hosted web app |
| 581 | + needs: [preflight, release] |
| 582 | + if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' }} |
| 583 | + runs-on: blacksmith-8vcpu-ubuntu-2404 |
| 584 | + timeout-minutes: 10 |
| 585 | + env: |
| 586 | + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} |
| 587 | + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} |
| 588 | + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} |
| 589 | + T3CODE_WEB_ROUTER_URL: ${{ vars.T3CODE_WEB_ROUTER_URL }} |
| 590 | + T3CODE_WEB_LATEST_DOMAIN: ${{ vars.T3CODE_WEB_LATEST_DOMAIN }} |
| 591 | + T3CODE_WEB_NIGHTLY_DOMAIN: ${{ vars.T3CODE_WEB_NIGHTLY_DOMAIN }} |
| 592 | + VERCEL_TEAM_SLUG: ${{ vars.VERCEL_TEAM_SLUG }} |
| 593 | + steps: |
| 594 | + - name: Checkout |
| 595 | + uses: actions/checkout@v6 |
| 596 | + with: |
| 597 | + ref: ${{ needs.preflight.outputs.ref }} |
| 598 | + |
| 599 | + - name: Setup Bun |
| 600 | + uses: oven-sh/setup-bun@v2 |
| 601 | + with: |
| 602 | + bun-version-file: package.json |
| 603 | + |
| 604 | + - name: Setup Node |
| 605 | + uses: actions/setup-node@v6 |
| 606 | + with: |
| 607 | + node-version-file: package.json |
| 608 | + |
| 609 | + - name: Install release tooling dependencies |
| 610 | + run: bun install --frozen-lockfile --filter=@t3tools/scripts --filter=@t3tools/web |
| 611 | + |
| 612 | + - name: Align package versions to release version |
| 613 | + run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" |
| 614 | + |
| 615 | + - name: Refresh release lockfile |
| 616 | + run: bun install --lockfile-only --ignore-scripts |
| 617 | + |
| 618 | + - name: Deploy and alias channel |
| 619 | + shell: bash |
| 620 | + run: | |
| 621 | + set -euo pipefail |
| 622 | +
|
| 623 | + if [[ -z "${VERCEL_TOKEN:-}" || -z "${VERCEL_ORG_ID:-}" || -z "${VERCEL_PROJECT_ID:-}" ]]; then |
| 624 | + echo "Missing one or more required Vercel secrets: VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID." >&2 |
| 625 | + exit 1 |
| 626 | + fi |
| 627 | +
|
| 628 | + router_url="${T3CODE_WEB_ROUTER_URL:-https://app.t3.codes}" |
| 629 | + latest_domain="${T3CODE_WEB_LATEST_DOMAIN:-latest.app.t3.codes}" |
| 630 | + nightly_domain="${T3CODE_WEB_NIGHTLY_DOMAIN:-nightly.app.t3.codes}" |
| 631 | + router_domain="${router_url#http://}" |
| 632 | + router_domain="${router_domain#https://}" |
| 633 | + router_domain="${router_domain%%/*}" |
| 634 | +
|
| 635 | + if [[ "${{ needs.preflight.outputs.release_channel }}" == "stable" ]]; then |
| 636 | + channel_domain="$latest_domain" |
| 637 | + channel_name="latest" |
| 638 | + else |
| 639 | + channel_domain="$nightly_domain" |
| 640 | + channel_name="nightly" |
| 641 | + fi |
| 642 | +
|
| 643 | + vercel_scope="${VERCEL_TEAM_SLUG:-$VERCEL_ORG_ID}" |
| 644 | + vercel_scope_args=(--scope "$vercel_scope") |
| 645 | +
|
| 646 | + echo "Deploying hosted web app for $channel_name channel." |
| 647 | + deployment_url="$( |
| 648 | + |
| 649 | + --prod \ |
| 650 | + --skip-domain \ |
| 651 | + --yes \ |
| 652 | + --token "$VERCEL_TOKEN" \ |
| 653 | + "${vercel_scope_args[@]}" \ |
| 654 | + --build-env "APP_VERSION=${{ needs.preflight.outputs.version }}" \ |
| 655 | + --build-env "VITE_HOSTED_APP_URL=$router_url" \ |
| 656 | + --build-env "VITE_HOSTED_APP_CHANNEL=$channel_name" |
| 657 | + )" |
| 658 | +
|
| 659 | + echo "Aliasing $deployment_url to $channel_domain." |
| 660 | + bunx [email protected] alias set "$deployment_url" "$channel_domain" \ |
| 661 | + --token "$VERCEL_TOKEN" \ |
| 662 | + "${vercel_scope_args[@]}" |
| 663 | +
|
| 664 | + if [[ "$channel_name" == "latest" && -n "$router_domain" && "$router_domain" != "$channel_domain" ]]; then |
| 665 | + echo "Aliasing $deployment_url to router domain $router_domain." |
| 666 | + bunx [email protected] alias set "$deployment_url" "$router_domain" \ |
| 667 | + --token "$VERCEL_TOKEN" \ |
| 668 | + "${vercel_scope_args[@]}" |
| 669 | + fi |
| 670 | +
|
514 | 671 | finalize: |
515 | 672 | name: Finalize release |
516 | 673 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' && needs.preflight.outputs.release_channel == 'stable' }} |
@@ -595,8 +752,9 @@ jobs: |
595 | 752 | always() && !cancelled() && |
596 | 753 | needs.preflight.result == 'success' && |
597 | 754 | needs.release.result == 'success' && |
| 755 | + needs.deploy_web.result == 'success' && |
598 | 756 | (needs.finalize.result == 'success' || needs.finalize.result == 'skipped') |
599 | | - needs: [preflight, release, finalize] |
| 757 | + needs: [preflight, release, deploy_web, finalize] |
600 | 758 | runs-on: blacksmith-8vcpu-ubuntu-2404 |
601 | 759 | timeout-minutes: 10 |
602 | 760 | steps: |
|
0 commit comments