| ... | @@ -348,36 +348,18 @@ jobs: | ... | @@ -348,36 +348,18 @@ jobs: |
| 348 | echo "clerk_cli_oauth_client_id=$CLERK_CLI_OAUTH_CLIENT_ID" >> "$GITHUB_OUTPUT" | 348 | echo "clerk_cli_oauth_client_id=$CLERK_CLI_OAUTH_CLIENT_ID" >> "$GITHUB_OUTPUT" |
| 349 | echo "relay_url=https://$relay_domain" >> "$GITHUB_OUTPUT" | 349 | echo "relay_url=https://$relay_domain" >> "$GITHUB_OUTPUT" |
| 350 | | 350 | |
| 351 | # node-pty publishes no Linux prebuilt and the WSL backend runs under the | 351 | # The platform-independent JS (server bundle, web client, Electron main) is |
| 352 | # distro's own (Linux) Node, which can't load the Windows/Electron binary. We | 352 | # built exactly once here and handed to every platform job as `js-bundle`. |
| 353 | # build the Linux pty.node here, on Linux, and hand it to the Windows packaging | 353 | # The relay/Clerk values are baked into the bundle, so they belong to this |
| 354 | # job — the Windows artifact then ships a ready WSL backend binary with no | 354 | # job rather than to the packaging jobs. |
| 355 | # cross-compiling and no first-launch compiler/node-gyp/network on the user's | 355 | build_bundle: |
| 356 | # machine. node-pty is N-API, so one binary works across all WSL Node versions. | 356 | name: Build JS bundle |
| 357 | # The Linux CLI archive is built ahead of the desktop matrix because two | | |
| 358 | # consumers need it: the Linux desktop entry attaches it to the release, and | | |
| 359 | # the Windows desktop entry embeds it as the WSL runtime. Building it once | | |
| 360 | # here means the WSL backend runs the exact bytes a Linux user downloads. | | |
| 361 | build_linux_cli: | | |
| 362 | name: Build CLI archive (linux-${{ matrix.arch }}) | | |
| 363 | # Same gating as relay_public_config: only the release commit is needed, so | 357 | # Same gating as relay_public_config: only the release commit is needed, so |
| 364 | # this runs alongside preflight. See the condition comment there. | 358 | # this runs alongside preflight. See the condition comment there. |
| 365 | needs: [resolve_commit, preflight, relay_public_config] | 359 | needs: [preflight, relay_public_config] |
| 366 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' }} | 360 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' }} |
| 367 | runs-on: ${{ matrix.runner }} | 361 | runs-on: blacksmith-32vcpu-ubuntu-2404 |
| 368 | timeout-minutes: 30 | 362 | timeout-minutes: 30 |
| 369 | strategy: | | |
| 370 | fail-fast: false | | |
| 371 | matrix: | | |
| 372 | include: | | |
| 373 | - arch: x64 | | |
| 374 | runner: blacksmith-32vcpu-ubuntu-2404 | | |
| 375 | rust_target: x86_64-unknown-linux-gnu | | |
| 376 | # node-pty has no Linux prebuild and compiles from source, so the | | |
| 377 | # arm64 archive is built on arm64 hardware rather than cross-built. | | |
| 378 | - arch: arm64 | | |
| 379 | runner: ubuntu-24.04-arm | | |
| 380 | rust_target: aarch64-unknown-linux-gnu | | |
| 381 | env: | 363 | env: |
| 382 | T3CODE_CLERK_PUBLISHABLE_KEY: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} | 364 | T3CODE_CLERK_PUBLISHABLE_KEY: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| 383 | T3CODE_CLERK_JWT_TEMPLATE: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} | 365 | T3CODE_CLERK_JWT_TEMPLATE: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| ... | @@ -398,358 +380,8 @@ jobs: | ... | @@ -398,358 +380,8 @@ jobs: |
| 398 | with: | 380 | with: |
| 399 | node-version-file: package.json | 381 | node-version-file: package.json |
| 400 | cache: true | 382 | cache: true |
| 401 | run-install: | | | |
| 402 | args: | | |
| 403 | - --filter=t3... | | |
| 404 | - --filter=@t3tools/web... | | |
| 405 | - --filter=@t3tools/scripts... | | |
| 406 | | | |
| 407 | - name: Cache resource monitor | | |
| 408 | id: resource_monitor_cache | | |
| 409 | uses: actions/cache@v6 | | |
| 410 | with: | | |
| 411 | path: native/resource-monitor/target/${{ matrix.rust_target }}/release/t3-resource-monitor | | |
| 412 | key: resource-monitor-${{ matrix.rust_target }}-${{ hashFiles('native/resource-monitor/Cargo.lock', 'native/resource-monitor/Cargo.toml', 'native/resource-monitor/src/**') }} | | |
| 413 | | | |
| 414 | - name: Setup Rust | | |
| 415 | if: steps.resource_monitor_cache.outputs.cache-hit != 'true' | | |
| 416 | uses: dtolnay/rust-toolchain@stable | | |
| 417 | with: | | |
| 418 | targets: ${{ matrix.rust_target }} | | |
| 419 | | | |
| 420 | - name: Build resource monitor | | |
| 421 | if: steps.resource_monitor_cache.outputs.cache-hit != 'true' | | |
| 422 | run: cargo build --locked --release --manifest-path native/resource-monitor/Cargo.toml --target ${{ matrix.rust_target }} | | |
| 423 | | | |
| 424 | - name: Download relay client tracing config | | |
| 425 | uses: actions/download-artifact@v8 | | |
| 426 | with: | | |
| 427 | name: relay-client-tracing-config | | |
| 428 | path: ${{ runner.temp }}/relay-client-tracing | | |
| 429 | | | |
| 430 | - name: Load relay client tracing config | | |
| 431 | shell: bash | | |
| 432 | run: | | | |
| 433 | config_path="$RUNNER_TEMP/relay-client-tracing/relay-client-tracing.env" | | |
| 434 | tracing_token="$(sed -n 's/^T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=//p' "$config_path")" | | |
| 435 | echo "::add-mask::$tracing_token" | | |
| 436 | cat "$config_path" >> "$GITHUB_ENV" | | |
| 437 | | | |
| 438 | - name: Align package versions to release version | | |
| 439 | run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | | |
| 440 | | | |
| 441 | # The t3 build task depends on @t3tools/web#build, so the web client is | | |
| 442 | # built as part of this step. | | |
| 443 | - name: Build CLI package | | |
| 444 | run: vp run --filter t3 build | | |
| 445 | | | |
| 446 | - name: Build CLI single-executable | | |
| 447 | env: | | |
| 448 | # The exact version, not a major: vp downloads it from nodejs.org/dist on | | |
| 449 | # the runner, and only exact versions have a dist directory. Keep in | | |
| 450 | # step with SEA_NODE_VERSION in apps/server/vite.config.ts. | | |
| 451 | VP_NODE_VERSION: "26.8.2" | | |
| 452 | run: node apps/server/scripts/cli.ts build-exe --verbose | | |
| 453 | | | |
| 454 | - name: Stage resource monitor for the CLI archive | | |
| 455 | run: | | | |
| 456 | set -euo pipefail | | |
| 457 | target_dir="$RUNNER_TEMP/cli-resource-monitor/linux-${{ matrix.arch }}" | | |
| 458 | mkdir -p "$target_dir" | | |
| 459 | cp native/resource-monitor/target/${{ matrix.rust_target }}/release/t3-resource-monitor "$target_dir/" | | |
| 460 | | | |
| 461 | - name: Build CLI archive | | |
| 462 | run: | | | |
| 463 | node scripts/build-cli-archive.ts \ | | |
| 464 | --platform linux \ | | |
| 465 | --arch ${{ matrix.arch }} \ | | |
| 466 | --version "${{ needs.preflight.outputs.version }}" \ | | |
| 467 | --resource-monitor-dir "$RUNNER_TEMP/cli-resource-monitor" \ | | |
| 468 | --output-dir release-cli | | |
| 469 | | | |
| 470 | - name: Smoke-test CLI archive | | |
| 471 | run: node scripts/smoke-cli-archive.ts --archive release-cli/* --expect-version "${{ needs.preflight.outputs.version }}" | | |
| 472 | | | |
| 473 | - name: Upload CLI archive | | |
| 474 | uses: actions/upload-artifact@v7 | | |
| 475 | with: | | |
| 476 | name: cli-linux-${{ matrix.arch }} | | |
| 477 | path: release-cli/* | | |
| 478 | if-no-files-found: error | | |
| 479 | | | |
| 480 | # Windows arm64 has no desktop build yet (the NSIS arm64 row is still off), | | |
| 481 | # but the CLI archive is built here on arm64 hardware so it is signed and | | |
| 482 | # smoke-tested on the architecture it targets, like every other archive. | | |
| 483 | build_windows_arm64_cli: | | |
| 484 | name: Build CLI archive (win32-arm64) | | |
| 485 | needs: [resolve_commit, preflight, relay_public_config] | | |
| 486 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' }} | | |
| 487 | runs-on: windows-11-arm | | |
| 488 | timeout-minutes: 30 | | |
| 489 | env: | | |
| 490 | T3CODE_CLERK_PUBLISHABLE_KEY: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} | | |
| 491 | T3CODE_CLERK_JWT_TEMPLATE: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} | | |
| 492 | T3CODE_CLERK_CLI_OAUTH_CLIENT_ID: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} | | |
| 493 | T3CODE_RELAY_URL: ${{ needs.relay_public_config.outputs.relay_url }} | | |
| 494 | steps: | | |
| 495 | - name: Checkout | | |
| 496 | uses: actions/checkout@v6 | | |
| 497 | with: | | |
| 498 | ref: ${{ needs.preflight.outputs.ref }} | | |
| 499 | sparse-checkout: | | | |
| 500 | /* | | |
| 501 | !/.repos/ | | |
| 502 | sparse-checkout-cone-mode: false | | |
| 503 | | | |
| 504 | - name: Setup Vite+ | | |
| 505 | uses: voidzero-dev/setup-vp@v1 | | |
| 506 | with: | | |
| 507 | node-version-file: package.json | | |
| 508 | cache: false | | |
| 509 | run-install: false | 383 | run-install: false |
| 510 | | 384 | |
| 511 | - name: Download dependency verification | | |
| 512 | continue-on-error: true | | |
| 513 | uses: actions/download-artifact@v8 | | |
| 514 | with: | | |
| 515 | name: release-dependency-verification | | |
| 516 | path: ${{ runner.temp }}/pnpm-metadata | | |
| 517 | | | |
| 518 | - name: Install dependencies | | |
| 519 | env: | | |
| 520 | pnpm_config_cache_dir: ${{ runner.temp }}/pnpm-metadata | | |
| 521 | run: vp install --filter=t3... --filter=@t3tools/web... --filter=@t3tools/scripts... | | |
| 522 | | | |
| 523 | - name: Setup Rust | | |
| 524 | uses: dtolnay/rust-toolchain@stable | | |
| 525 | with: | | |
| 526 | targets: aarch64-pc-windows-msvc | | |
| 527 | | | |
| 528 | - name: Build resource monitor | | |
| 529 | run: cargo build --locked --release --manifest-path native/resource-monitor/Cargo.toml --target aarch64-pc-windows-msvc | | |
| 530 | | | |
| 531 | - name: Download relay client tracing config | | |
| 532 | uses: actions/download-artifact@v8 | | |
| 533 | with: | | |
| 534 | name: relay-client-tracing-config | | |
| 535 | path: ${{ runner.temp }}/relay-client-tracing | | |
| 536 | | | |
| 537 | - name: Load relay client tracing config | | |
| 538 | shell: bash | | |
| 539 | run: | | | |
| 540 | config_path="$RUNNER_TEMP/relay-client-tracing/relay-client-tracing.env" | | |
| 541 | tracing_token="$(sed -n 's/^T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=//p' "$config_path")" | | |
| 542 | echo "::add-mask::$tracing_token" | | |
| 543 | cat "$config_path" >> "$GITHUB_ENV" | | |
| 544 | | | |
| 545 | - name: Align package versions to release version | | |
| 546 | run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | | |
| 547 | | | |
| 548 | # The t3 build task depends on @t3tools/web#build, so the web client is | | |
| 549 | # built as part of this step. | | |
| 550 | - name: Build CLI package | | |
| 551 | run: vp run --filter t3 build | | |
| 552 | | | |
| 553 | - name: Build CLI single-executable | | |
| 554 | shell: bash | | |
| 555 | env: | | |
| 556 | VP_NODE_VERSION: "26.8.2" | | |
| 557 | run: node apps/server/scripts/cli.ts build-exe --verbose | | |
| 558 | | | |
| 559 | - name: Prepare Azure Trusted Signing | | |
| 560 | shell: pwsh | | |
| 561 | env: | | |
| 562 | AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | | |
| 563 | AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | | |
| 564 | AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | | |
| 565 | AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | | |
| 566 | AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | | |
| 567 | AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | | |
| 568 | AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | | |
| 569 | run: | | | |
| 570 | $ErrorActionPreference = "Stop" | | |
| 571 | | | |
| 572 | $requiredSecrets = @( | | |
| 573 | $env:AZURE_TENANT_ID, | | |
| 574 | $env:AZURE_CLIENT_ID, | | |
| 575 | $env:AZURE_CLIENT_SECRET, | | |
| 576 | $env:AZURE_TRUSTED_SIGNING_ENDPOINT, | | |
| 577 | $env:AZURE_TRUSTED_SIGNING_ACCOUNT_NAME, | | |
| 578 | $env:AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME, | | |
| 579 | $env:AZURE_TRUSTED_SIGNING_PUBLISHER_NAME | | |
| 580 | ) | | |
| 581 | if ($requiredSecrets | Where-Object { [string]::IsNullOrWhiteSpace($_) }) { | | |
| 582 | Write-Host "Azure Trusted Signing disabled; skipping TrustedSigning module preparation." | | |
| 583 | exit 0 | | |
| 584 | } | | |
| 585 | | | |
| 586 | try { | | |
| 587 | Install-PackageProvider ` | | |
| 588 | -Name NuGet ` | | |
| 589 | -MinimumVersion 2.8.5.201 ` | | |
| 590 | -Force ` | | |
| 591 | -Scope CurrentUser ` | | |
| 592 | -ErrorAction Stop | | |
| 593 | } catch { | | |
| 594 | Write-Warning "Could not bootstrap NuGet package provider. Continuing because the runner may already have a usable provider. $($_.Exception.Message)" | | |
| 595 | } | | |
| 596 | | | |
| 597 | Install-Module ` | | |
| 598 | -Name TrustedSigning ` | | |
| 599 | -MinimumVersion 0.5.0 ` | | |
| 600 | -Force ` | | |
| 601 | -AllowClobber ` | | |
| 602 | -Repository PSGallery ` | | |
| 603 | -Scope CurrentUser ` | | |
| 604 | -ErrorAction Stop | | |
| 605 | | | |
| 606 | Import-Module TrustedSigning -MinimumVersion 0.5.0 -Force | | |
| 607 | Get-Command Invoke-TrustedSigning -ErrorAction Stop | | |
| 608 | | | |
| 609 | $moduleRoots = @( | | |
| 610 | [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "PowerShell", "Modules"), | | |
| 611 | [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "WindowsPowerShell", "Modules"), | | |
| 612 | [System.IO.Path]::Combine($env:ProgramFiles, "PowerShell", "Modules"), | | |
| 613 | [System.IO.Path]::Combine($env:ProgramFiles, "WindowsPowerShell", "Modules") | | |
| 614 | ) | | |
| 615 | $modulePathEntries = @($moduleRoots + ($env:PSModulePath -split ";")) | | | |
| 616 | Where-Object { $_ -and (Test-Path $_) } | | | |
| 617 | Select-Object -Unique | | |
| 618 | "PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV | | |
| 619 | | | |
| 620 | - name: Stage resource monitor for the CLI archive | | |
| 621 | shell: bash | | |
| 622 | run: | | | |
| 623 | set -euo pipefail | | |
| 624 | target_dir="$RUNNER_TEMP/cli-resource-monitor/win32-arm64" | | |
| 625 | mkdir -p "$target_dir" | | |
| 626 | cp native/resource-monitor/target/aarch64-pc-windows-msvc/release/t3-resource-monitor.exe "$target_dir/" | | |
| 627 | | | |
| 628 | - name: Build CLI archive | | |
| 629 | shell: bash | | |
| 630 | env: | | |
| 631 | AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | | |
| 632 | AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | | |
| 633 | AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | | |
| 634 | AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | | |
| 635 | AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | | |
| 636 | AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | | |
| 637 | run: | | | |
| 638 | node scripts/build-cli-archive.ts \ | | |
| 639 | --platform win \ | | |
| 640 | --arch arm64 \ | | |
| 641 | --version "${{ needs.preflight.outputs.version }}" \ | | |
| 642 | --resource-monitor-dir "$RUNNER_TEMP/cli-resource-monitor" \ | | |
| 643 | --output-dir release-cli | | |
| 644 | | | |
| 645 | - name: Smoke-test CLI archive | | |
| 646 | shell: bash | | |
| 647 | run: node scripts/smoke-cli-archive.ts --archive release-cli/* --expect-version "${{ needs.preflight.outputs.version }}" | | |
| 648 | | | |
| 649 | - name: Upload CLI archive | | |
| 650 | uses: actions/upload-artifact@v7 | | |
| 651 | with: | | |
| 652 | name: cli-win-arm64 | | |
| 653 | path: release-cli/* | | |
| 654 | if-no-files-found: error | | |
| 655 | | | |
| 656 | build: | | |
| 657 | name: Build ${{ matrix.label }} | | |
| 658 | # build_linux_cli stays in `needs` so it runs first and its artifact is | | |
| 659 | # available to download, but only the Windows matrix entry consumes it (as | | |
| 660 | # the WSL runtime). The job is gated on preflight + relay WITHOUT requiring | | |
| 661 | # build_linux_cli, so a failed Linux archive doesn't skip the macOS builds. | | |
| 662 | # `!cancelled()` (not `!failure()`) lets the job run even when | | |
| 663 | # build_linux_cli failed; the Windows-only download step below then fails | | |
| 664 | # that single platform if the archive is missing. | | |
| 665 | needs: [preflight, relay_public_config, build_linux_cli] | | |
| 666 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' }} | | |
| 667 | runs-on: ${{ matrix.runner }} | | |
| 668 | timeout-minutes: 30 | | |
| 669 | env: | | |
| 670 | T3CODE_CLERK_PUBLISHABLE_KEY: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} | | |
| 671 | T3CODE_CLERK_JWT_TEMPLATE: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} | | |
| 672 | T3CODE_CLERK_CLI_OAUTH_CLIENT_ID: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} | | |
| 673 | T3CODE_RELAY_URL: ${{ needs.relay_public_config.outputs.relay_url }} | | |
| 674 | strategy: | | |
| 675 | fail-fast: false | | |
| 676 | matrix: | | |
| 677 | include: | | |
| 678 | # cli_archive: whether the job also builds the self-contained CLI | | |
| 679 | # archive for its own platform/arch, on this runner, and smoke-tests | | |
| 680 | # it here. Every archive is built on hardware of its own | | |
| 681 | # architecture: Linux and Windows arm64 have their own jobs below. | | |
| 682 | - label: macOS arm64 | | |
| 683 | runner: blacksmith-12vcpu-macos-26 | | |
| 684 | platform: mac | | |
| 685 | target: dmg | | |
| 686 | arch: arm64 | | |
| 687 | rust_target: aarch64-apple-darwin | | |
| 688 | resource_key: darwin-arm64 | | |
| 689 | cli_archive: true | | |
| 690 | # No CLI archive: Node single-executables are unsupported on x64 | | |
| 691 | # macOS (the SEA docs list macOS as arm64 only) and the built binary | | |
| 692 | # segfaults on start. The x64 desktop app is Electron and unaffected. | | |
| 693 | - label: macOS x64 | | |
| 694 | runner: blacksmith-12vcpu-macos-26 | | |
| 695 | platform: mac | | |
| 696 | target: dmg | | |
| 697 | arch: x64 | | |
| 698 | rust_target: x86_64-apple-darwin | | |
| 699 | resource_key: darwin-x64 | | |
| 700 | cli_archive: false | | |
| 701 | # The Linux CLI archive is produced by build_linux_cli, not here. | | |
| 702 | - label: Linux x64 | | |
| 703 | runner: blacksmith-32vcpu-ubuntu-2404 | | |
| 704 | platform: linux | | |
| 705 | target: AppImage | | |
| 706 | arch: x64 | | |
| 707 | rust_target: x86_64-unknown-linux-gnu | | |
| 708 | resource_key: linux-x64 | | |
| 709 | cli_archive: false | | |
| 710 | - label: Windows x64 | | |
| 711 | runner: blacksmith-32vcpu-windows-2025 | | |
| 712 | platform: win | | |
| 713 | target: nsis | | |
| 714 | arch: x64 | | |
| 715 | rust_target: x86_64-pc-windows-msvc | | |
| 716 | resource_key: win32-x64 | | |
| 717 | cli_archive: true | | |
| 718 | # - label: Windows arm64 | | |
| 719 | # runner: windows-11-arm | | |
| 720 | # platform: win | | |
| 721 | # target: nsis | | |
| 722 | # arch: arm64 | | |
| 723 | steps: | | |
| 724 | - name: Checkout | | |
| 725 | uses: actions/checkout@v6 | | |
| 726 | with: | | |
| 727 | ref: ${{ needs.preflight.outputs.ref }} | | |
| 728 | sparse-checkout: | | | |
| 729 | /* | | |
| 730 | !/.repos/ | | |
| 731 | sparse-checkout-cone-mode: false | | |
| 732 | | | |
| 733 | - name: Setup Vite+ | | |
| 734 | uses: voidzero-dev/setup-vp@v1 | | |
| 735 | with: | | |
| 736 | node-version-file: package.json | | |
| 737 | cache: ${{ matrix.platform != 'win' }} | | |
| 738 | run-install: false | | |
| 739 | | | |
| 740 | - name: Resolve Windows package cache path | | |
| 741 | if: matrix.platform == 'win' | | |
| 742 | id: package_cache_path | | |
| 743 | shell: pwsh | | |
| 744 | run: '"path=$(vp pm cache dir)" >> $env:GITHUB_OUTPUT' | | |
| 745 | | | |
| 746 | - name: Cache Windows packages | | |
| 747 | if: matrix.platform == 'win' | | |
| 748 | uses: actions/cache@v6 | | |
| 749 | with: | | |
| 750 | path: ${{ steps.package_cache_path.outputs.path }} | | |
| 751 | key: windows-release-packages-v1-${{ matrix.arch }}-${{ hashFiles('pnpm-lock.yaml') }} | | |
| 752 | | | |
| 753 | # pnpm checks the lockfile and policy before reusing this result. A missing | 385 | # pnpm checks the lockfile and policy before reusing this result. A missing |
| 754 | # artifact leaves the cache empty, so installation runs the checks again. | 386 | # artifact leaves the cache empty, so installation runs the checks again. |
| 755 | - name: Download dependency verification | 387 | - name: Download dependency verification |
| ... | @@ -759,33 +391,10 @@ jobs: | ... | @@ -759,33 +391,10 @@ jobs: |
| 759 | name: release-dependency-verification | 391 | name: release-dependency-verification |
| 760 | path: ${{ runner.temp }}/pnpm-metadata | 392 | path: ${{ runner.temp }}/pnpm-metadata |
| 761 | | 393 | |
| 762 | - name: Install desktop dependencies | 394 | - name: Install bundle dependencies |
| 763 | env: | 395 | env: |
| 764 | pnpm_config_cache_dir: ${{ runner.temp }}/pnpm-metadata | 396 | pnpm_config_cache_dir: ${{ runner.temp }}/pnpm-metadata |
| 765 | run: vp install --filter=@t3tools/desktop... --filter=t3... --filter=@t3tools/scripts... | 397 | run: vp install --filter=t3... --filter=@t3tools/web... --filter=@t3tools/desktop... --filter=@t3tools/scripts... |
| 766 | | | |
| 767 | - name: Cache resource monitor | | |
| 768 | id: resource_monitor_cache | | |
| 769 | uses: actions/cache@v6 | | |
| 770 | with: | | |
| 771 | path: native/resource-monitor/target/${{ matrix.rust_target }}/release/t3-resource-monitor${{ matrix.platform == 'win' && '.exe' || '' }} | | |
| 772 | key: resource-monitor-${{ matrix.rust_target }}-${{ hashFiles('native/resource-monitor/Cargo.lock', 'native/resource-monitor/Cargo.toml', 'native/resource-monitor/src/**') }} | | |
| 773 | | | |
| 774 | - name: Cache Linux capture helpers | | |
| 775 | if: matrix.platform == 'linux' | | |
| 776 | id: capture_helper_cache | | |
| 777 | uses: actions/cache@v6 | | |
| 778 | with: | | |
| 779 | path: | | | |
| 780 | native/kde-snap-shot/target/${{ matrix.rust_target }}/release/t3-kde-snap-shot | | |
| 781 | native/hyprland-snap-shot/target/${{ matrix.rust_target }}/release/t3-hyprland-snap-shot | | |
| 782 | key: linux-capture-helpers-${{ matrix.rust_target }}-${{ hashFiles('native/kde-snap-shot/Cargo.lock', 'native/kde-snap-shot/Cargo.toml', 'native/kde-snap-shot/src/**', 'native/hyprland-snap-shot/Cargo.lock', 'native/hyprland-snap-shot/Cargo.toml', 'native/hyprland-snap-shot/src/**', 'native/hyprland-snap-shot/protocols/**') }} | | |
| 783 | | | |
| 784 | - name: Setup Rust | | |
| 785 | if: steps.resource_monitor_cache.outputs.cache-hit != 'true' || (matrix.platform == 'linux' && steps.capture_helper_cache.outputs.cache-hit != 'true') | | |
| 786 | uses: dtolnay/rust-toolchain@stable | | |
| 787 | with: | | |
| 788 | targets: ${{ matrix.rust_target }} | | |
| 789 | | 398 | |
| 790 | - name: Download relay client tracing config | 399 | - name: Download relay client tracing config |
| 791 | uses: actions/download-artifact@v8 | 400 | uses: actions/download-artifact@v8 |
| ... | @@ -804,373 +413,190 @@ jobs: | ... | @@ -804,373 +413,190 @@ jobs: |
| 804 | - name: Align package versions to release version | 413 | - name: Align package versions to release version |
| 805 | run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | 414 | run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" |
| 806 | | 415 | |
| 807 | # The WSL backend runs the Linux CLI archive inside the distro, so the | | |
| 808 | # Windows desktop embeds the same archive the release attaches. | | |
| 809 | - name: Download Linux CLI archive for WSL | | |
| 810 | if: matrix.platform == 'win' | | |
| 811 | uses: actions/download-artifact@v8 | | |
| 812 | with: | | |
| 813 | name: cli-linux-x64 | | |
| 814 | path: wsl-runtime | | |
| 815 | | | |
| 816 | - name: Install Spectre-mitigated MSVC libs | | |
| 817 | if: matrix.platform == 'win' | | |
| 818 | shell: pwsh | | |
| 819 | run: | | | |
| 820 | $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" | | |
| 821 | $installPath = & $vswhere -products * -latest -property installationPath | | |
| 822 | $setupExe = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\setup.exe" | | |
| 823 | $proc = Start-Process -FilePath $setupExe ` | | |
| 824 | -ArgumentList "modify", "--installPath", "`"$installPath`"", "--add", ` | | |
| 825 | "Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre", "--quiet", "--norestart" ` | | |
| 826 | -Wait -PassThru -NoNewWindow | | |
| 827 | if ($null -eq $proc -or $proc.ExitCode -ne 0) { | | |
| 828 | $code = if ($null -ne $proc) { $proc.ExitCode } else { 1 } | | |
| 829 | Write-Error "Visual Studio Installer failed with exit code $code" | | |
| 830 | exit $code | | |
| 831 | } | | |
| 832 | | | |
| 833 | - uses: ./.github/actions/setup-apt-mirrors | 416 | - uses: ./.github/actions/setup-apt-mirrors |
| 834 | if: matrix.platform == 'linux' | | |
| 835 | | | |
| 836 | - name: Install Linux desktop build libraries | | |
| 837 | if: matrix.platform == 'linux' | | |
| 838 | shell: bash | | |
| 839 | run: | | | |
| 840 | sudo apt-get update | | |
| 841 | sudo apt-get install -y libsecret-1-dev pkg-config | | |
| 842 | if ! command -v magick >/dev/null 2>&1 && ! command -v convert >/dev/null 2>&1; then | | |
| 843 | sudo apt-get install -y imagemagick | | |
| 844 | fi | | |
| 845 | | | |
| 846 | if command -v magick >/dev/null 2>&1; then | | |
| 847 | magick -version | | |
| 848 | else | | |
| 849 | convert -version | | |
| 850 | fi | | |
| 851 | | | |
| 852 | - name: Prepare Azure Trusted Signing | | |
| 853 | if: matrix.platform == 'win' | | |
| 854 | shell: pwsh | | |
| 855 | env: | | |
| 856 | AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | | |
| 857 | AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | | |
| 858 | AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | | |
| 859 | AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | | |
| 860 | AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | | |
| 861 | AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | | |
| 862 | AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | | |
| 863 | run: | | | |
| 864 | $ErrorActionPreference = "Stop" | | |
| 865 | | | |
| 866 | $requiredSecrets = @( | | |
| 867 | $env:AZURE_TENANT_ID, | | |
| 868 | $env:AZURE_CLIENT_ID, | | |
| 869 | $env:AZURE_CLIENT_SECRET, | | |
| 870 | $env:AZURE_TRUSTED_SIGNING_ENDPOINT, | | |
| 871 | $env:AZURE_TRUSTED_SIGNING_ACCOUNT_NAME, | | |
| 872 | $env:AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME, | | |
| 873 | $env:AZURE_TRUSTED_SIGNING_PUBLISHER_NAME | | |
| 874 | ) | | |
| 875 | if ($requiredSecrets | Where-Object { [string]::IsNullOrWhiteSpace($_) }) { | | |
| 876 | Write-Host "Azure Trusted Signing disabled; skipping TrustedSigning module preparation." | | |
| 877 | exit 0 | | |
| 878 | } | | |
| 879 | | | |
| 880 | try { | | |
| 881 | Install-PackageProvider ` | | |
| 882 | -Name NuGet ` | | |
| 883 | -MinimumVersion 2.8.5.201 ` | | |
| 884 | -Force ` | | |
| 885 | -Scope CurrentUser ` | | |
| 886 | -ErrorAction Stop | | |
| 887 | } catch { | | |
| 888 | Write-Warning "Could not bootstrap NuGet package provider. Continuing because the runner may already have a usable provider. $($_.Exception.Message)" | | |
| 889 | } | | |
| 890 | | | |
| 891 | Install-Module ` | | |
| 892 | -Name TrustedSigning ` | | |
| 893 | -MinimumVersion 0.5.0 ` | | |
| 894 | -Force ` | | |
| 895 | -AllowClobber ` | | |
| 896 | -Repository PSGallery ` | | |
| 897 | -Scope CurrentUser ` | | |
| 898 | -ErrorAction Stop | | |
| 899 | | | |
| 900 | Import-Module TrustedSigning -MinimumVersion 0.5.0 -Force | | |
| 901 | Get-Command Invoke-TrustedSigning -ErrorAction Stop | | |
| 902 | | | |
| 903 | $moduleRoots = @( | | |
| 904 | [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "PowerShell", "Modules"), | | |
| 905 | [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "WindowsPowerShell", "Modules"), | | |
| 906 | [System.IO.Path]::Combine($env:ProgramFiles, "PowerShell", "Modules"), | | |
| 907 | [System.IO.Path]::Combine($env:ProgramFiles, "WindowsPowerShell", "Modules") | | |
| 908 | ) | | |
| 909 | $modulePathEntries = @($moduleRoots + ($env:PSModulePath -split ";")) | | | |
| 910 | Where-Object { $_ -and (Test-Path $_) } | | | |
| 911 | Select-Object -Unique | | |
| 912 | "PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV | | |
| 913 | | | |
| 914 | - name: Build desktop artifact | | |
| 915 | shell: bash | | |
| 916 | env: | | |
| 917 | pnpm_config_cache_dir: ${{ runner.temp }}/pnpm-metadata | | |
| 918 | T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR: ${{ steps.resource_monitor_cache.outputs.cache-hit == 'true' }} | | |
| 919 | T3CODE_DESKTOP_REUSE_LINUX_CAPTURE_HELPERS: ${{ steps.capture_helper_cache.outputs.cache-hit == 'true' }} | | |
| 920 | CSC_LINK: ${{ secrets.CSC_LINK }} | | |
| 921 | CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | | |
| 922 | APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | | |
| 923 | APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | | |
| 924 | APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | | |
| 925 | APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} | | |
| 926 | MACOS_PROVISIONING_PROFILE: ${{ secrets.MACOS_PROVISIONING_PROFILE }} | | |
| 927 | T3CODE_CLERK_PASSKEY_RP_DOMAINS: ${{ vars.CLERK_PASSKEY_RP_DOMAINS }} | | |
| 928 | AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | | |
| 929 | AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | | |
| 930 | AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | | |
| 931 | AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | | |
| 932 | AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | | |
| 933 | AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | | |
| 934 | AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | | |
| 935 | run: | | | |
| 936 | args=( | | |
| 937 | --platform "${{ matrix.platform }}" | | |
| 938 | --target "${{ matrix.target }}" | | |
| 939 | --arch "${{ matrix.arch }}" | | |
| 940 | --build-version "${{ needs.preflight.outputs.version }}" | | |
| 941 | --verbose | | |
| 942 | ) | | |
| 943 | | | |
| 944 | has_all() { | | |
| 945 | for value in "$@"; do | | |
| 946 | if [[ -z "$value" ]]; then | | |
| 947 | return 1 | | |
| 948 | fi | | |
| 949 | done | | |
| 950 | return 0 | | |
| 951 | } | | |
| 952 | | | |
| 953 | if [[ "${{ matrix.platform }}" == "mac" ]]; then | | |
| 954 | if has_all "$CSC_LINK" "$CSC_KEY_PASSWORD" "$APPLE_API_KEY" "$APPLE_API_KEY_ID" "$APPLE_API_ISSUER"; then | | |
| 955 | if ! has_all "$APPLE_TEAM_ID" "$MACOS_PROVISIONING_PROFILE"; then | | |
| 956 | echo "macOS signing is configured, but APPLE_TEAM_ID or MACOS_PROVISIONING_PROFILE is missing." >&2 | | |
| 957 | exit 1 | | |
| 958 | fi | | |
| 959 | | | |
| 960 | key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8" | | |
| 961 | printf '%s' "$APPLE_API_KEY" > "$key_path" | | |
| 962 | export APPLE_API_KEY="$key_path" | | |
| 963 | | | |
| 964 | profile_path="$RUNNER_TEMP/t3code.provisionprofile" | | |
| 965 | printf '%s' "$MACOS_PROVISIONING_PROFILE" | base64 -D > "$profile_path" | | |
| 966 | security cms -D -i "$profile_path" >/dev/null | | |
| 967 | export T3CODE_APPLE_TEAM_ID="$APPLE_TEAM_ID" | | |
| 968 | export T3CODE_MACOS_PROVISIONING_PROFILE="$profile_path" | | |
| 969 | | | |
| 970 | echo "macOS signing enabled." | | |
| 971 | args+=(--signed) | | |
| 972 | else | | |
| 973 | echo "macOS signing disabled (missing one or more Apple signing secrets)." | | |
| 974 | fi | | |
| 975 | elif [[ "${{ matrix.platform }}" == "win" ]]; then | | |
| 976 | # Embed the Linux CLI archive built by build_linux_cli as the WSL | | |
| 977 | # runtime. Required for a working WSL backend on Windows. | | |
| 978 | args+=(--wsl-runtime "$GITHUB_WORKSPACE"/wsl-runtime/t3-*-linux-x64.tar.gz) | | |
| 979 | if has_all \ | | |
| 980 | "$AZURE_TENANT_ID" \ | | |
| 981 | "$AZURE_CLIENT_ID" \ | | |
| 982 | "$AZURE_CLIENT_SECRET" \ | | |
| 983 | "$AZURE_TRUSTED_SIGNING_ENDPOINT" \ | | |
| 984 | "$AZURE_TRUSTED_SIGNING_ACCOUNT_NAME" \ | | |
| 985 | "$AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME" \ | | |
| 986 | "$AZURE_TRUSTED_SIGNING_PUBLISHER_NAME"; then | | |
| 987 | echo "Windows signing enabled (Azure Trusted Signing)." | | |
| 988 | args+=(--signed) | | |
| 989 | else | | |
| 990 | echo "Windows signing disabled (missing one or more Azure Trusted Signing secrets)." | | |
| 991 | fi | | |
| 992 | else | | |
| 993 | echo "Signing disabled for ${{ matrix.platform }}." | | |
| 994 | fi | | |
| 995 | | | |
| 996 | vp run dist:desktop:artifact "${args[@]}" | | |
| 997 | | | |
| 998 | # The single-executable is built with a Node that supports --build-sea | | |
| 999 | # (25.7+); the repo itself stays on the engines.node version. It always | | |
| 1000 | # injects into the runner's own Node: tsdown's cross-target download path | | |
| 1001 | # runs `tar` on a drive-letter path on Windows, which GNU tar reads as a | | |
| 1002 | # remote host, and a cross-built macOS binary cannot be smoke-tested. | | |
| 1003 | - name: Build CLI single-executable | | |
| 1004 | if: matrix.cli_archive | | |
| 1005 | shell: bash | | |
| 1006 | env: | | |
| 1007 | # The exact version, not a major: vp downloads it from nodejs.org/dist on | | |
| 1008 | # the runner, and only exact versions have a dist directory. Keep in | | |
| 1009 | # step with SEA_NODE_VERSION in apps/server/vite.config.ts. | | |
| 1010 | VP_NODE_VERSION: "26.8.2" | | |
| 1011 | run: node apps/server/scripts/cli.ts build-exe --verbose | | |
| 1012 | | | |
| 1013 | - name: Import macOS signing certificate for the CLI archive | | |
| 1014 | if: matrix.cli_archive && matrix.platform == 'mac' | | |
| 1015 | shell: bash | | |
| 1016 | env: | | |
| 1017 | CSC_LINK: ${{ secrets.CSC_LINK }} | | |
| 1018 | CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | | |
| 1019 | run: | | | |
| 1020 | set -euo pipefail | | |
| 1021 | if [[ -z "$CSC_LINK" || -z "$CSC_KEY_PASSWORD" ]]; then | | |
| 1022 | echo "macOS CLI signing disabled (missing CSC_LINK); the archive is signed ad hoc." | | |
| 1023 | exit 0 | | |
| 1024 | fi | | |
| 1025 | keychain="$RUNNER_TEMP/t3-cli-signing.keychain-db" | | |
| 1026 | keychain_password="$(openssl rand -hex 16)" | | |
| 1027 | cert_path="$RUNNER_TEMP/t3-cli-signing.p12" | | |
| 1028 | printf '%s' "$CSC_LINK" | base64 --decode > "$cert_path" | | |
| 1029 | security create-keychain -p "$keychain_password" "$keychain" | | |
| 1030 | security set-keychain-settings -lut 21600 "$keychain" | | |
| 1031 | security unlock-keychain -p "$keychain_password" "$keychain" | | |
| 1032 | security import "$cert_path" -k "$keychain" -P "$CSC_KEY_PASSWORD" -T /usr/bin/codesign | | |
| 1033 | security set-key-partition-list -S apple-tool:,apple: -s -k "$keychain_password" "$keychain" >/dev/null | | |
| 1034 | security list-keychains -d user -s "$keychain" $(security list-keychains -d user | tr -d '"') | | |
| 1035 | identity="$(security find-identity -v -p codesigning "$keychain" | sed -n 's/.*"\(Developer ID Application: [^"]*\)".*/\1/p' | head -n 1)" | | |
| 1036 | if [[ -z "$identity" ]]; then | | |
| 1037 | echo "No Developer ID Application identity found in CSC_LINK." >&2 | | |
| 1038 | exit 1 | | |
| 1039 | fi | | |
| 1040 | echo "::add-mask::$keychain_password" | | |
| 1041 | echo "T3CODE_CLI_MAC_SIGN_IDENTITY=$identity" >> "$GITHUB_ENV" | | |
| 1042 | echo "macOS CLI signing enabled." | | |
| 1043 | | | |
| 1044 | - name: Stage resource monitor for the CLI archive | | |
| 1045 | if: matrix.cli_archive | | |
| 1046 | shell: bash | | |
| 1047 | run: | | | |
| 1048 | set -euo pipefail | | |
| 1049 | binary_name="t3-resource-monitor" | | |
| 1050 | if [[ "${{ matrix.platform }}" == "win" ]]; then | | |
| 1051 | binary_name="${binary_name}.exe" | | |
| 1052 | fi | | |
| 1053 | target_dir="$RUNNER_TEMP/cli-resource-monitor/${{ matrix.resource_key }}" | | |
| 1054 | mkdir -p "$target_dir" | | |
| 1055 | cp "native/resource-monitor/target/${{ matrix.rust_target }}/release/${binary_name}" "$target_dir/$binary_name" | | |
| 1056 | | 417 | |
| 1057 | - name: Build CLI archive | 418 | # @t3tools/desktop#build compiles the Linux browser secret helper on a |
| 1058 | if: matrix.cli_archive | 419 | # Linux host before packing, and that needs libsecret headers. |
| 1059 | shell: bash | 420 | - name: Install browser secret helper build libraries |
| 1060 | env: | 421 | run: sudo apt-get update && sudo apt-get install -y libsecret-1-dev pkg-config |
| 1061 | APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | | |
| 1062 | APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | | |
| 1063 | APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | | |
| 1064 | AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | | |
| 1065 | AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | | |
| 1066 | AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | | |
| 1067 | AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | | |
| 1068 | AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | | |
| 1069 | AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | | |
| 1070 | run: | | | |
| 1071 | set -euo pipefail | | |
| 1072 | if [[ "${{ matrix.platform }}" == "mac" && -n "${APPLE_API_KEY:-}" ]]; then | | |
| 1073 | key_path="$RUNNER_TEMP/AuthKey_cli_${APPLE_API_KEY_ID}.p8" | | |
| 1074 | printf '%s' "$APPLE_API_KEY" > "$key_path" | | |
| 1075 | export APPLE_API_KEY="$key_path" | | |
| 1076 | fi | | |
| 1077 | node scripts/build-cli-archive.ts \ | | |
| 1078 | --platform "${{ matrix.platform }}" \ | | |
| 1079 | --arch "${{ matrix.arch }}" \ | | |
| 1080 | --version "${{ needs.preflight.outputs.version }}" \ | | |
| 1081 | --resource-monitor-dir "$RUNNER_TEMP/cli-resource-monitor" \ | | |
| 1082 | --output-dir release-cli | | |
| 1083 | | | |
| 1084 | - name: Smoke-test CLI archive | | |
| 1085 | if: matrix.cli_archive | | |
| 1086 | shell: bash | | |
| 1087 | run: node scripts/smoke-cli-archive.ts --archive release-cli/* --expect-version "${{ needs.preflight.outputs.version }}" | | |
| 1088 | | | |
| 1089 | - name: Upload CLI archive | | |
| 1090 | if: matrix.cli_archive | | |
| 1091 | uses: actions/upload-artifact@v7 | | |
| 1092 | with: | | |
| 1093 | name: cli-${{ matrix.platform }}-${{ matrix.arch }} | | |
| 1094 | path: release-cli/* | | |
| 1095 | if-no-files-found: error | | |
| 1096 | | | |
| 1097 | - name: Collect release assets | | |
| 1098 | shell: bash | | |
| 1099 | run: | | | |
| 1100 | set -euo pipefail | | |
| 1101 | mkdir -p release-publish | | |
| 1102 | | | |
| 1103 | shopt -s nullglob | | |
| 1104 | patterns=( | | |
| 1105 | "release/*.dmg" | | |
| 1106 | "release/*.zip" | | |
| 1107 | "release/*.AppImage" | | |
| 1108 | "release/*.exe" | | |
| 1109 | ) | | |
| 1110 | # Preview builds have no publish config, so electron-builder writes | | |
| 1111 | # no feed manifest for them, but it still emits blockmaps beside the | | |
| 1112 | # installers. Neither belongs on a release no updater may follow. | | |
| 1113 | if [[ "${{ needs.preflight.outputs.release_channel }}" != "preview" ]]; then | | |
| 1114 | patterns+=("release/*.blockmap" "release/*.yml") | | |
| 1115 | fi | | |
| 1116 | for pattern in "${patterns[@]}"; do | | |
| 1117 | for file in $pattern; do | | |
| 1118 | cp "$file" release-publish/ | | |
| 1119 | done | | |
| 1120 | done | | |
| 1121 | | | |
| 1122 | if [[ "${{ matrix.platform }}" == "mac" && "${{ matrix.arch }}" != "arm64" ]]; then | | |
| 1123 | shopt -s nullglob | | |
| 1124 | for manifest in release-publish/*-mac.yml; do | | |
| 1125 | mv "$manifest" "${manifest%.yml}-${{ matrix.arch }}.yml" | | |
| 1126 | done | | |
| 1127 | fi | | |
| 1128 | | 422 | |
| 1129 | # Enable if Windows arm64 builds are enabled. | 423 | # Runs t3#build (which depends on @t3tools/web#build) and |
| 1130 | # Windows updater metadata is channel-specific (for example | 424 | # @t3tools/desktop#build, so apps/server/dist holds the server bundle |
| 1131 | # "latest.yml" or "nightly.yml"). Suffix each per-arch copy so the | 425 | # plus the web client and apps/desktop/dist-electron the Electron main. |
| 1132 | # release job can merge matching arm64/x64 manifests back into one | 426 | - name: Build JS bundle |
| 1133 | # canonical manifest per channel. | 427 | run: vp run build:desktop |
| 1134 | # if [[ "${{ matrix.platform }}" == "win" ]]; then | | |
| 1135 | # shopt -s nullglob | | |
| 1136 | # for manifest in release-publish/*.yml; do | | |
| 1137 | # mv "$manifest" "${manifest%.yml}-win-${{ matrix.arch }}.yml" | | |
| 1138 | # done | | |
| 1139 | # fi | | |
| 1140 | | | |
| 1141 | - name: Collect resource monitor | | |
| 1142 | shell: bash | | |
| 1143 | run: | | | |
| 1144 | set -euo pipefail | | |
| 1145 | binary_name="t3-resource-monitor" | | |
| 1146 | if [[ "${{ matrix.platform }}" == "win" ]]; then | | |
| 1147 | binary_name="${binary_name}.exe" | | |
| 1148 | fi | | |
| 1149 | source_path="native/resource-monitor/target/${{ matrix.rust_target }}/release/${binary_name}" | | |
| 1150 | target_dir="resource-monitor-publish/${{ matrix.resource_key }}" | | |
| 1151 | mkdir -p "$target_dir" | | |
| 1152 | cp "$source_path" "$target_dir/$binary_name" | | |
| 1153 | | 428 | |
| 1154 | - name: Upload build artifacts | 429 | # Two paths under apps/ so the artifact root is apps/; consumers download |
| | 430 | # into `apps` to restore both at their original locations. |
| | 431 | - name: Upload JS bundle |
| 1155 | uses: actions/upload-artifact@v7 | 432 | uses: actions/upload-artifact@v7 |
| 1156 | with: | 433 | with: |
| 1157 | name: desktop-${{ matrix.platform }}-${{ matrix.arch }} | 434 | name: js-bundle |
| 1158 | path: release-publish/* | 435 | path: | |
| | 436 | apps/server/dist |
| | 437 | apps/desktop/dist-electron |
| 1159 | if-no-files-found: error | 438 | if-no-files-found: error |
| | 439 | retention-days: 1 |
| 1160 | | 440 | |
| 1161 | - name: Upload resource monitor | 441 | # One job per platform and architecture (see release-desktop.yml), each on |
| 1162 | uses: actions/upload-artifact@v7 | 442 | # hardware of its own architecture, and each gated only on what it consumes: |
| 1163 | with: | 443 | # every platform needs the JS bundle, and the Windows jobs also need the |
| 1164 | name: resource-monitor-${{ matrix.resource_key }} | 444 | # same-arch Linux job, whose CLI archive they embed as the WSL runtime. Every |
| 1165 | path: resource-monitor-publish/${{ matrix.resource_key }}/* | 445 | # job builds the desktop app; all but macOS x64 also build the CLI archive |
| 1166 | if-no-files-found: error | 446 | # for their platform, so a target either ships fully or not at all. |
| | 447 | desktop_mac_arm64: |
| | 448 | name: Desktop macOS arm64 |
| | 449 | needs: [preflight, relay_public_config, build_bundle] |
| | 450 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| | 451 | uses: ./.github/workflows/release-desktop.yml |
| | 452 | secrets: inherit |
| | 453 | with: |
| | 454 | version: ${{ needs.preflight.outputs.version }} |
| | 455 | ref: ${{ needs.preflight.outputs.ref }} |
| | 456 | release_channel: ${{ needs.preflight.outputs.release_channel }} |
| | 457 | clerk_publishable_key: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| | 458 | clerk_jwt_template: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| | 459 | clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} |
| | 460 | relay_url: ${{ needs.relay_public_config.outputs.relay_url }} |
| | 461 | label: macOS arm64 |
| | 462 | runner: blacksmith-12vcpu-macos-26 |
| | 463 | platform: mac |
| | 464 | target: dmg |
| | 465 | arch: arm64 |
| | 466 | rust_target: aarch64-apple-darwin |
| | 467 | resource_key: darwin-arm64 |
| | 468 | cli_archive: true |
| | 469 | |
| | 470 | desktop_mac_x64: |
| | 471 | name: Desktop macOS x64 |
| | 472 | needs: [preflight, relay_public_config, build_bundle] |
| | 473 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| | 474 | uses: ./.github/workflows/release-desktop.yml |
| | 475 | secrets: inherit |
| | 476 | with: |
| | 477 | version: ${{ needs.preflight.outputs.version }} |
| | 478 | ref: ${{ needs.preflight.outputs.ref }} |
| | 479 | release_channel: ${{ needs.preflight.outputs.release_channel }} |
| | 480 | clerk_publishable_key: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| | 481 | clerk_jwt_template: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| | 482 | clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} |
| | 483 | relay_url: ${{ needs.relay_public_config.outputs.relay_url }} |
| | 484 | label: macOS x64 |
| | 485 | runner: blacksmith-12vcpu-macos-26 |
| | 486 | platform: mac |
| | 487 | target: dmg |
| | 488 | arch: x64 |
| | 489 | rust_target: x86_64-apple-darwin |
| | 490 | resource_key: darwin-x64 |
| | 491 | # No CLI archive: Node single-executables are unsupported on x64 macOS |
| | 492 | # (the SEA docs list macOS as arm64 only) and the built binary segfaults |
| | 493 | # on start. The x64 desktop app is Electron and unaffected. |
| | 494 | cli_archive: false |
| | 495 | |
| | 496 | desktop_linux_x64: |
| | 497 | name: Desktop Linux x64 |
| | 498 | needs: [preflight, relay_public_config, build_bundle] |
| | 499 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| | 500 | uses: ./.github/workflows/release-desktop.yml |
| | 501 | secrets: inherit |
| | 502 | with: |
| | 503 | version: ${{ needs.preflight.outputs.version }} |
| | 504 | ref: ${{ needs.preflight.outputs.ref }} |
| | 505 | release_channel: ${{ needs.preflight.outputs.release_channel }} |
| | 506 | clerk_publishable_key: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| | 507 | clerk_jwt_template: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| | 508 | clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} |
| | 509 | relay_url: ${{ needs.relay_public_config.outputs.relay_url }} |
| | 510 | label: Linux x64 |
| | 511 | runner: blacksmith-32vcpu-ubuntu-2404 |
| | 512 | platform: linux |
| | 513 | target: AppImage |
| | 514 | arch: x64 |
| | 515 | rust_target: x86_64-unknown-linux-gnu |
| | 516 | resource_key: linux-x64 |
| | 517 | cli_archive: true |
| | 518 | |
| | 519 | # node-pty has no Linux prebuild and compiles from source, so the arm64 app |
| | 520 | # and archive are built on arm64 hardware rather than cross-built. |
| | 521 | desktop_linux_arm64: |
| | 522 | name: Desktop Linux arm64 |
| | 523 | needs: [preflight, relay_public_config, build_bundle] |
| | 524 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| | 525 | uses: ./.github/workflows/release-desktop.yml |
| | 526 | secrets: inherit |
| | 527 | with: |
| | 528 | version: ${{ needs.preflight.outputs.version }} |
| | 529 | ref: ${{ needs.preflight.outputs.ref }} |
| | 530 | release_channel: ${{ needs.preflight.outputs.release_channel }} |
| | 531 | clerk_publishable_key: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| | 532 | clerk_jwt_template: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| | 533 | clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} |
| | 534 | relay_url: ${{ needs.relay_public_config.outputs.relay_url }} |
| | 535 | label: Linux arm64 |
| | 536 | runner: ubuntu-24.04-arm |
| | 537 | platform: linux |
| | 538 | target: AppImage |
| | 539 | arch: arm64 |
| | 540 | rust_target: aarch64-unknown-linux-gnu |
| | 541 | resource_key: linux-arm64 |
| | 542 | cli_archive: true |
| | 543 | |
| | 544 | # The Windows jobs embed the same-arch Linux CLI archive as the WSL runtime. |
| | 545 | # `!cancelled()` (not `!failure()`) still lets them start when that Linux job |
| | 546 | # failed; the download step inside then fails this single platform if the |
| | 547 | # archive is missing. |
| | 548 | desktop_win_x64: |
| | 549 | name: Desktop Windows x64 |
| | 550 | needs: [preflight, relay_public_config, build_bundle, desktop_linux_x64] |
| | 551 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| | 552 | uses: ./.github/workflows/release-desktop.yml |
| | 553 | secrets: inherit |
| | 554 | with: |
| | 555 | version: ${{ needs.preflight.outputs.version }} |
| | 556 | ref: ${{ needs.preflight.outputs.ref }} |
| | 557 | release_channel: ${{ needs.preflight.outputs.release_channel }} |
| | 558 | clerk_publishable_key: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| | 559 | clerk_jwt_template: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| | 560 | clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} |
| | 561 | relay_url: ${{ needs.relay_public_config.outputs.relay_url }} |
| | 562 | label: Windows x64 |
| | 563 | runner: blacksmith-32vcpu-windows-2025 |
| | 564 | platform: win |
| | 565 | target: nsis |
| | 566 | arch: x64 |
| | 567 | rust_target: x86_64-pc-windows-msvc |
| | 568 | resource_key: win32-x64 |
| | 569 | cli_archive: true |
| | 570 | |
| | 571 | desktop_win_arm64: |
| | 572 | name: Desktop Windows arm64 |
| | 573 | needs: [preflight, relay_public_config, build_bundle, desktop_linux_arm64] |
| | 574 | if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build_bundle.result == 'success' }} |
| | 575 | uses: ./.github/workflows/release-desktop.yml |
| | 576 | secrets: inherit |
| | 577 | with: |
| | 578 | version: ${{ needs.preflight.outputs.version }} |
| | 579 | ref: ${{ needs.preflight.outputs.ref }} |
| | 580 | release_channel: ${{ needs.preflight.outputs.release_channel }} |
| | 581 | clerk_publishable_key: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| | 582 | clerk_jwt_template: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| | 583 | clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} |
| | 584 | relay_url: ${{ needs.relay_public_config.outputs.relay_url }} |
| | 585 | label: Windows arm64 |
| | 586 | runner: windows-11-arm |
| | 587 | platform: win |
| | 588 | target: nsis |
| | 589 | arch: arm64 |
| | 590 | rust_target: aarch64-pc-windows-msvc |
| | 591 | resource_key: win32-arm64 |
| | 592 | cli_archive: true |
| 1167 | | 593 | |
| 1168 | # Preview releases never reach npm: the archive on the GitHub Release is the | 594 | # Preview releases never reach npm: the archive on the GitHub Release is the |
| 1169 | # only way to obtain one, so no dist-tag can ever resolve to a preview build. | 595 | # only way to obtain one, so no dist-tag can ever resolve to a preview build. |
| 1170 | publish_cli: | 596 | publish_cli: |
| 1171 | name: Publish CLI to npm | 597 | name: Publish CLI to npm |
| 1172 | needs: [preflight, relay_public_config, quality, build] | 598 | needs: [preflight, relay_public_config, quality, build_bundle] |
| 1173 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' && needs.preflight.outputs.release_channel != 'preview' }} | 599 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.quality.result == 'success' && needs.build_bundle.result == 'success' && needs.preflight.outputs.release_channel != 'preview' }} |
| 1174 | runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | 600 | runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 |
| 1175 | timeout-minutes: 10 | 601 | timeout-minutes: 10 |
| 1176 | permissions: | 602 | permissions: |
| ... | @@ -1199,30 +625,19 @@ jobs: | ... | @@ -1199,30 +625,19 @@ jobs: |
| 1199 | run-install: | | 625 | run-install: | |
| 1200 | args: | 626 | args: |
| 1201 | - --filter=t3... | 627 | - --filter=t3... |
| 1202 | - --filter=@t3tools/web... | | |
| 1203 | - --filter=@t3tools/scripts... | 628 | - --filter=@t3tools/scripts... |
| 1204 | | 629 | |
| 1205 | - name: Download relay client tracing config | | |
| 1206 | uses: actions/download-artifact@v8 | | |
| 1207 | with: | | |
| 1208 | name: relay-client-tracing-config | | |
| 1209 | path: ${{ runner.temp }}/relay-client-tracing | | |
| 1210 | | | |
| 1211 | - name: Load relay client tracing config | | |
| 1212 | shell: bash | | |
| 1213 | run: | | | |
| 1214 | config_path="$RUNNER_TEMP/relay-client-tracing/relay-client-tracing.env" | | |
| 1215 | tracing_token="$(sed -n 's/^T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=//p' "$config_path")" | | |
| 1216 | echo "::add-mask::$tracing_token" | | |
| 1217 | cat "$config_path" >> "$GITHUB_ENV" | | |
| 1218 | | | |
| 1219 | - name: Align package versions to release version | 630 | - name: Align package versions to release version |
| 1220 | run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | 631 | run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" |
| 1221 | | 632 | |
| 1222 | # The t3 build task depends on @t3tools/web#build, so the web client is | 633 | # The artifact root is `apps/` (upload-artifact keeps the least common |
| 1223 | # built (once) as part of this step. | 634 | # ancestor of its paths), so extracting into `apps` restores |
| 1224 | - name: Build CLI package | 635 | # apps/server/dist and apps/desktop/dist-electron at their build paths. |
| 1225 | run: vp run --filter t3 build | 636 | - name: Download JS bundle |
| | 637 | uses: actions/download-artifact@v8 |
| | 638 | with: |
| | 639 | name: js-bundle |
| | 640 | path: apps |
| 1226 | | 641 | |
| 1227 | - name: Download resource monitors | 642 | - name: Download resource monitors |
| 1228 | uses: actions/download-artifact@v8 | 643 | uses: actions/download-artifact@v8 |
| ... | @@ -1247,8 +662,18 @@ jobs: | ... | @@ -1247,8 +662,18 @@ jobs: |
| 1247 | | 662 | |
| 1248 | release: | 663 | release: |
| 1249 | name: Publish GitHub Release | 664 | name: Publish GitHub Release |
| 1250 | needs: [preflight, build, build_windows_arm64_cli, publish_cli] | 665 | needs: |
| 1251 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' && needs.build_windows_arm64_cli.result == 'success' && (needs.publish_cli.result == 'success' || (needs.preflight.outputs.release_channel == 'preview' && needs.publish_cli.result == 'skipped')) }} | 666 | [ |
| | 667 | preflight, |
| | 668 | desktop_mac_arm64, |
| | 669 | desktop_mac_x64, |
| | 670 | desktop_linux_x64, |
| | 671 | desktop_linux_arm64, |
| | 672 | desktop_win_x64, |
| | 673 | desktop_win_arm64, |
| | 674 | publish_cli, |
| | 675 | ] |
| | 676 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.desktop_mac_arm64.result == 'success' && needs.desktop_mac_x64.result == 'success' && needs.desktop_linux_x64.result == 'success' && needs.desktop_linux_arm64.result == 'success' && needs.desktop_win_x64.result == 'success' && needs.desktop_win_arm64.result == 'success' && (needs.publish_cli.result == 'success' || (needs.preflight.outputs.release_channel == 'preview' && needs.publish_cli.result == 'skipped')) }} |
| 1252 | runs-on: blacksmith-8vcpu-ubuntu-2404 | 677 | runs-on: blacksmith-8vcpu-ubuntu-2404 |
| 1253 | timeout-minutes: 30 | 678 | timeout-minutes: 30 |
| 1254 | permissions: | 679 | permissions: |
| ... | @@ -1319,6 +744,9 @@ jobs: | ... | @@ -1319,6 +744,9 @@ jobs: |
| 1319 | exit 1 | 744 | exit 1 |
| 1320 | fi | 745 | fi |
| 1321 | | 746 | |
| | 747 | # electron-updater reads one manifest per platform and channel and picks |
| | 748 | # the file entry whose name carries the running arch, so the per-arch |
| | 749 | # manifests the build jobs wrote are merged back into that one file. |
| 1322 | - name: Merge macOS updater manifests | 750 | - name: Merge macOS updater manifests |
| 1323 | if: needs.preflight.outputs.release_channel != 'preview' | 751 | if: needs.preflight.outputs.release_channel != 'preview' |
| 1324 | run: | | 752 | run: | |
| ... | @@ -1331,6 +759,21 @@ jobs: | ... | @@ -1331,6 +759,21 @@ jobs: |
| 1331 | fi | 759 | fi |
| 1332 | done | 760 | done |
| 1333 | | 761 | |
| | 762 | - name: Merge Windows updater manifests |
| | 763 | if: needs.preflight.outputs.release_channel != 'preview' |
| | 764 | run: | |
| | 765 | shopt -s nullglob |
| | 766 | for x64_manifest in release-assets/*-win-x64.yml; do |
| | 767 | arm64_manifest="${x64_manifest%-x64.yml}-arm64.yml" |
| | 768 | merged_manifest="${x64_manifest%-win-x64.yml}.yml" |
| | 769 | if [[ -f "$arm64_manifest" ]]; then |
| | 770 | node scripts/merge-update-manifests.ts --platform win "$x64_manifest" "$arm64_manifest" "$merged_manifest" |
| | 771 | rm -f "$x64_manifest" "$arm64_manifest" |
| | 772 | else |
| | 773 | mv "$x64_manifest" "$merged_manifest" |
| | 774 | fi |
| | 775 | done |
| | 776 | |
| 1334 | # Updater manifests and blockmaps are what electron-updater consumes. | 777 | # Updater manifests and blockmaps are what electron-updater consumes. |
| 1335 | # They are only listed for channels an updater is meant to follow. | 778 | # They are only listed for channels an updater is meant to follow. |
| 1336 | - id: release_files | 779 | - id: release_files |