Skip to content

Commit 03ac1f0

Browse files
fix(release): stage pnpm 11 allowBuilds for desktop installs (#3781)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 7778a1c commit 03ac1f0

2 files changed

Lines changed: 118 additions & 47 deletions

File tree

scripts/build-desktop-artifact.test.ts

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { ChildProcessSpawner } from "effect/unstable/process";
1111
import {
1212
BuildCommandFailedError,
1313
createStageWorkspaceConfig,
14-
createStagePnpmConfig,
14+
createStagePatchedDependencies,
1515
createBuildConfig,
1616
DESKTOP_ASAR_UNPACK,
1717
InvalidMacPasskeyRpDomainError,
@@ -168,7 +168,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
168168

169169
it("carries only staged dependency patch metadata into staged desktop installs", () => {
170170
assert.deepStrictEqual(
171-
createStagePnpmConfig(
171+
createStagePatchedDependencies(
172172
{
173173
"@expo/metro-config@56.0.13": "patches/@expo%2Fmetro-config@56.0.13.patch",
174174
"@ff-labs/fff-node@0.9.4": "patches/@ff-labs__fff-node@0.9.4.patch",
@@ -183,57 +183,111 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
183183
},
184184
),
185185
{
186-
patchedDependencies: {
187-
"@ff-labs/fff-node@0.9.4": "patches/@ff-labs__fff-node@0.9.4.patch",
188-
"@pierre/diffs@1.1.20": "patches/@pierre%2Fdiffs@1.1.20.patch",
189-
"effect@4.0.0-beta.73": "patches/effect@4.0.0-beta.73.patch",
190-
},
186+
"@ff-labs/fff-node@0.9.4": "patches/@ff-labs__fff-node@0.9.4.patch",
187+
"@pierre/diffs@1.1.20": "patches/@pierre%2Fdiffs@1.1.20.patch",
188+
"effect@4.0.0-beta.73": "patches/effect@4.0.0-beta.73.patch",
191189
},
192190
);
193191

194-
assert.equal(
195-
createStagePnpmConfig(
192+
assert.deepStrictEqual(
193+
createStagePatchedDependencies(
196194
{
197195
"@expo/metro-config@56.0.13": "patches/@expo%2Fmetro-config@56.0.13.patch",
198196
},
199197
{ effect: "4.0.0-beta.73" },
200198
),
201-
undefined,
199+
{},
202200
);
203201
});
204202

205203
it("installs optional native dependencies for the target desktop architecture", () => {
206204
assert.deepStrictEqual(STAGE_INSTALL_ARGS, ["install", "--prod"]);
207-
assert.deepStrictEqual(createStageWorkspaceConfig("mac", "x64"), {
205+
assert.deepStrictEqual(createStageWorkspaceConfig({ platform: "mac", arch: "x64" }), {
208206
supportedArchitectures: {
209207
os: ["darwin"],
210208
cpu: ["x64"],
211209
},
212210
});
213211
// Windows artifacts also bundle the same-architecture WSL (Linux, glibc) backend, so the
214212
// staged install must fetch its native optional deps (e.g. ffi-rs) too.
215-
assert.deepStrictEqual(createStageWorkspaceConfig("win", "x64"), {
213+
assert.deepStrictEqual(createStageWorkspaceConfig({ platform: "win", arch: "x64" }), {
216214
supportedArchitectures: {
217215
os: ["win32", "linux"],
218216
cpu: ["x64"],
219217
libc: ["glibc"],
220218
},
221219
});
222-
assert.deepStrictEqual(createStageWorkspaceConfig("win", "arm64"), {
220+
assert.deepStrictEqual(createStageWorkspaceConfig({ platform: "win", arch: "arm64" }), {
223221
supportedArchitectures: {
224222
os: ["win32", "linux"],
225223
cpu: ["arm64"],
226224
libc: ["glibc"],
227225
},
228226
});
229-
assert.deepStrictEqual(createStageWorkspaceConfig("mac", "universal"), {
227+
assert.deepStrictEqual(createStageWorkspaceConfig({ platform: "mac", arch: "universal" }), {
230228
supportedArchitectures: {
231229
os: ["darwin"],
232230
cpu: ["arm64", "x64"],
233231
},
234232
});
235233
});
236234

235+
it("stages pnpm 11 allowBuilds and patchedDependencies in the workspace yaml", () => {
236+
assert.deepStrictEqual(
237+
createStageWorkspaceConfig({
238+
platform: "linux",
239+
arch: "x64",
240+
allowBuilds: {
241+
electron: true,
242+
"node-pty": true,
243+
"browser-tabs-lock": false,
244+
},
245+
patchedDependencies: {
246+
"effect@4.0.0-beta.73": "patches/effect@4.0.0-beta.73.patch",
247+
},
248+
overrides: {
249+
effect: "4.0.0-beta.73",
250+
},
251+
}),
252+
{
253+
supportedArchitectures: {
254+
os: ["linux"],
255+
cpu: ["x64"],
256+
},
257+
allowBuilds: {
258+
electron: true,
259+
"node-pty": true,
260+
"browser-tabs-lock": false,
261+
},
262+
patchedDependencies: {
263+
"effect@4.0.0-beta.73": "patches/effect@4.0.0-beta.73.patch",
264+
},
265+
overrides: {
266+
effect: "4.0.0-beta.73",
267+
},
268+
},
269+
);
270+
271+
// Empty maps must not be written — pnpm would still require reviewed
272+
// packages if allowBuilds is present but incomplete, and omitting empty
273+
// patchedDependencies keeps the stage yaml minimal.
274+
assert.deepStrictEqual(
275+
createStageWorkspaceConfig({
276+
platform: "mac",
277+
arch: "arm64",
278+
allowBuilds: {},
279+
patchedDependencies: {},
280+
overrides: {},
281+
}),
282+
{
283+
supportedArchitectures: {
284+
os: ["darwin"],
285+
cpu: ["arm64"],
286+
},
287+
},
288+
);
289+
});
290+
237291
it("unpacks the fff shared library for filesystem and FFI access", () => {
238292
assert.deepStrictEqual(DESKTOP_ASAR_UNPACK, ["node_modules/@ff-labs/fff-bin-*/**/*"]);
239293
});

scripts/build-desktop-artifact.ts

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const WorkspaceConfig = Schema.Struct({
4040
catalog: Schema.optional(Schema.Record(Schema.String, Schema.String)),
4141
overrides: Schema.optional(Schema.Record(Schema.String, Schema.String)),
4242
patchedDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
43+
allowBuilds: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)),
4344
});
4445
type WorkspaceConfig = typeof WorkspaceConfig.Type;
4546

@@ -49,7 +50,14 @@ const StageWorkspaceConfig = Schema.Struct({
4950
cpu: Schema.Array(Schema.String),
5051
libc: Schema.optional(Schema.Array(Schema.String)),
5152
}),
53+
// pnpm 11 only reads these from pnpm-workspace.yaml (not package.json#pnpm).
54+
// Without allowBuilds the staged `vp install --prod` fails with
55+
// ERR_PNPM_IGNORED_BUILDS for packages that have lifecycle scripts.
56+
allowBuilds: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)),
57+
patchedDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
58+
overrides: Schema.optional(Schema.Record(Schema.String, Schema.String)),
5259
});
60+
type StageWorkspaceConfig = typeof StageWorkspaceConfig.Type;
5361

5462
const RepoRoot = Effect.service(Path.Path).pipe(
5563
Effect.flatMap((path) => path.fromFileUrl(new URL("..", import.meta.url))),
@@ -563,10 +571,6 @@ interface StagePackageJson {
563571
readonly devDependencies: {
564572
readonly electron: string;
565573
};
566-
readonly overrides: Record<string, unknown>;
567-
readonly pnpm?: {
568-
readonly patchedDependencies?: Record<string, string>;
569-
};
570574
}
571575

572576
export const STAGE_INSTALL_ARGS = ["install", "--prod"] as const;
@@ -876,47 +880,52 @@ const stageClerkPasskeyNativeBinaries = Effect.fn("stageClerkPasskeyNativeBinari
876880
}
877881
});
878882

879-
export function createStageWorkspaceConfig(
880-
platform: typeof BuildPlatform.Type,
881-
arch: typeof BuildArch.Type,
882-
): typeof StageWorkspaceConfig.Type {
883+
export function createStageWorkspaceConfig(input: {
884+
readonly platform: typeof BuildPlatform.Type;
885+
readonly arch: typeof BuildArch.Type;
886+
readonly allowBuilds?: Record<string, boolean>;
887+
readonly patchedDependencies?: Record<string, string>;
888+
readonly overrides?: Record<string, string>;
889+
}): StageWorkspaceConfig {
890+
const { platform, arch, allowBuilds, patchedDependencies, overrides } = input;
883891
const hostOs = platform === "mac" ? "darwin" : platform === "win" ? "win32" : "linux";
884892
const hostCpu = arch === "universal" ? ["arm64", "x64"] : [arch];
885893
// Windows artifacts also bundle the same-architecture WSL Linux backend, which loads
886894
// Linux-native optional deps at runtime (e.g. @yuuang/ffi-rs-linux-x64-gnu).
887895
// Pull the Linux (glibc) variants in addition to the host platform's so
888896
// they ship in the asar; without them the WSL backend crash-loops on require
889897
// ("Cannot find module '@yuuang/ffi-rs-linux-x64-gnu'").
890-
if (platform === "win") {
891-
return {
892-
supportedArchitectures: {
893-
os: Array.from(new Set([hostOs, "linux"])),
894-
cpu: hostCpu,
895-
libc: ["glibc"],
896-
},
897-
};
898-
}
898+
const supportedArchitectures =
899+
platform === "win"
900+
? {
901+
os: Array.from(new Set([hostOs, "linux"])),
902+
cpu: hostCpu,
903+
libc: ["glibc"],
904+
}
905+
: {
906+
os: [hostOs],
907+
cpu: hostCpu,
908+
};
909+
899910
return {
900-
supportedArchitectures: {
901-
os: [hostOs],
902-
cpu: hostCpu,
903-
},
911+
supportedArchitectures,
912+
...(allowBuilds && Object.keys(allowBuilds).length > 0 ? { allowBuilds } : {}),
913+
...(patchedDependencies && Object.keys(patchedDependencies).length > 0
914+
? { patchedDependencies }
915+
: {}),
916+
...(overrides && Object.keys(overrides).length > 0 ? { overrides } : {}),
904917
};
905918
}
906919

907-
export function createStagePnpmConfig(
920+
export function createStagePatchedDependencies(
908921
patchedDependencies: Record<string, string>,
909922
dependencies: Record<string, unknown>,
910-
): StagePackageJson["pnpm"] | undefined {
911-
const stagePatchedDependencies = Object.fromEntries(
923+
): Record<string, string> {
924+
return Object.fromEntries(
912925
Object.entries(patchedDependencies).filter(([patchKey]) =>
913926
Object.hasOwn(dependencies, getPatchedDependencyPackageName(patchKey)),
914927
),
915928
);
916-
917-
return Object.keys(stagePatchedDependencies).length > 0
918-
? { patchedDependencies: stagePatchedDependencies }
919-
: undefined;
920929
}
921930

922931
function getPatchedDependencyPackageName(patchKey: string): string {
@@ -1540,6 +1549,7 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
15401549
const workspaceCatalog = workspaceConfig.catalog ?? {};
15411550
const workspaceOverrides = workspaceConfig.overrides ?? {};
15421551
const workspacePatchedDependencies = workspaceConfig.patchedDependencies ?? {};
1552+
const workspaceAllowBuilds = workspaceConfig.allowBuilds ?? {};
15431553

15441554
const platformConfig = PLATFORM_CONFIG[options.platform];
15451555
if (!platformConfig) {
@@ -1709,7 +1719,10 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
17091719
)
17101720
: {}),
17111721
};
1712-
const stagePnpmConfig = createStagePnpmConfig(workspacePatchedDependencies, stageDependencies);
1722+
const stagePatchedDependencies = createStagePatchedDependencies(
1723+
workspacePatchedDependencies,
1724+
stageDependencies,
1725+
);
17131726
const stagePackageJson: StagePackageJson = {
17141727
name: "t3code",
17151728
version: appVersion,
@@ -1738,20 +1751,24 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
17381751
devDependencies: {
17391752
electron: electronVersion,
17401753
},
1741-
overrides: resolvedOverrides,
1742-
...(stagePnpmConfig ? { pnpm: stagePnpmConfig } : {}),
17431754
};
17441755

17451756
const stagePackageJsonString = yield* encodeJsonString(stagePackageJson);
17461757
yield* fs.writeFileString(path.join(stageAppDir, "package.json"), `${stagePackageJsonString}\n`);
1747-
const stageWorkspaceConfig = createStageWorkspaceConfig(options.platform, options.arch);
1758+
const stageWorkspaceConfig = createStageWorkspaceConfig({
1759+
platform: options.platform,
1760+
arch: options.arch,
1761+
allowBuilds: workspaceAllowBuilds,
1762+
patchedDependencies: stagePatchedDependencies,
1763+
overrides: resolvedOverrides,
1764+
});
17481765
const stageWorkspaceConfigString = yield* encodeStageWorkspaceConfig(stageWorkspaceConfig);
17491766
yield* fs.writeFileString(
17501767
path.join(stageAppDir, "pnpm-workspace.yaml"),
17511768
stageWorkspaceConfigString,
17521769
);
17531770

1754-
if (Object.keys(workspacePatchedDependencies).length > 0) {
1771+
if (Object.keys(stagePatchedDependencies).length > 0) {
17551772
yield* fs.copy(path.join(repoRoot, "patches"), path.join(stageAppDir, "patches"));
17561773
}
17571774

0 commit comments

Comments
 (0)