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:...@@ -10438,10 +10438,6 @@ packages:
10438 resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}10438 resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
10439 engines: {node: '>=6'}10439 engines: {node: '>=6'}
1044010440
10441 tough-cookie@6.0.1:
10442 resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
10443 engines: {node: '>=16'}
10444
10445 tough-cookie@6.0.2:10441 tough-cookie@6.0.2:
10446 resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==}10442 resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==}
10447 engines: {node: '>=16'}10443 engines: {node: '>=16'}
...@@ -18908,7 +18904,7 @@ snapshots:...@@ -18908,7 +18904,7 @@ snapshots:
18908 decimal.js: 10.6.018904 decimal.js: 10.6.0
18909 html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0)18905 html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0)
18910 is-potential-custom-element-name: 1.0.118906 is-potential-custom-element-name: 1.0.1
18911 lru-cache: 11.5.218907 lru-cache: 11.5.3
18912 parse5: 8.0.118908 parse5: 8.0.1
18913 saxes: 6.0.018909 saxes: 6.0.0
18914 tough-cookie: 6.0.218910 tough-cookie: 6.0.2
...@@ -20013,7 +20009,7 @@ snapshots:...@@ -20013,7 +20009,7 @@ snapshots:
20013 rettime: 0.10.120009 rettime: 0.10.1
20014 statuses: 2.0.220010 statuses: 2.0.2
20015 strict-event-emitter: 0.5.120011 strict-event-emitter: 0.5.1
20016 tough-cookie: 6.0.120012 tough-cookie: 6.0.2
20017 type-fest: 5.7.020013 type-fest: 5.7.0
20018 until-async: 3.0.220014 until-async: 3.0.2
20019 yargs: 17.7.220015 yargs: 17.7.2
...@@ -20039,7 +20035,7 @@ snapshots:...@@ -20039,7 +20035,7 @@ snapshots:
20039 rettime: 0.10.120035 rettime: 0.10.1
20040 statuses: 2.0.220036 statuses: 2.0.2
20041 strict-event-emitter: 0.5.120037 strict-event-emitter: 0.5.1
20042 tough-cookie: 6.0.120038 tough-cookie: 6.0.2
20043 type-fest: 5.7.020039 type-fest: 5.7.0
20044 until-async: 3.0.220040 until-async: 3.0.2
20045 yargs: 17.7.220041 yargs: 17.7.2
...@@ -20516,7 +20512,7 @@ snapshots:...@@ -20516,7 +20512,7 @@ snapshots:
2051620512
20517 path-scurry@2.0.2:20513 path-scurry@2.0.2:
20518 dependencies:20514 dependencies:
20519 lru-cache: 11.5.220515 lru-cache: 11.5.3
20520 minipass: 7.1.320516 minipass: 7.1.3
2052120517
20522 path-to-regexp@6.1.0: {}20518 path-to-regexp@6.1.0: {}
...@@ -21953,11 +21949,6 @@ snapshots:...@@ -21953,11 +21949,6 @@ snapshots:
2195321949
21954 totalist@3.0.1: {}21950 totalist@3.0.1: {}
2195521951
21956 tough-cookie@6.0.1:
21957 dependencies:
21958 tldts: 7.4.2
21959 optional: true
21960
21961 tough-cookie@6.0.2:21952 tough-cookie@6.0.2:
21962 dependencies:21953 dependencies:
21963 tldts: 7.4.221954 tldts: 7.4.2
scripts/build-desktop-artifact.test.ts+61-2
...@@ -175,6 +175,8 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu...@@ -175,6 +175,8 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu
175 readonly copyUnpackedNatives: boolean;175 readonly copyUnpackedNatives: boolean;
176 readonly serverEntrySource?: string;176 readonly serverEntrySource?: string;
177 readonly wslRuntime?: "valid" | "loose-server-tree" | "missing-pty" | "bad-digest";177 readonly wslRuntime?: "valid" | "loose-server-tree" | "missing-pty" | "bad-digest";
178 readonly targetArch?: "x64" | "arm64";
179 readonly ptyPrebuildArch?: "x64" | "arm64";
178}) {180}) {
179 const fs = yield* FileSystem.FileSystem;181 const fs = yield* FileSystem.FileSystem;
180 const path = yield* Path.Path;182 const path = yield* Path.Path;
...@@ -212,7 +214,7 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu...@@ -212,7 +214,7 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu
212 yield* fs.writeFileString(path.join(packagedAppDir, "chrome_crashpad_handler.exe"), "crashpad");214 yield* fs.writeFileString(path.join(packagedAppDir, "chrome_crashpad_handler.exe"), "crashpad");
213215
214 if (input.wslRuntime !== undefined) {216 if (input.wslRuntime !== undefined) {
215 const stem = wslRuntimeArchiveStem(WINDOWS_PAYLOAD_FIXTURE_VERSION, "x64");217 const stem = wslRuntimeArchiveStem(WINDOWS_PAYLOAD_FIXTURE_VERSION, input.targetArch ?? "x64");
216 const sourceArchivePath =218 const sourceArchivePath =
217 input.wslRuntime === "loose-server-tree"219 input.wslRuntime === "loose-server-tree"
218 ? // The old hand-rolled runtime: apps/server/dist + node_modules at the220 ? // The old hand-rolled runtime: apps/server/dist + node_modules at the
...@@ -226,9 +228,16 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu...@@ -226,9 +228,16 @@ const makeWindowsPayloadFixture = Effect.fn("test.makeWindowsPayloadFixture")(fu
226 : yield* makeLinuxCliArchiveFixture({228 : yield* makeLinuxCliArchiveFixture({
227 root: path.join(tempDir, "wsl-runtime"),229 root: path.join(tempDir, "wsl-runtime"),
228 stem,230 stem,
229 ...(input.wslRuntime === "missing-pty"231 ...(input.wslRuntime === "missing-pty" || input.ptyPrebuildArch !== undefined
230 ? { omitMembers: [`${stem}/node_modules/node-pty/build/Release/pty.node`] }232 ? { omitMembers: [`${stem}/node_modules/node-pty/build/Release/pty.node`] }
231 : {}),233 : {}),
234 ...(input.ptyPrebuildArch !== undefined
235 ? {
236 extraMembers: [
237 `${stem}/node_modules/node-pty/prebuilds/linux-${input.ptyPrebuildArch}/pty.node`,
238 ],
239 }
240 : {}),
232 });241 });
233 const archivePath = path.join(resourcesDir, WSL_RUNTIME_ARCHIVE_NAME);242 const archivePath = path.join(resourcesDir, WSL_RUNTIME_ARCHIVE_NAME);
234 const hashPath = path.join(resourcesDir, WSL_RUNTIME_ARCHIVE_HASH_NAME);243 const hashPath = path.join(resourcesDir, WSL_RUNTIME_ARCHIVE_HASH_NAME);
...@@ -1206,6 +1215,55 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {...@@ -1206,6 +1215,55 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
1206 ).pipe(Effect.provideService(HostProcessPlatform, "linux")),1215 ).pipe(Effect.provideService(HostProcessPlatform, "linux")),
1207 );1216 );
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
1209 it.effect("rejects an embedded archive built for a different release version", () =>1267 it.effect("rejects an embedded archive built for a different release version", () =>
1210 Effect.scoped(1268 Effect.scoped(
1211 Effect.gen(function* () {1269 Effect.gen(function* () {
...@@ -1285,6 +1343,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {...@@ -1285,6 +1343,7 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
1285 assert.equal(error.reason, "wsl-runtime-invalid");1343 assert.equal(error.reason, "wsl-runtime-invalid");
1286 assert.deepStrictEqual(error.missingFiles, [1344 assert.deepStrictEqual(error.missingFiles, [
1287 `${wslRuntimeArchiveStem(WINDOWS_PAYLOAD_FIXTURE_VERSION, "x64")}/node_modules/node-pty/build/Release/pty.node`,1345 `${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`,
1288 ]);1347 ]);
1289 }),1348 }),
1290 ),1349 ),
scripts/build-desktop-artifact.ts+8-5
...@@ -3291,13 +3291,16 @@ export const validateWindowsPackagedPayload = Effect.fn(...@@ -3291,13 +3291,16 @@ export const validateWindowsPackagedPayload = Effect.fn(
3291 ),3291 ),
3292 );3292 );
3293 }3293 }
3294 const requiredMembers = [3294 const requiredMembers = [`${stem}/t3`, `${stem}/client`, `${stem}/node_modules`];
3295 `${stem}/t3`,3295 const missingMembers = requiredMembers.filter((member) => !members.includes(member));
3296 `${stem}/client`,3296 // node-pty can load a source build or the prebuild for the WSL target.
3297 `${stem}/node_modules`,3297 const ptyCandidates = [
3298 `${stem}/node_modules/node-pty/build/Release/pty.node`,3298 `${stem}/node_modules/node-pty/build/Release/pty.node`,
3299 `${stem}/node_modules/node-pty/prebuilds/linux-${input.targetArch}/pty.node`,
3299 ];3300 ];
3300 const missingMembers = requiredMembers.filter((member) => !members.includes(member));3301 if (!ptyCandidates.some((member) => members.includes(member))) {
3302 missingMembers.push(...ptyCandidates);
3303 }
3301 if (missingMembers.length > 0) {3304 if (missingMembers.length > 0) {
3302 return yield* new WindowsPackagedPayloadValidationError({3305 return yield* new WindowsPackagedPayloadValidationError({
3303 reason: "wsl-runtime-invalid",3306 reason: "wsl-runtime-invalid",