| author | |
| committer | |
| log | 25dcee00a6e12db2781a17b326e6e34de0d4ced7 |
| tree | d12c2e7bcbe33ff826a1c232f0f747fea602ee6d |
| parent | f70eeeeb06d6d96f292be7eef1ed8948103e68dc |
| signature | Signed by PGP key B5690EEEBB952194 |
6 files changed, 251 insertions(+), 120 deletions(-)
.github/workflows/release.yml+63-19| ... | ... | @@ -100,9 +100,6 @@ jobs: |
| 100 | 100 | cache: true |
| 101 | 101 | run-install: true |
| 102 | 102 | |
| 103 | - name: Ensure Electron runtime is installed | |
| 104 | run: vp run --filter @t3tools/desktop ensure:electron | |
| 105 | ||
| 106 | 103 | - id: release_meta |
| 107 | 104 | name: Resolve release version |
| 108 | 105 | shell: bash |
| ... | ... | @@ -157,6 +154,40 @@ jobs: |
| 157 | 154 | fi |
| 158 | 155 | fi |
| 159 | 156 | |
| 157 | - id: previous_tag | |
| 158 | name: Resolve previous release tag | |
| 159 | run: | | |
| 160 | node scripts/resolve-previous-release-tag.ts \ | |
| 161 | --channel "${{ steps.release_meta.outputs.release_channel }}" \ | |
| 162 | --current-tag "${{ steps.release_meta.outputs.tag }}" \ | |
| 163 | --github-output | |
| 164 | ||
| 165 | quality: | |
| 166 | name: Release quality checks | |
| 167 | needs: [preflight] | |
| 168 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' }} | |
| 169 | runs-on: blacksmith-8vcpu-ubuntu-2404 | |
| 170 | timeout-minutes: 10 | |
| 171 | steps: | |
| 172 | - name: Checkout | |
| 173 | uses: actions/checkout@v6 | |
| 174 | with: | |
| 175 | ref: ${{ needs.preflight.outputs.ref }} | |
| 176 | sparse-checkout: | | |
| 177 | /* | |
| 178 | !/.repos/ | |
| 179 | sparse-checkout-cone-mode: false | |
| 180 | ||
| 181 | - name: Setup Vite+ | |
| 182 | uses: voidzero-dev/setup-vp@v1 | |
| 183 | with: | |
| 184 | node-version-file: package.json | |
| 185 | cache: true | |
| 186 | run-install: true | |
| 187 | ||
| 188 | - name: Ensure Electron runtime is installed | |
| 189 | run: vp run --filter @t3tools/desktop ensure:electron | |
| 190 | ||
| 160 | 191 | - name: Check |
| 161 | 192 | run: vp check |
| 162 | 193 | |
| ... | ... | @@ -166,14 +197,6 @@ jobs: |
| 166 | 197 | - name: Test |
| 167 | 198 | run: vp run test |
| 168 | 199 | |
| 169 | - id: previous_tag | |
| 170 | name: Resolve previous release tag | |
| 171 | run: | | |
| 172 | node scripts/resolve-previous-release-tag.ts \ | |
| 173 | --channel "${{ steps.release_meta.outputs.release_channel }}" \ | |
| 174 | --current-tag "${{ steps.release_meta.outputs.tag }}" \ | |
| 175 | --github-output | |
| 176 | ||
| 177 | 200 | relay_public_config: |
| 178 | 201 | name: Resolve T3 Connect public config |
| 179 | 202 | needs: preflight |
| ... | ... | @@ -385,14 +408,34 @@ jobs: |
| 385 | 408 | uses: voidzero-dev/setup-vp@v1 |
| 386 | 409 | with: |
| 387 | 410 | node-version-file: package.json |
| 388 | cache: true | |
| 389 | run-install: | | |
| 390 | args: | |
| 391 | - --filter=@t3tools/desktop... | |
| 392 | - --filter=t3... | |
| 393 | - --filter=@t3tools/scripts... | |
| 411 | cache: ${{ matrix.platform != 'win' }} | |
| 412 | run-install: false | |
| 413 | ||
| 414 | - name: Resolve Windows package cache path | |
| 415 | if: matrix.platform == 'win' | |
| 416 | id: package_cache_path | |
| 417 | shell: pwsh | |
| 418 | run: '"path=$(vp pm cache dir)" >> $env:GITHUB_OUTPUT' | |
| 419 | ||
| 420 | - name: Cache Windows packages | |
| 421 | if: matrix.platform == 'win' | |
| 422 | uses: actions/cache@v6 | |
| 423 | with: | |
| 424 | path: ${{ steps.package_cache_path.outputs.path }} | |
| 425 | key: windows-release-packages-v1-${{ matrix.arch }}-${{ hashFiles('pnpm-lock.yaml') }} | |
| 426 | ||
| 427 | - name: Install desktop dependencies | |
| 428 | run: vp install --filter=@t3tools/desktop... --filter=t3... --filter=@t3tools/scripts... | |
| 429 | ||
| 430 | - name: Cache resource monitor | |
| 431 | id: resource_monitor_cache | |
| 432 | uses: actions/cache@v6 | |
| 433 | with: | |
| 434 | path: native/resource-monitor/target/${{ matrix.rust_target }}/release/t3-resource-monitor${{ matrix.platform == 'win' && '.exe' || '' }} | |
| 435 | key: resource-monitor-${{ matrix.rust_target }}-${{ hashFiles('native/resource-monitor/Cargo.lock', 'native/resource-monitor/Cargo.toml', 'native/resource-monitor/src/**') }} | |
| 394 | 436 | |
| 395 | 437 | - name: Setup Rust |
| 438 | if: steps.resource_monitor_cache.outputs.cache-hit != 'true' | |
| 396 | 439 | uses: dtolnay/rust-toolchain@stable |
| 397 | 440 | with: |
| 398 | 441 | targets: ${{ matrix.rust_target }} |
| ... | ... | @@ -518,6 +561,7 @@ jobs: |
| 518 | 561 | - name: Build desktop artifact |
| 519 | 562 | shell: bash |
| 520 | 563 | env: |
| 564 | T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR: ${{ steps.resource_monitor_cache.outputs.cache-hit == 'true' }} | |
| 521 | 565 | CSC_LINK: ${{ secrets.CSC_LINK }} |
| 522 | 566 | CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} |
| 523 | 567 | APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} |
| ... | ... | @@ -664,8 +708,8 @@ jobs: |
| 664 | 708 | |
| 665 | 709 | publish_cli: |
| 666 | 710 | name: Publish CLI to npm |
| 667 | needs: [preflight, relay_public_config, build] | |
| 668 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.build.result == 'success' }} | |
| 711 | needs: [preflight, relay_public_config, quality, build] | |
| 712 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' }} | |
| 669 | 713 | runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 |
| 670 | 714 | timeout-minutes: 10 |
| 671 | 715 | permissions: |
docs/operations/release.md+2-1| ... | ... | @@ -11,7 +11,7 @@ This document covers the unified release workflow for stable and nightly desktop |
| 11 | 11 | - push tag matching `v*.*.*` for stable releases |
| 12 | 12 | - scheduled nightly check every three hours |
| 13 | 13 | - manual `workflow_dispatch` for either channel |
| 14 | - Runs quality gates first: lint, typecheck, test. | |
| 14 | - Runs lint, typecheck, and tests alongside artifact builds. Publishing waits for every check. | |
| 15 | 15 | - Reads the shared production T3 Connect relay URL and Clerk client configuration before packaging clients. |
| 16 | 16 | - Builds four artifacts in parallel for both channels: |
| 17 | 17 | - macOS `arm64` DMG |
| ... | ... | @@ -365,6 +365,7 @@ Checklist: |
| 365 | 365 | 4. Push tag. |
| 366 | 366 | 5. Verify workflow steps: |
| 367 | 367 | - preflight passes |
| 368 | - release quality checks pass | |
| 368 | 369 | - all matrix builds pass |
| 369 | 370 | - `publish_cli` publishes the exact release version before the release job |
| 370 | 371 | - release job uploads expected files |
pnpm-lock.yaml+8-77| ... | ... | @@ -38,6 +38,14 @@ catalogs: |
| 38 | 38 | version: 0.2.2 |
| 39 | 39 | |
| 40 | 40 | overrides: |
| 41 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-darwin-arm64': '-' | |
| 42 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-darwin-x64': '-' | |
| 43 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-arm64': '-' | |
| 44 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-arm64-musl': '-' | |
| 45 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-x64': '-' | |
| 46 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-x64-musl': '-' | |
| 47 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-win32-arm64': '-' | |
| 48 | '@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-win32-x64': '-' | |
| 41 | 49 | '@clerk/backend': 3.14.0 |
| 42 | 50 | '@clerk/clerk-js': 6.29.2 |
| 43 | 51 | '@clerk/clerk-js>@base-org/account': '-' |
| ... | ... | @@ -960,50 +968,6 @@ packages: |
| 960 | 968 | '@alchemy.run/node-utils@0.0.5': |
| 961 | 969 | resolution: {integrity: sha512-5agdhQxWBodxa5hDRyjnpx91RTU3g+qd5fxYB7uNDCaOzB0XC47UU/KHR6zf6jhz/NXf61gJS+vhYwn8NHeRoQ==} |
| 962 | 970 | |
| 963 | '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.170': | |
| 964 | resolution: {integrity: sha512-rwfgArIa5WI0QPNqFsRBgvtSI0mrtpynUm0oK6+l6/KX4hcgnYGEzciZR1bOeD9/7sSZlTdIgt+T9alKeZmXcg==} | |
| 965 | cpu: [arm64] | |
| 966 | os: [darwin] | |
| 967 | ||
| 968 | '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.170': | |
| 969 | resolution: {integrity: sha512-0e58h8UQMtsQxLGIv9r4foxfBFWKZ7NeDtoplLhuD7EwQonehomw1sBXCch77t/IfUS+q5vQ5zv+fOGmap5nLQ==} | |
| 970 | cpu: [x64] | |
| 971 | os: [darwin] | |
| 972 | ||
| 973 | '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.170': | |
| 974 | resolution: {integrity: sha512-SRYfQcsXlOq+CD/FqkQBTSHbaD++w73GnnO+NUV9adLYrca3kfetRwWT1iguY1cNS0l34dCR3rlzCPq78vg1Jg==} | |
| 975 | cpu: [arm64] | |
| 976 | os: [linux] | |
| 977 | libc: [musl] | |
| 978 | ||
| 979 | '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.170': | |
| 980 | resolution: {integrity: sha512-gLbaFqcGppFJQd4DLNV4IXoeahejT/p2/M8bSSvRDbla9GOsBr1AxV5XLRyBn1e7xFGozZIAIQr3+1chp7NJgQ==} | |
| 981 | cpu: [arm64] | |
| 982 | os: [linux] | |
| 983 | libc: [glibc] | |
| 984 | ||
| 985 | '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.170': | |
| 986 | resolution: {integrity: sha512-m4+I0qBEk7cxRKS+pL+eoWXbXTFOAo83fQ0tQvap4z/mDMm06IWJtEPoYTaMBwsp32GJWLkHWKbZSBCHZnp2DQ==} | |
| 987 | cpu: [x64] | |
| 988 | os: [linux] | |
| 989 | libc: [musl] | |
| 990 | ||
| 991 | '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.170': | |
| 992 | resolution: {integrity: sha512-Xl/m7TaSC3T5IDBdHrZQ9fCQYyDmPELN34CL+MoyPIf7uSmuZnjE9fUOqDh2Rv26JxWssi1M6X+BBvVuKd6Cpg==} | |
| 993 | cpu: [x64] | |
| 994 | os: [linux] | |
| 995 | libc: [glibc] | |
| 996 | ||
| 997 | '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.170': | |
| 998 | resolution: {integrity: sha512-IG+8isJNNJKbnnhO7m+PGhfVCg+XoQ/MDxGde5eigFI0WsEfitjuWSWwx82bT9ghxI1aa6qNvI+UPgPcZuo5Fg==} | |
| 999 | cpu: [arm64] | |
| 1000 | os: [win32] | |
| 1001 | ||
| 1002 | '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.170': | |
| 1003 | resolution: {integrity: sha512-7cuqSKbHVItPGVwRbd3A0BEJwcNtc7Fhoh6qHN4C6yrmjSrvdYYx3MLvq/VI768/RoG7mAMDxb+j7WfEfoP9BA==} | |
| 1004 | cpu: [x64] | |
| 1005 | os: [win32] | |
| 1006 | ||
| 1007 | 971 | '@anthropic-ai/claude-agent-sdk@0.3.170': |
| 1008 | 972 | resolution: {integrity: sha512-pAvhfk+iTodXZ6RF18Kz7BEUWFjL7EcR3tKuhUNdPpE1NAYCR3mSHGbafi72JsrNwKEDIs7FU31z3fqhwy8QzA==} |
| 1009 | 973 | engines: {node: '>=18.0.0'} |
| ... | ... | @@ -10546,44 +10510,11 @@ snapshots: |
| 10546 | 10510 | |
| 10547 | 10511 | '@alchemy.run/node-utils@0.0.5': {} |
| 10548 | 10512 | |
| 10549 | '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.170': | |
| 10550 | optional: true | |
| 10551 | ||
| 10552 | '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.170': | |
| 10553 | optional: true | |
| 10554 | ||
| 10555 | '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.170': | |
| 10556 | optional: true | |
| 10557 | ||
| 10558 | '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.170': | |
| 10559 | optional: true | |
| 10560 | ||
| 10561 | '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.170': | |
| 10562 | optional: true | |
| 10563 | ||
| 10564 | '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.170': | |
| 10565 | optional: true | |
| 10566 | ||
| 10567 | '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.170': | |
| 10568 | optional: true | |
| 10569 | ||
| 10570 | '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.170': | |
| 10571 | optional: true | |
| 10572 | ||
| 10573 | 10513 | '@anthropic-ai/claude-agent-sdk@0.3.170(@anthropic-ai/sdk@0.93.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)': |
| 10574 | 10514 | dependencies: |
| 10575 | 10515 | '@anthropic-ai/sdk': 0.93.0(zod@4.4.3) |
| 10576 | 10516 | '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) |
| 10577 | 10517 | zod: 4.4.3 |
| 10578 | optionalDependencies: | |
| 10579 | '@anthropic-ai/claude-agent-sdk-darwin-arm64': 0.3.170 | |
| 10580 | '@anthropic-ai/claude-agent-sdk-darwin-x64': 0.3.170 | |
| 10581 | '@anthropic-ai/claude-agent-sdk-linux-arm64': 0.3.170 | |
| 10582 | '@anthropic-ai/claude-agent-sdk-linux-arm64-musl': 0.3.170 | |
| 10583 | '@anthropic-ai/claude-agent-sdk-linux-x64': 0.3.170 | |
| 10584 | '@anthropic-ai/claude-agent-sdk-linux-x64-musl': 0.3.170 | |
| 10585 | '@anthropic-ai/claude-agent-sdk-win32-arm64': 0.3.170 | |
| 10586 | '@anthropic-ai/claude-agent-sdk-win32-x64': 0.3.170 | |
| 10587 | 10518 | |
| 10588 | 10519 | '@anthropic-ai/sdk@0.93.0(zod@4.4.3)': |
| 10589 | 10520 | dependencies: |
pnpm-workspace.yaml+9| ... | ... | @@ -78,6 +78,15 @@ minimumReleaseAgeExclude: |
| 78 | 78 | - "@legendapp/list@3.3.5" |
| 79 | 79 | |
| 80 | 80 | overrides: |
| 81 | # The SDK always receives the user's Claude executable, so its bundled binaries are unused. | |
| 82 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-darwin-arm64": "-" | |
| 83 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-darwin-x64": "-" | |
| 84 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-arm64": "-" | |
| 85 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "-" | |
| 86 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-x64": "-" | |
| 87 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-linux-x64-musl": "-" | |
| 88 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-win32-arm64": "-" | |
| 89 | "@anthropic-ai/claude-agent-sdk>@anthropic-ai/claude-agent-sdk-win32-x64": "-" | |
| 81 | 90 | "@clerk/backend": "catalog:" |
| 82 | 91 | "@clerk/clerk-js": "catalog:" |
| 83 | 92 | "@clerk/clerk-js>@base-org/account": "-" |
scripts/build-desktop-artifact.test.ts+119-1| ... | ... | @@ -40,6 +40,7 @@ import { |
| 40 | 40 | resolveDesktopUpdateChannel, |
| 41 | 41 | resolveDesktopWebAssetBrand, |
| 42 | 42 | resolveResourceMonitorRustTargets, |
| 43 | resolveWindowsServerAsarIgnoreGlobs, | |
| 43 | 44 | resourceMonitorExecutableName, |
| 44 | 45 | resolveGitHubPublishConfig, |
| 45 | 46 | resolveMockUpdateServerPort, |
| ... | ... | @@ -47,6 +48,7 @@ import { |
| 47 | 48 | resolvePackageManagerUserAgent, |
| 48 | 49 | stageLinuxIconSize, |
| 49 | 50 | stageDesktopDmgBackground, |
| 51 | stageResourceMonitor, | |
| 50 | 52 | STAGE_INSTALL_ARGS, |
| 51 | 53 | ancestorNodeModulesPaths, |
| 52 | 54 | copyDirectoryPreservingSymlinks, |
| ... | ... | @@ -118,7 +120,7 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu |
| 118 | 120 | yield* fs.writeFileString(nativePath, "native-binary"); |
| 119 | 121 | |
| 120 | 122 | const generatedAsarPath = path.join(tempDir, WINDOWS_SERVER_ASAR_RESOURCE); |
| 121 | yield* packWindowsServerAsar({ sourceDir, asarPath: generatedAsarPath }); | |
| 123 | yield* packWindowsServerAsar({ sourceDir, asarPath: generatedAsarPath, arch: "x64" }); | |
| 122 | 124 | |
| 123 | 125 | const stageDistDir = path.join(tempDir, "dist"); |
| 124 | 126 | const packagedAppDir = path.join(stageDistDir, "win-unpacked"); |
| ... | ... | @@ -488,6 +490,121 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 488 | 490 | }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))), |
| 489 | 491 | ); |
| 490 | 492 | |
| 493 | it("excludes node-pty binaries for the other Windows architecture", () => { | |
| 494 | assert.deepStrictEqual(resolveWindowsServerAsarIgnoreGlobs("x64"), [ | |
| 495 | ...WINDOWS_SERVER_ASAR_IGNORE_GLOBS, | |
| 496 | "**/node_modules/node-pty/prebuilds/win32-arm64", | |
| 497 | "**/node_modules/node-pty/prebuilds/win32-arm64/**", | |
| 498 | "**/node_modules/node-pty/third_party/conpty/*/win10-arm64", | |
| 499 | "**/node_modules/node-pty/third_party/conpty/*/win10-arm64/**", | |
| 500 | ]); | |
| 501 | assert.deepStrictEqual(resolveWindowsServerAsarIgnoreGlobs("arm64"), [ | |
| 502 | ...WINDOWS_SERVER_ASAR_IGNORE_GLOBS, | |
| 503 | "**/node_modules/node-pty/prebuilds/win32-x64", | |
| 504 | "**/node_modules/node-pty/prebuilds/win32-x64/**", | |
| 505 | "**/node_modules/node-pty/third_party/conpty/*/win10-x64", | |
| 506 | "**/node_modules/node-pty/third_party/conpty/*/win10-x64/**", | |
| 507 | ]); | |
| 508 | }); | |
| 509 | ||
| 510 | it.effect( | |
| 511 | "keeps target and WSL native files while excluding the other Windows architecture", | |
| 512 | () => | |
| 513 | Effect.scoped( | |
| 514 | Effect.gen(function* () { | |
| 515 | const fs = yield* FileSystem.FileSystem; | |
| 516 | const path = yield* Path.Path; | |
| 517 | const tempDir = yield* fs.makeTempDirectoryScoped({ | |
| 518 | prefix: "t3-windows-architecture-test-", | |
| 519 | }); | |
| 520 | const sourceDir = path.join(tempDir, "server"); | |
| 521 | const nativeFiles = [ | |
| 522 | "node_modules/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe", | |
| 523 | "node_modules/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe", | |
| 524 | "node_modules/node-pty/prebuilds/linux-x64/pty.node", | |
| 525 | "node_modules/node-pty/third_party/conpty/1.0.0/win10-x64/OpenConsole.exe", | |
| 526 | "node_modules/node-pty/third_party/conpty/1.0.0/win10-arm64/OpenConsole.exe", | |
| 527 | ]; | |
| 528 | ||
| 529 | for (const nativeFile of nativeFiles) { | |
| 530 | const nativePath = path.join(sourceDir, nativeFile); | |
| 531 | yield* fs.makeDirectory(path.dirname(nativePath), { recursive: true }); | |
| 532 | yield* fs.writeFileString(nativePath, "native"); | |
| 533 | } | |
| 534 | ||
| 535 | const asarPath = path.join(tempDir, "server.asar"); | |
| 536 | yield* packWindowsServerAsar({ sourceDir, asarPath, arch: "x64" }); | |
| 537 | const unpackedRoot = `${asarPath}.unpacked`; | |
| 538 | ||
| 539 | assert.isTrue( | |
| 540 | yield* fs.exists( | |
| 541 | path.join( | |
| 542 | unpackedRoot, | |
| 543 | "node_modules/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe", | |
| 544 | ), | |
| 545 | ), | |
| 546 | ); | |
| 547 | assert.isTrue( | |
| 548 | yield* fs.exists( | |
| 549 | path.join(unpackedRoot, "node_modules/node-pty/prebuilds/linux-x64/pty.node"), | |
| 550 | ), | |
| 551 | ); | |
| 552 | assert.isFalse( | |
| 553 | yield* fs.exists( | |
| 554 | path.join(unpackedRoot, "node_modules/node-pty/prebuilds/win32-arm64"), | |
| 555 | ), | |
| 556 | ); | |
| 557 | assert.isFalse( | |
| 558 | yield* fs.exists( | |
| 559 | path.join(unpackedRoot, "node_modules/node-pty/third_party/conpty/1.0.0/win10-arm64"), | |
| 560 | ), | |
| 561 | ); | |
| 562 | }), | |
| 563 | ), | |
| 564 | ); | |
| 565 | ||
| 566 | it.effect("stages a cached resource monitor without invoking Cargo", () => | |
| 567 | Effect.scoped( | |
| 568 | Effect.gen(function* () { | |
| 569 | const fs = yield* FileSystem.FileSystem; | |
| 570 | const path = yield* Path.Path; | |
| 571 | const repoRoot = yield* fs.makeTempDirectoryScoped({ | |
| 572 | prefix: "t3-resource-monitor-cache-test-", | |
| 573 | }); | |
| 574 | const binaryPath = path.join( | |
| 575 | repoRoot, | |
| 576 | "native/resource-monitor/target/x86_64-unknown-linux-gnu/release/t3-resource-monitor", | |
| 577 | ); | |
| 578 | const stageResourcesDir = path.join(repoRoot, "stage"); | |
| 579 | yield* fs.makeDirectory(path.dirname(binaryPath), { recursive: true }); | |
| 580 | yield* fs.writeFileString(binaryPath, "cached monitor"); | |
| 581 | ||
| 582 | yield* stageResourceMonitor({ | |
| 583 | repoRoot, | |
| 584 | stageResourcesDir, | |
| 585 | platform: "linux", | |
| 586 | arch: "x64", | |
| 587 | verbose: false, | |
| 588 | }).pipe( | |
| 589 | Effect.provide( | |
| 590 | ConfigProvider.layer( | |
| 591 | ConfigProvider.fromEnv({ | |
| 592 | env: { T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR: "true" }, | |
| 593 | }), | |
| 594 | ), | |
| 595 | ), | |
| 596 | ); | |
| 597 | ||
| 598 | assert.equal( | |
| 599 | yield* fs.readFileString( | |
| 600 | path.join(stageResourcesDir, "resource-monitor/t3-resource-monitor"), | |
| 601 | ), | |
| 602 | "cached monitor", | |
| 603 | ); | |
| 604 | }), | |
| 605 | ), | |
| 606 | ); | |
| 607 | ||
| 491 | 608 | it.effect("validates every ASAR-unpacked native in the packaged Windows payload", () => |
| 492 | 609 | Effect.scoped( |
| 493 | 610 | Effect.gen(function* () { |
| ... | ... | @@ -504,6 +621,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 504 | 621 | yield* packWindowsServerAsar({ |
| 505 | 622 | sourceDir: fixture.sourceDir, |
| 506 | 623 | asarPath: secondAsarPath, |
| 624 | arch: "x64", | |
| 507 | 625 | }); |
| 508 | 626 | const [firstAsar, secondAsar] = yield* Effect.all([ |
| 509 | 627 | fs.readFile(fixture.generatedAsarPath), |
scripts/build-desktop-artifact.ts+50-22| ... | ... | @@ -819,6 +819,21 @@ export const WINDOWS_SERVER_ASAR_IGNORE_GLOBS = [ |
| 819 | 819 | "**/node_modules/.bin", |
| 820 | 820 | "**/node_modules/.bin/**", |
| 821 | 821 | ] as const; |
| 822 | ||
| 823 | export function resolveWindowsServerAsarIgnoreGlobs(arch: typeof BuildArch.Type) { | |
| 824 | const unusedArch = arch === "arm64" ? "x64" : "arm64"; | |
| 825 | const unusedPrebuild = `**/node_modules/node-pty/prebuilds/win32-${unusedArch}`; | |
| 826 | const unusedConpty = `**/node_modules/node-pty/third_party/conpty/*/win10-${unusedArch}`; | |
| 827 | ||
| 828 | return [ | |
| 829 | ...WINDOWS_SERVER_ASAR_IGNORE_GLOBS, | |
| 830 | unusedPrebuild, | |
| 831 | `${unusedPrebuild}/**`, | |
| 832 | unusedConpty, | |
| 833 | `${unusedConpty}/**`, | |
| 834 | ]; | |
| 835 | } | |
| 836 | ||
| 822 | 837 | export const WINDOWS_PACKAGED_PAYLOAD_FILE_LIMIT = 80; |
| 823 | 838 | export const WINDOWS_SERVER_RESOURCE_SOURCE_DIR = "apps/desktop/prod-resources/windows-server"; |
| 824 | 839 | export const WINDOWS_SERVER_EXTRA_RESOURCES = [ |
| ... | ... | @@ -1643,7 +1658,7 @@ const verifyPackagedBundleIsSelfContained = Effect.fn("verifyPackagedBundleIsSel |
| 1643 | 1658 | }, |
| 1644 | 1659 | ); |
| 1645 | 1660 | |
| 1646 | const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: { | |
| 1661 | export const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: { | |
| 1647 | 1662 | readonly repoRoot: string; |
| 1648 | 1663 | readonly stageResourcesDir: string; |
| 1649 | 1664 | readonly platform: typeof BuildPlatform.Type; |
| ... | ... | @@ -1655,28 +1670,33 @@ const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: |
| 1655 | 1670 | const manifestPath = path.join(input.repoRoot, "native/resource-monitor/Cargo.toml"); |
| 1656 | 1671 | const executableName = resourceMonitorExecutableName(input.platform); |
| 1657 | 1672 | const rustTargets = resolveResourceMonitorRustTargets(input.platform, input.arch); |
| 1673 | const reuseResourceMonitor = yield* Config.boolean("T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR").pipe( | |
| 1674 | Config.withDefault(false), | |
| 1675 | ); | |
| 1658 | 1676 | const builtBinaries: string[] = []; |
| 1659 | 1677 | |
| 1660 | 1678 | for (const rustTarget of rustTargets) { |
| 1661 | const spawnCommand = yield* resolveSpawnCommand("cargo", [ | |
| 1662 | "build", | |
| 1663 | "--locked", | |
| 1664 | "--release", | |
| 1665 | "--manifest-path", | |
| 1666 | manifestPath, | |
| 1667 | "--target", | |
| 1668 | rustTarget, | |
| 1669 | ]); | |
| 1670 | yield* runCommand( | |
| 1671 | ChildProcess.make(spawnCommand.command, spawnCommand.args, { | |
| 1672 | cwd: input.repoRoot, | |
| 1673 | shell: spawnCommand.shell, | |
| 1674 | }), | |
| 1675 | { | |
| 1676 | label: `cargo build resource monitor (${rustTarget})`, | |
| 1677 | verbose: input.verbose, | |
| 1678 | }, | |
| 1679 | ); | |
| 1679 | if (!reuseResourceMonitor) { | |
| 1680 | const spawnCommand = yield* resolveSpawnCommand("cargo", [ | |
| 1681 | "build", | |
| 1682 | "--locked", | |
| 1683 | "--release", | |
| 1684 | "--manifest-path", | |
| 1685 | manifestPath, | |
| 1686 | "--target", | |
| 1687 | rustTarget, | |
| 1688 | ]); | |
| 1689 | yield* runCommand( | |
| 1690 | ChildProcess.make(spawnCommand.command, spawnCommand.args, { | |
| 1691 | cwd: input.repoRoot, | |
| 1692 | shell: spawnCommand.shell, | |
| 1693 | }), | |
| 1694 | { | |
| 1695 | label: `cargo build resource monitor (${rustTarget})`, | |
| 1696 | verbose: input.verbose, | |
| 1697 | }, | |
| 1698 | ); | |
| 1699 | } | |
| 1680 | 1700 | |
| 1681 | 1701 | const binaryPath = path.join( |
| 1682 | 1702 | input.repoRoot, |
| ... | ... | @@ -1693,6 +1713,9 @@ const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: |
| 1693 | 1713 | arch: input.arch, |
| 1694 | 1714 | }); |
| 1695 | 1715 | } |
| 1716 | if (reuseResourceMonitor) { | |
| 1717 | yield* Effect.log(`[desktop-artifact] Reusing cached resource monitor (${rustTarget}).`); | |
| 1718 | } | |
| 1696 | 1719 | builtBinaries.push(binaryPath); |
| 1697 | 1720 | } |
| 1698 | 1721 | |
| ... | ... | @@ -2253,6 +2276,7 @@ const stageWslNodePtyPrebuild = Effect.fn("stageWslNodePtyPrebuild")(function* ( |
| 2253 | 2276 | export const packWindowsServerAsar = Effect.fn("packWindowsServerAsar")(function* (input: { |
| 2254 | 2277 | readonly sourceDir: string; |
| 2255 | 2278 | readonly asarPath: string; |
| 2279 | readonly arch: typeof BuildArch.Type; | |
| 2256 | 2280 | }) { |
| 2257 | 2281 | const fs = yield* FileSystem.FileSystem; |
| 2258 | 2282 | yield* Effect.tryPromise({ |
| ... | ... | @@ -2260,7 +2284,7 @@ export const packWindowsServerAsar = Effect.fn("packWindowsServerAsar")(function |
| 2260 | 2284 | createPackageWithOptions(input.sourceDir, input.asarPath, { |
| 2261 | 2285 | dot: true, |
| 2262 | 2286 | unpack: WINDOWS_SERVER_ASAR_UNPACK_GLOB, |
| 2263 | globOptions: { ignore: [...WINDOWS_SERVER_ASAR_IGNORE_GLOBS] }, | |
| 2287 | globOptions: { ignore: resolveWindowsServerAsarIgnoreGlobs(input.arch) }, | |
| 2264 | 2288 | }), |
| 2265 | 2289 | catch: (cause) => new WindowsServerSidecarPackError({ asarPath: input.asarPath, cause }), |
| 2266 | 2290 | }); |
| ... | ... | @@ -2354,7 +2378,11 @@ export const stageWindowsServerSidecar = Effect.fn("stageWindowsServerSidecar")( |
| 2354 | 2378 | |
| 2355 | 2379 | yield* Effect.log("[desktop-artifact] Packing server.asar..."); |
| 2356 | 2380 | yield* fs.makeDirectory(path.dirname(input.asarPath), { recursive: true }); |
| 2357 | yield* packWindowsServerAsar({ sourceDir: serverStageDir, asarPath: input.asarPath }); | |
| 2381 | yield* packWindowsServerAsar({ | |
| 2382 | sourceDir: serverStageDir, | |
| 2383 | asarPath: input.asarPath, | |
| 2384 | arch: input.arch, | |
| 2385 | }); | |
| 2358 | 2386 | const packedStat = yield* fs.stat(input.asarPath); |
| 2359 | 2387 | yield* Effect.log( |
| 2360 | 2388 | `[desktop-artifact] Packed server.asar (${String(packedStat.size)} bytes) + unpacked natives.`, |