authorgravatar for saiansh2525@gmail.comYash Singh <saiansh2525@gmail.com> 2026-09-26 15:38:24-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-26 15:38:24-05:00
logea7d46ac107e6a3461b81065ebc86dfd5483095f
tree70895720cf8a1bfeda73e17096a8c28a0bc6e75b
parentd110f9867041f0466bed9b032c15efcfcefcbfc5
signature Signed by PGP key B5690EEEBB952194

fix(build): validate Linux node-pty prebuilds in Windows artifacts (#13867)


3 files changed, 73 insertions(+), 20 deletions(-)

pnpm-lock.yaml+4-13
......@@ -10438,10 +10438,6 @@ packages:
1043810438 resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
1043910439 engines: {node: '>=6'}
1044010440
10441 tough-cookie@6.0.1:
10442 resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
10443 engines: {node: '>=16'}
10444
1044510441 tough-cookie@6.0.2:
1044610442 resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==}
1044710443 engines: {node: '>=16'}
......@@ -18908,7 +18904,7 @@ snapshots:
1890818904 decimal.js: 10.6.0
1890918905 html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0)
1891018906 is-potential-custom-element-name: 1.0.1
18911 lru-cache: 11.5.2
18907 lru-cache: 11.5.3
1891218908 parse5: 8.0.1
1891318909 saxes: 6.0.0
1891418910 tough-cookie: 6.0.2
......@@ -20013,7 +20009,7 @@ snapshots:
2001320009 rettime: 0.10.1
2001420010 statuses: 2.0.2
2001520011 strict-event-emitter: 0.5.1
20016 tough-cookie: 6.0.1
20012 tough-cookie: 6.0.2
2001720013 type-fest: 5.7.0
2001820014 until-async: 3.0.2
2001920015 yargs: 17.7.2
......@@ -20039,7 +20035,7 @@ snapshots:
2003920035 rettime: 0.10.1
2004020036 statuses: 2.0.2
2004120037 strict-event-emitter: 0.5.1
20042 tough-cookie: 6.0.1
20038 tough-cookie: 6.0.2
2004320039 type-fest: 5.7.0
2004420040 until-async: 3.0.2
2004520041 yargs: 17.7.2
......@@ -20516,7 +20512,7 @@ snapshots:
2051620512
2051720513 path-scurry@2.0.2:
2051820514 dependencies:
20519 lru-cache: 11.5.2
20515 lru-cache: 11.5.3
2052020516 minipass: 7.1.3
2052120517
2052220518 path-to-regexp@6.1.0: {}
......@@ -21953,11 +21949,6 @@ snapshots:
2195321949
2195421950 totalist@3.0.1: {}
2195521951
21956 tough-cookie@6.0.1:
21957 dependencies:
21958 tldts: 7.4.2
21959 optional: true
21960
2196121952 tough-cookie@6.0.2:
2196221953 dependencies:
2196321954 tldts: 7.4.2
scripts/build-desktop-artifact.test.ts+61-2
......@@ -175,6 +175,8 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu
175175 readonly copyUnpackedNatives: boolean;
176176 readonly serverEntrySource?: string;
177177 readonly wslRuntime?: "valid" | "loose-server-tree" | "missing-pty" | "bad-digest";
178 readonly targetArch?: "x64" | "arm64";
179 readonly ptyPrebuildArch?: "x64" | "arm64";
178180}) {
179181 const fs = yield* FileSystem.FileSystem;
180182 const path = yield* Path.Path;
......@@ -212,7 +214,7 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu
212214 yield* fs.writeFileString(path.join(packagedAppDir, "chrome_crashpad_handler.exe"), "crashpad");
213215
214216 if (input.wslRuntime !== undefined) {
215 const stem = wslRuntimeArchiveStem(WINDOWS_PAYLOAD_FIXTURE_VERSION, "x64");
217 const stem = wslRuntimeArchiveStem(WINDOWS_PAYLOAD_FIXTURE_VERSION, input.targetArch ?? "x64");
216218 const sourceArchivePath =
217219 input.wslRuntime === "loose-server-tree"
218220 ? // The old hand-rolled runtime: apps/server/dist + node_modules at the
......@@ -226,9 +228,16 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu
226228 : yield* makeLinuxCliArchiveFixture({
227229 root: path.join(tempDir, "wsl-runtime"),
228230 stem,
229 ...(input.wslRuntime === "missing-pty"
231 ...(input.wslRuntime === "missing-pty" || input.ptyPrebuildArch !== undefined
230232 ? { omitMembers: [`${stem}/node_modules/node-pty/build/Release/pty.node`] }
231233 : {}),
234 ...(input.ptyPrebuildArch !== undefined
235 ? {
236 extraMembers: [
237 `${stem}/node_modules/node-pty/prebuilds/linux-${input.ptyPrebuildArch}/pty.node`,
238 ],
239 }
240 : {}),
232241 });
233242 const archivePath = path.join(resourcesDir, WSL_RUNTIME_ARCHIVE_NAME);
234243 const hashPath = path.join(resourcesDir, WSL_RUNTIME_ARCHIVE_HASH_NAME);
......@@ -1206,6 +1215,55 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
12061215 ).pipe(Effect.provideService(HostProcessPlatform, "linux")),
12071216 );
12081217
1218 for (const targetArch of ["x64", "arm64"] as const) {
1219 it.effect(`accepts an embedded archive with the Linux ${targetArch} node-pty prebuild`, () =>
1220 Effect.scoped(
1221 Effect.gen(function* () {
1222 const fixture = yield* makeWindowsPayloadFixture({
1223 copyUnpackedNatives: true,
1224 wslRuntime: "valid",
1225 targetArch,
1226 ptyPrebuildArch: targetArch,
1227 });
1228 const result = yield* validateWindowsPackagedPayload({
1229 stageDistDir: fixture.stageDistDir,
1230 appExecutableName: fixture.appExecutableName,
1231 targetArch,
1232 appVersion: WINDOWS_PAYLOAD_FIXTURE_VERSION,
1233 expectWslRuntime: true,
1234 });
1235
1236 assert.equal(result.packagedAppDir, fixture.packagedAppDir);
1237 }),
1238 ).pipe(Effect.provideService(HostProcessPlatform, "linux")),
1239 );
1240
1241 it.effect(
1242 `rejects a node-pty prebuild for the wrong architecture in a Linux ${targetArch} archive`,
1243 () =>
1244 Effect.scoped(
1245 Effect.gen(function* () {
1246 const fixture = yield* makeWindowsPayloadFixture({
1247 copyUnpackedNatives: true,
1248 wslRuntime: "valid",
1249 targetArch,
1250 ptyPrebuildArch: targetArch === "x64" ? "arm64" : "x64",
1251 });
1252 const error = yield* validateWindowsPackagedPayload({
1253 stageDistDir: fixture.stageDistDir,
1254 appExecutableName: fixture.appExecutableName,
1255 targetArch,
1256 appVersion: WINDOWS_PAYLOAD_FIXTURE_VERSION,
1257 expectWslRuntime: true,
1258 }).pipe(Effect.flip);
1259
1260 assert.instanceOf(error, WindowsPackagedPayloadValidationError);
1261 assert.equal(error.reason, "wsl-runtime-invalid");
1262 }),
1263 ),
1264 );
1265 }
1266
12091267 it.effect("rejects an embedded archive built for a different release version", () =>
12101268 Effect.scoped(
12111269 Effect.gen(function* () {
......@@ -1285,6 +1343,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
12851343 assert.equal(error.reason, "wsl-runtime-invalid");
12861344 assert.deepStrictEqual(error.missingFiles, [
12871345 `${wslRuntimeArchiveStem(WINDOWS_PAYLOAD_FIXTURE_VERSION, "x64")}/node_modules/node-pty/build/Release/pty.node`,
1346 `${wslRuntimeArchiveStem(WINDOWS_PAYLOAD_FIXTURE_VERSION, "x64")}/node_modules/node-pty/prebuilds/linux-x64/pty.node`,
12881347 ]);
12891348 }),
12901349 ),
scripts/build-desktop-artifact.ts+8-5
......@@ -3291,13 +3291,16 @@ export const validateWindowsPackagedPayload = Effect.fn(
32913291 ),
32923292 );
32933293 }
3294 const requiredMembers = [
3295 `${stem}/t3`,
3296 `${stem}/client`,
3297 `${stem}/node_modules`,
3294 const requiredMembers = [`${stem}/t3`, `${stem}/client`, `${stem}/node_modules`];
3295 const missingMembers = requiredMembers.filter((member) => !members.includes(member));
3296 // node-pty can load a source build or the prebuild for the WSL target.
3297 const ptyCandidates = [
32983298 `${stem}/node_modules/node-pty/build/Release/pty.node`,
3299 `${stem}/node_modules/node-pty/prebuilds/linux-${input.targetArch}/pty.node`,
32993300 ];
3300 const missingMembers = requiredMembers.filter((member) => !members.includes(member));
3301 if (!ptyCandidates.some((member) => members.includes(member))) {
3302 missingMembers.push(...ptyCandidates);
3303 }
33013304 if (missingMembers.length > 0) {
33023305 return yield* new WindowsPackagedPayloadValidationError({
33033306 reason: "wsl-runtime-invalid",