| ... | ... | @@ -348,36 +348,18 @@ jobs: |
| 348 | 348 | echo "clerk_cli_oauth_client_id=$CLERK_CLI_OAUTH_CLIENT_ID" >> "$GITHUB_OUTPUT" |
| 349 | 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 |
| 352 | | # distro's own (Linux) Node, which can't load the Windows/Electron binary. We |
| 353 | | # build the Linux pty.node here, on Linux, and hand it to the Windows packaging |
| 354 | | # job — the Windows artifact then ships a ready WSL backend binary with no |
| 355 | | # cross-compiling and no first-launch compiler/node-gyp/network on the user's |
| 356 | | # machine. node-pty is N-API, so one binary works across all WSL Node versions. |
| 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 }}) |
| 351 | # The platform-independent JS (server bundle, web client, Electron main) is |
| 352 | # built exactly once here and handed to every platform job as `js-bundle`. |
| 353 | # The relay/Clerk values are baked into the bundle, so they belong to this |
| 354 | # job rather than to the packaging jobs. |
| 355 | build_bundle: |
| 356 | name: Build JS bundle |
| 363 | 357 | # Same gating as relay_public_config: only the release commit is needed, so |
| 364 | 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 | 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 | 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 | 363 | env: |
| 382 | 364 | T3CODE_CLERK_PUBLISHABLE_KEY: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} |
| 383 | 365 | T3CODE_CLERK_JWT_TEMPLATE: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} |
| ... | ... | @@ -398,358 +380,8 @@ jobs: |
| 398 | 380 | with: |
| 399 | 381 | node-version-file: package.json |
| 400 | 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 | 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 | 385 | # pnpm checks the lockfile and policy before reusing this result. A missing |
| 754 | 386 | # artifact leaves the cache empty, so installation runs the checks again. |
| 755 | 387 | - name: Download dependency verification |
| ... | ... | @@ -759,33 +391,10 @@ jobs: |
| 759 | 391 | name: release-dependency-verification |
| 760 | 392 | path: ${{ runner.temp }}/pnpm-metadata |
| 761 | 393 | |
| 762 | | - name: Install desktop dependencies |
| 394 | - name: Install bundle dependencies |
| 763 | 395 | env: |
| 764 | 396 | pnpm_config_cache_dir: ${{ runner.temp }}/pnpm-metadata |
| 765 | | run: vp install --filter=@t3tools/desktop... --filter=t3... --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 }} |
| 397 | run: vp install --filter=t3... --filter=@t3tools/web... --filter=@t3tools/desktop... --filter=@t3tools/scripts... |
| 789 | 398 | |
| 790 | 399 | - name: Download relay client tracing config |
| 791 | 400 | uses: actions/download-artifact@v8 |
| ... | ... | @@ -804,373 +413,190 @@ jobs: |
| 804 | 413 | - name: Align package versions to release version |
| 805 | 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 | 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 |
| 1058 | | if: matrix.cli_archive |
| 1059 | | shell: bash |
| 1060 | | env: |
| 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 |
| 418 | # @t3tools/desktop#build compiles the Linux browser secret helper on a |
| 419 | # Linux host before packing, and that needs libsecret headers. |
| 420 | - name: Install browser secret helper build libraries |
| 421 | run: sudo apt-get update && sudo apt-get install -y libsecret-1-dev pkg-config |
| 1128 | 422 | |
| 1129 | | # Enable if Windows arm64 builds are enabled. |
| 1130 | | # Windows updater metadata is channel-specific (for example |
| 1131 | | # "latest.yml" or "nightly.yml"). Suffix each per-arch copy so the |
| 1132 | | # release job can merge matching arm64/x64 manifests back into one |
| 1133 | | # canonical manifest per channel. |
| 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" |
| 423 | # Runs t3#build (which depends on @t3tools/web#build) and |
| 424 | # @t3tools/desktop#build, so apps/server/dist holds the server bundle |
| 425 | # plus the web client and apps/desktop/dist-electron the Electron main. |
| 426 | - name: Build JS bundle |
| 427 | run: vp run build:desktop |
| 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 | 432 | uses: actions/upload-artifact@v7 |
| 1156 | 433 | with: |
| 1157 | | name: desktop-${{ matrix.platform }}-${{ matrix.arch }} |
| 1158 | | path: release-publish/* |
| 434 | name: js-bundle |
| 435 | path: | |
| 436 | apps/server/dist |
| 437 | apps/desktop/dist-electron |
| 1159 | 438 | if-no-files-found: error |
| 439 | retention-days: 1 |
| 1160 | 440 | |
| 1161 | | - name: Upload resource monitor |
| 1162 | | uses: actions/upload-artifact@v7 |
| 1163 | | with: |
| 1164 | | name: resource-monitor-${{ matrix.resource_key }} |
| 1165 | | path: resource-monitor-publish/${{ matrix.resource_key }}/* |
| 1166 | | if-no-files-found: error |
| 441 | # One job per platform and architecture (see release-desktop.yml), each on |
| 442 | # hardware of its own architecture, and each gated only on what it consumes: |
| 443 | # every platform needs the JS bundle, and the Windows jobs also need the |
| 444 | # same-arch Linux job, whose CLI archive they embed as the WSL runtime. Every |
| 445 | # job builds the desktop app; all but macOS x64 also build the CLI archive |
| 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 | 594 | # Preview releases never reach npm: the archive on the GitHub Release is the |
| 1169 | 595 | # only way to obtain one, so no dist-tag can ever resolve to a preview build. |
| 1170 | 596 | publish_cli: |
| 1171 | 597 | name: Publish CLI to npm |
| 1172 | | needs: [preflight, relay_public_config, quality, build] |
| 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' }} |
| 598 | needs: [preflight, relay_public_config, quality, build_bundle] |
| 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 | 600 | runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 |
| 1175 | 601 | timeout-minutes: 10 |
| 1176 | 602 | permissions: |
| ... | ... | @@ -1199,30 +625,19 @@ jobs: |
| 1199 | 625 | run-install: | |
| 1200 | 626 | args: |
| 1201 | 627 | - --filter=t3... |
| 1202 | | - --filter=@t3tools/web... |
| 1203 | 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 | 630 | - name: Align package versions to release version |
| 1220 | 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 |
| 1223 | | # built (once) as part of this step. |
| 1224 | | - name: Build CLI package |
| 1225 | | run: vp run --filter t3 build |
| 633 | # The artifact root is `apps/` (upload-artifact keeps the least common |
| 634 | # ancestor of its paths), so extracting into `apps` restores |
| 635 | # apps/server/dist and apps/desktop/dist-electron at their build paths. |
| 636 | - name: Download JS bundle |
| 637 | uses: actions/download-artifact@v8 |
| 638 | with: |
| 639 | name: js-bundle |
| 640 | path: apps |
| 1226 | 641 | |
| 1227 | 642 | - name: Download resource monitors |
| 1228 | 643 | uses: actions/download-artifact@v8 |
| ... | ... | @@ -1247,8 +662,18 @@ jobs: |
| 1247 | 662 | |
| 1248 | 663 | release: |
| 1249 | 664 | name: Publish GitHub Release |
| 1250 | | needs: [preflight, build, build_windows_arm64_cli, publish_cli] |
| 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')) }} |
| 665 | needs: |
| 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 | 677 | runs-on: blacksmith-8vcpu-ubuntu-2404 |
| 1253 | 678 | timeout-minutes: 30 |
| 1254 | 679 | permissions: |
| ... | ... | @@ -1319,6 +744,9 @@ jobs: |
| 1319 | 744 | exit 1 |
| 1320 | 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 | 750 | - name: Merge macOS updater manifests |
| 1323 | 751 | if: needs.preflight.outputs.release_channel != 'preview' |
| 1324 | 752 | run: | |
| ... | ... | @@ -1331,6 +759,21 @@ jobs: |
| 1331 | 759 | fi |
| 1332 | 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 | 777 | # Updater manifests and blockmaps are what electron-updater consumes. |
| 1335 | 778 | # They are only listed for channels an updater is meant to follow. |
| 1336 | 779 | - id: release_files |