authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-08-24 21:19:22-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-24 21:19:22-07:00
log63eb0429f49fad5f8722407ea971e3199e6c4514
tree8019c4f30e059db59a1551c06148e3877ab17a94
parent143341b0bc4e3e81066ab2cd86ddd199574bfd83
signature Signed by PGP key B5690EEEBB952194

perf(desktop): cut macOS signing calls by 81% (#8093)


6 files changed, 140 insertions(+), 18 deletions(-)

pnpm-lock.yaml+17
......@@ -927,6 +927,9 @@ importers:
927927 '@electron/asar':
928928 specifier: ^3.4.1
929929 version: 3.4.1
930 '@electron/osx-sign':
931 specifier: 2.7.0
932 version: 2.7.0
930933 '@t3tools/contracts':
931934 specifier: workspace:*
932935 version: link:../packages/contracts
......@@ -2050,6 +2053,11 @@ packages:
20502053 engines: {node: '>=12.0.0'}
20512054 hasBin: true
20522055
2056 '@electron/osx-sign@2.7.0':
2057 resolution: {integrity: sha512-9DGhNqKMl6ibkhUoXbN7OHX2gZznfY10L3ZwG0u6r667Kfb6kec4JEfFTXftoqzmOfZ+OzwDbr4p/nKBMHnz0g==}
2058 engines: {node: '>=22.12.0'}
2059 hasBin: true
2060
20532061 '@electron/rebuild@4.0.4':
20542062 resolution: {integrity: sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==}
20552063 engines: {node: '>=22.12.0'}
......@@ -11900,6 +11908,15 @@ snapshots:
1190011908 transitivePeerDependencies:
1190111909 - supports-color
1190211910
11911 '@electron/osx-sign@2.7.0':
11912 dependencies:
11913 debug: 4.4.3
11914 isbinaryfile: 4.0.10
11915 plist: 3.1.1
11916 semver: 7.8.5
11917 transitivePeerDependencies:
11918 - supports-color
11919
1190311920 '@electron/rebuild@4.0.4':
1190411921 dependencies:
1190511922 '@malept/cross-spawn-promise': 2.0.0
scripts/build-desktop-artifact.test.ts+43-1
......@@ -20,6 +20,7 @@ import {
2020 DESKTOP_ELECTRON_LANGUAGES,
2121 DESKTOP_FILE_EXCLUSIONS,
2222 DESKTOP_EXTRA_RESOURCES,
23 MAC_FILE_EXCLUSIONS,
2324 InvalidMacPasskeyRpDomainError,
2425 InvalidMacPasskeyPublishableKeyError,
2526 InvalidMockUpdateServerPortError,
......@@ -33,6 +34,7 @@ import {
3334 resolveClerkPasskeyNativeArtifacts,
3435 resolveMacPasskeySigningConfiguration,
3536 resolveDesktopRuntimeDependencies,
37 resolveMacStageDependencies,
3638 resolveFffNativeDependencies,
3739 resolveBuildOptions,
3840 resolveDesktopBuildIconAssets,
......@@ -483,13 +485,52 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
483485 assert.deepStrictEqual((linux.linux as Record<string, unknown>).protocols, [
484486 { name: "T3 Code", schemes: ["t3code", "t3code-dev"] },
485487 ]);
486 for (const config of [mac, linux, win]) {
488 assert.deepStrictEqual(mac.files, [...DESKTOP_FILE_EXCLUSIONS, ...MAC_FILE_EXCLUSIONS]);
489 assert.notProperty(mac.mac as Record<string, unknown>, "sign");
490 for (const config of [linux, win]) {
487491 assert.deepStrictEqual(config.electronLanguages, DESKTOP_ELECTRON_LANGUAGES);
488492 assert.deepStrictEqual(config.files, DESKTOP_FILE_EXCLUSIONS);
489493 }
494 assert.deepStrictEqual(mac.electronLanguages, DESKTOP_ELECTRON_LANGUAGES);
490495 }).pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })))),
491496 );
492497
498 it("excludes Windows terminal binaries only from macOS packages", () => {
499 assert.deepStrictEqual(MAC_FILE_EXCLUSIONS, [
500 "!**/node_modules/node-pty/prebuilds/win32-*/**/*",
501 "!**/node_modules/node-pty/third_party/conpty/**/*",
502 ]);
503 });
504
505 it("stages only server runtime externals in macOS packages", () => {
506 assert.deepStrictEqual(
507 resolveMacStageDependencies({
508 serverDependencies: {
509 "@anthropic-ai/claude-agent-sdk": "^0.3.170",
510 "@ff-labs/fff-node": "0.9.4",
511 "@opencode-ai/sdk": "^1.3.15",
512 "@pierre/diffs": "1.3.0",
513 "msgpackr-extract": "3.0.4",
514 "node-pty": "1.1.0",
515 },
516 desktopDependencies: {
517 "@clerk/electron": "0.0.34",
518 effect: "4.0.0-beta.103",
519 },
520 arch: "arm64",
521 fffNodeVersion: "0.9.4",
522 }),
523 {
524 "@ff-labs/fff-node": "0.9.4",
525 "msgpackr-extract": "3.0.4",
526 "node-pty": "1.1.0",
527 "@clerk/electron": "0.0.34",
528 effect: "4.0.0-beta.103",
529 "@ff-labs/fff-bin-darwin-arm64": "0.9.4",
530 },
531 );
532 });
533
493534 it("excludes node-pty binaries for the other Windows architecture", () => {
494535 assert.deepStrictEqual(resolveWindowsServerAsarIgnoreGlobs("x64"), [
495536 ...WINDOWS_SERVER_ASAR_IGNORE_GLOBS,
......@@ -1112,6 +1153,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
11121153 assert.equal(config.appId, "com.t3tools.t3code");
11131154 assert.equal(mac.entitlements, "/tmp/entitlements.mac.plist");
11141155 assert.equal(mac.provisioningProfile, "/tmp/t3code.provisionprofile");
1156 assert.match(String(mac.sign), /\/scripts\/sign-macos\.ts$/);
11151157 assert.deepStrictEqual(mac.protocols, [
11161158 { name: "T3 Code", schemes: ["t3code", "t3code-dev"] },
11171159 ]);
scripts/build-desktop-artifact.ts+47-17
......@@ -796,6 +796,11 @@ export const DESKTOP_FILE_EXCLUSIONS = [
796796 "!apps/desktop/prod-resources/windows-server",
797797 "!apps/desktop/prod-resources/windows-server/**/*",
798798] as const;
799// Windows terminal helpers cannot run on macOS and slow signing and notarization.
800export const MAC_FILE_EXCLUSIONS = [
801 "!**/node_modules/node-pty/prebuilds/win32-*/**/*",
802 "!**/node_modules/node-pty/third_party/conpty/**/*",
803] as const;
799804// Windows ships the server tree (bundle + node_modules) as a separate
800805// resources/server.asar sidecar instead of loose files: the NSIS installer
801806// then extracts a handful of large archives instead of thousands of small
......@@ -1097,6 +1102,19 @@ export function resolveFffNativeDependencies(
10971102 );
10981103}
10991104
1105export function resolveMacStageDependencies(input: {
1106 readonly serverDependencies: Record<string, string>;
1107 readonly desktopDependencies: Record<string, string>;
1108 readonly arch: typeof BuildArch.Type;
1109 readonly fffNodeVersion: string;
1110}) {
1111 return {
1112 ...selectCliRuntimeExternalDependencies(input.serverDependencies),
1113 ...input.desktopDependencies,
1114 ...resolveFffNativeDependencies("mac", input.arch, input.fffNodeVersion),
1115 };
1116}
1117
11001118export interface ClerkPasskeyNativeArtifact {
11011119 readonly packageName: string;
11021120 readonly binaryFileName: string;
......@@ -2061,7 +2079,7 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* (
20612079 productName: resolveDesktopProductName(version),
20622080 artifactName: "T3-Code-${version}-${arch}.${ext}",
20632081 electronLanguages: [...DESKTOP_ELECTRON_LANGUAGES],
2064 files: [...DESKTOP_FILE_EXCLUSIONS],
2082 files: [...DESKTOP_FILE_EXCLUSIONS, ...(platform === "mac" ? MAC_FILE_EXCLUSIONS : [])],
20652083 directories: {
20662084 buildResources: "apps/desktop/resources",
20672085 },
......@@ -2088,6 +2106,8 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* (
20882106 }
20892107
20902108 if (platform === "mac") {
2109 const path = yield* Path.Path;
2110 const repoRoot = yield* RepoRoot;
20912111 buildConfig.mac = {
20922112 target: target === "dmg" ? [target, "zip"] : [target],
20932113 icon: "icon.icns",
......@@ -2098,6 +2118,7 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* (
20982118 schemes: ["t3code", "t3code-dev"],
20992119 },
21002120 ],
2121 ...(signed ? { sign: path.join(repoRoot, "scripts/sign-macos.ts") } : {}),
21012122 ...(macPasskeySigning
21022123 ? {
21032124 entitlements: macPasskeySigning.entitlementsPath,
......@@ -2899,21 +2920,28 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
28992920
29002921 // Windows splits dependencies per process: app.asar carries only the
29012922 // desktop main-process runtime deps, while the server bundle's deps live in
2902 // the server.asar sidecar (see stageWindowsServerSidecar). macOS and Linux
2903 // keep the single merged tree — their primary resolves everything from
2904 // app.asar and there is no second consumer.
2923 // the server.asar sidecar (see stageWindowsServerSidecar). macOS adds only
2924 // server packages that remain external to its merged app.asar. Linux retains
2925 // its existing full dependency tree.
29052926 const stageDependencies =
29062927 options.platform === "win"
29072928 ? { ...resolvedDesktopRuntimeDependencies }
2908 : {
2909 ...resolvedServerDependencies,
2910 ...resolvedDesktopRuntimeDependencies,
2911 ...resolveFffNativeDependencies(
2912 options.platform,
2913 options.arch,
2914 serverPackageJson.dependencies["@ff-labs/fff-node"],
2915 ),
2916 };
2929 : options.platform === "mac"
2930 ? resolveMacStageDependencies({
2931 serverDependencies: resolvedServerDependencies,
2932 desktopDependencies: resolvedDesktopRuntimeDependencies,
2933 arch: options.arch,
2934 fffNodeVersion: serverPackageJson.dependencies["@ff-labs/fff-node"],
2935 })
2936 : {
2937 ...resolvedServerDependencies,
2938 ...resolvedDesktopRuntimeDependencies,
2939 ...resolveFffNativeDependencies(
2940 options.platform,
2941 options.arch,
2942 serverPackageJson.dependencies["@ff-labs/fff-node"],
2943 ),
2944 };
29172945 const stagePatchedDependencies = createStagePatchedDependencies(
29182946 workspacePatchedDependencies,
29192947 stageDependencies,
......@@ -3034,10 +3062,12 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
30343062 buildEnv.GYP_MSVS_VERSION = buildEnv.GYP_MSVS_VERSION ?? "2022";
30353063 }
30363064 if (options.verbose) {
3037 buildEnv.DEBUG =
3038 buildEnv.DEBUG === undefined
3039 ? "electron-builder,electron-builder:*"
3040 : `${buildEnv.DEBUG},electron-builder,electron-builder:*`;
3065 const debugNamespaces = [
3066 "electron-builder",
3067 "electron-builder:*",
3068 ...(options.platform === "mac" ? ["electron-osx-sign*", "electron-notarize*"] : []),
3069 ];
3070 buildEnv.DEBUG = [buildEnv.DEBUG, ...debugNamespaces].filter(Boolean).join(",");
30413071 }
30423072
30433073 yield* Effect.log(
scripts/package.json+1
......@@ -9,6 +9,7 @@
99 "dependencies": {
1010 "@effect/platform-node": "catalog:",
1111 "@electron/asar": "^3.4.1",
12 "@electron/osx-sign": "2.7.0",
1213 "@t3tools/contracts": "workspace:*",
1314 "@t3tools/shared": "workspace:*",
1415 "@t3tools/tailscale": "workspace:*",
scripts/sign-macos.test.ts created+26
......@@ -0,0 +1,26 @@
1import { sign as signApplication, type SignOptions } from "@electron/osx-sign";
2import { expect, it, vi } from "vite-plus/test";
3
4import sign from "./sign-macos.ts";
5
6vi.mock("@electron/osx-sign", () => ({ sign: vi.fn() }));
7
8it("batches codesign calls without changing existing signing options", async () => {
9 const options = {
10 app: "/tmp/T3 Code.app",
11 identity: "Developer ID Application: T3 Tools, Inc.",
12 keychain: "/tmp/t3code.keychain",
13 provisioningProfile: "/tmp/t3code.provisionprofile",
14 optionsForFile: () => ({
15 entitlements: "/tmp/t3code.entitlements.plist",
16 hardenedRuntime: true,
17 }),
18 } satisfies SignOptions;
19
20 await sign(options);
21
22 expect(signApplication).toHaveBeenCalledExactlyOnceWith({
23 ...options,
24 batchCodesignCalls: true,
25 });
26});
scripts/sign-macos.ts created+6
......@@ -0,0 +1,6 @@
1import { sign as signApplication, type SignOptions } from "@electron/osx-sign";
2
3/** Sign files with matching options together instead of spawning codesign for each file. */
4export default async function sign(options: SignOptions): Promise<void> {
5 await signApplication({ ...options, batchCodesignCalls: true });
6}