History

perf(ci): speed up release builds and Windows packaging (#7975)

Theo Browne committed 2 months ago

Commit 25dcee01 parent f70eeee6 files · +251 −120Browse files

Unified · Split

6 files
  1. .github/workflows/release.yml +63 −19
  2. docs/operations/release.md +2 −1
  3. pnpm-lock.yaml +8 −77
  4. pnpm-workspace.yaml +9 −0
  5. scripts/build-desktop-artifact.test.ts +119 −1
  6. scripts/build-desktop-artifact.ts +50 −22
.github/workflows/release.yml+63−19
@@ -100,9 +100,6 @@ jobs: · 99 unmodified lines
100100 cache: true
101101 run-install: true
102102
103− - name: Ensure Electron runtime is installed
104− run: vp run --filter @t3tools/desktop ensure:electron
105−
106103 - id: release_meta
107104 name: Resolve release version
108105 shell: bash
@@ -157,6 +154,40 @@ jobs: · 48 unmodified lines
157154 fi
158155 fi
159156
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+
160191 - name: Check
161192 run: vp check
162193
@@ -166,14 +197,6 @@ jobs: · 3 unmodified lines
166197 - name: Test
167198 run: vp run test
168199
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−
177200 relay_public_config:
178201 name: Resolve T3 Connect public config
179202 needs: preflight
@@ -385,14 +408,34 @@ jobs: · 205 unmodified lines
385408 uses: voidzero-dev/setup-vp@v1
386409 with:
387410 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/**') }}
394436
395437 - name: Setup Rust
438+ if: steps.resource_monitor_cache.outputs.cache-hit != 'true'
396439 uses: dtolnay/rust-toolchain@stable
397440 with:
398441 targets: ${{ matrix.rust_target }}
@@ -518,6 +561,7 @@ jobs: · 119 unmodified lines
518561 - name: Build desktop artifact
519562 shell: bash
520563 env:
564+ T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR: ${{ steps.resource_monitor_cache.outputs.cache-hit == 'true' }}
521565 CSC_LINK: ${{ secrets.CSC_LINK }}
522566 CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
523567 APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
@@ -664,8 +708,8 @@ jobs: · 140 unmodified lines
664708
665709 publish_cli:
666710 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' }}
669713 runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404
670714 timeout-minutes: 10
671715 permissions:
docs/operations/release.md+2−1
@@ -11,7 +11,7 @@ This document covers the unified release workflow for stable and nightly desktop · 10 unmodified lines
1111 - push tag matching `v*.*.*` for stable releases
1212 - scheduled nightly check every three hours
1313 - 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.
1515 - Reads the shared production T3 Connect relay URL and Clerk client configuration before packaging clients.
1616 - Builds four artifacts in parallel for both channels:
1717 - macOS `arm64` DMG
@@ -365,6 +365,7 @@ Checklist: · 347 unmodified lines
365365 4. Push tag.
366366 5. Verify workflow steps:
367367 - preflight passes
368+ - release quality checks pass
368369 - all matrix builds pass
369370 - `publish_cli` publishes the exact release version before the release job
370371 - release job uploads expected files
pnpm-lock.yaml+8−77
@@ -38,6 +38,14 @@ catalogs: · 37 unmodified lines
3838 version: 0.2.2
3939
4040 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': '-'
4149 '@clerk/backend': 3.14.0
4250 '@clerk/clerk-js': 6.29.2
4351 '@clerk/clerk-js>@base-org/account': '-'
@@ -960,50 +968,6 @@ packages: · 916 unmodified lines
960968 '@alchemy.run/node-utils@0.0.5':
961969 resolution: {integrity: sha512-5agdhQxWBodxa5hDRyjnpx91RTU3g+qd5fxYB7uNDCaOzB0XC47UU/KHR6zf6jhz/NXf61gJS+vhYwn8NHeRoQ==}
962970
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−
1007971 '@anthropic-ai/claude-agent-sdk@0.3.170':
1008972 resolution: {integrity: sha512-pAvhfk+iTodXZ6RF18Kz7BEUWFjL7EcR3tKuhUNdPpE1NAYCR3mSHGbafi72JsrNwKEDIs7FU31z3fqhwy8QzA==}
1009973 engines: {node: '>=18.0.0'}
@@ -10546,44 +10510,11 @@ snapshots: · 9536 unmodified lines
1054610510
1054710511 '@alchemy.run/node-utils@0.0.5': {}
1054810512
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−
1057310513 '@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)':
1057410514 dependencies:
1057510515 '@anthropic-ai/sdk': 0.93.0(zod@4.4.3)
1057610516 '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3)
1057710517 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
1058710518
1058810519 '@anthropic-ai/sdk@0.93.0(zod@4.4.3)':
1058910520 dependencies:
pnpm-workspace.yaml+9−0
@@ -78,6 +78,15 @@ minimumReleaseAgeExclude: · 77 unmodified lines
7878 - "@legendapp/list@3.3.5"
7979
8080 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": "-"
8190 "@clerk/backend": "catalog:"
8291 "@clerk/clerk-js": "catalog:"
8392 "@clerk/clerk-js>@base-org/account": "-"
scripts/build-desktop-artifact.test.ts+119−1
@@ -40,6 +40,7 @@ import { · 39 unmodified lines
4040 resolveDesktopUpdateChannel,
4141 resolveDesktopWebAssetBrand,
4242 resolveResourceMonitorRustTargets,
43+ resolveWindowsServerAsarIgnoreGlobs,
4344 resourceMonitorExecutableName,
4445 resolveGitHubPublishConfig,
4546 resolveMockUpdateServerPort,
@@ -47,6 +48,7 @@ import { · 1 unmodified line
4748 resolvePackageManagerUserAgent,
4849 stageLinuxIconSize,
4950 stageDesktopDmgBackground,
51+ stageResourceMonitor,
5052 STAGE_INSTALL_ARGS,
5153 ancestorNodeModulesPaths,
5254 copyDirectoryPreservingSymlinks,
@@ -118,7 +120,7 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu · 65 unmodified lines
118120 yield* fs.writeFileString(nativePath, "native-binary");
119121
120122 const generatedAsarPath = path.join(tempDir, WINDOWS_SERVER_ASAR_RESOURCE);
121− yield* packWindowsServerAsar({ sourceDir, asarPath: generatedAsarPath });
123+ yield* packWindowsServerAsar({ sourceDir, asarPath: generatedAsarPath, arch: "x64" });
122124
123125 const stageDistDir = path.join(tempDir, "dist");
124126 const packagedAppDir = path.join(stageDistDir, "win-unpacked");
@@ -488,6 +490,121 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { · 363 unmodified lines
488490 }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))),
489491 );
490492
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+
491608 it.effect("validates every ASAR-unpacked native in the packaged Windows payload", () =>
492609 Effect.scoped(
493610 Effect.gen(function* () {
@@ -504,6 +621,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { · 10 unmodified lines
504621 yield* packWindowsServerAsar({
505622 sourceDir: fixture.sourceDir,
506623 asarPath: secondAsarPath,
624+ arch: "x64",
507625 });
508626 const [firstAsar, secondAsar] = yield* Effect.all([
509627 fs.readFile(fixture.generatedAsarPath),
scripts/build-desktop-artifact.ts+50−22
@@ -819,6 +819,21 @@ export const WINDOWS_SERVER_ASAR_IGNORE_GLOBS = [ · 818 unmodified lines
819819 "**/node_modules/.bin",
820820 "**/node_modules/.bin/**",
821821 ] 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+
822837 export const WINDOWS_PACKAGED_PAYLOAD_FILE_LIMIT = 80;
823838 export const WINDOWS_SERVER_RESOURCE_SOURCE_DIR = "apps/desktop/prod-resources/windows-server";
824839 export const WINDOWS_SERVER_EXTRA_RESOURCES = [
@@ -1643,7 +1658,7 @@ const verifyPackagedBundleIsSelfContained = Effect.fn("verifyPackagedBundleIsSel · 818 unmodified lines
16431658 },
16441659 );
16451660
1646−const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: {
1661+export const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: {
16471662 readonly repoRoot: string;
16481663 readonly stageResourcesDir: string;
16491664 readonly platform: typeof BuildPlatform.Type;
@@ -1655,28 +1670,33 @@ const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: · 5 unmodified lines
16551670 const manifestPath = path.join(input.repoRoot, "native/resource-monitor/Cargo.toml");
16561671 const executableName = resourceMonitorExecutableName(input.platform);
16571672 const rustTargets = resolveResourceMonitorRustTargets(input.platform, input.arch);
1673+ const reuseResourceMonitor = yield* Config.boolean("T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR").pipe(
1674+ Config.withDefault(false),
1675+ );
16581676 const builtBinaries: string[] = [];
16591677
16601678 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+ }
16801700
16811701 const binaryPath = path.join(
16821702 input.repoRoot,
@@ -1693,6 +1713,9 @@ const stageResourceMonitor = Effect.fn("stageResourceMonitor")(function* (input: · 10 unmodified lines
16931713 arch: input.arch,
16941714 });
16951715 }
1716+ if (reuseResourceMonitor) {
1717+ yield* Effect.log(`[desktop-artifact] Reusing cached resource monitor (${rustTarget}).`);
1718+ }
16961719 builtBinaries.push(binaryPath);
16971720 }
16981721
@@ -2253,6 +2276,7 @@ const stageWslNodePtyPrebuild = Effect.fn("stageWslNodePtyPrebuild")(function* ( · 554 unmodified lines
22532276 export const packWindowsServerAsar = Effect.fn("packWindowsServerAsar")(function* (input: {
22542277 readonly sourceDir: string;
22552278 readonly asarPath: string;
2279+ readonly arch: typeof BuildArch.Type;
22562280 }) {
22572281 const fs = yield* FileSystem.FileSystem;
22582282 yield* Effect.tryPromise({
@@ -2260,7 +2284,7 @@ export const packWindowsServerAsar = Effect.fn("packWindowsServerAsar")(function · 1 unmodified line
22602284 createPackageWithOptions(input.sourceDir, input.asarPath, {
22612285 dot: true,
22622286 unpack: WINDOWS_SERVER_ASAR_UNPACK_GLOB,
2263− globOptions: { ignore: [...WINDOWS_SERVER_ASAR_IGNORE_GLOBS] },
2287+ globOptions: { ignore: resolveWindowsServerAsarIgnoreGlobs(input.arch) },
22642288 }),
22652289 catch: (cause) => new WindowsServerSidecarPackError({ asarPath: input.asarPath, cause }),
22662290 });
@@ -2354,7 +2378,11 @@ export const stageWindowsServerSidecar = Effect.fn("stageWindowsServerSidecar")( · 87 unmodified lines
23542378
23552379 yield* Effect.log("[desktop-artifact] Packing server.asar...");
23562380 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+ });
23582386 const packedStat = yield* fs.stat(input.asarPath);
23592387 yield* Effect.log(
23602388 `[desktop-artifact] Packed server.asar (${String(packedStat.size)} bytes) + unpacked natives.`,