From c4c0d4d2961ca265e967afd0a03d02de57ca70d0 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Tue, 3 Nov 2026 16:09:13 +0530 Subject: [PATCH 1/3] fix(cli): validate npm runtime before publishing the cache --- scripts/build-npm-launcher.test.ts | 93 +++++++++++++++++++++++++----- scripts/npm-launcher/t3.mjs | 24 +++++++- 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/scripts/build-npm-launcher.test.ts b/scripts/build-npm-launcher.test.ts index 4c7edd4763359..c4d72bcd19357 100644 --- a/scripts/build-npm-launcher.test.ts +++ b/scripts/build-npm-launcher.test.ts @@ -47,7 +47,15 @@ const run = Effect.fn("test.run")(function* ( return { stdout, stderr, exitCode }; }, Effect.scoped); -const serveFakeRelease = Effect.fn("test.serveFakeRelease")(function* (root: string) { +const serveFakeRelease = Effect.fn("test.serveFakeRelease")(function* ( + root: string, + executable = `#!/bin/sh +if [ -n "$T3CODE_TEST_CALLS" ]; then echo "$*|$T3CODE_NPM_LAUNCHER_PATH" >> "$T3CODE_TEST_CALLS"; fi +if [ "$1" = "--version" ]; then echo "${VERSION}"; exit 0; fi +echo "stub $* launcher=$T3CODE_NPM_LAUNCHER_PATH" +exit 7 +`, +) { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const key = `${yield* HostProcess.Platform}-${yield* HostProcess.Architecture}`; @@ -56,10 +64,7 @@ const serveFakeRelease = Effect.fn("test.serveFakeRelease")(function* (root: str yield* fs.makeDirectory(path.join(root, "release", stem, "node_modules/native"), { recursive: true, }); - yield* fs.writeFileString( - path.join(root, "release", stem, "t3"), - '#!/bin/sh\necho "stub $* launcher=$T3CODE_NPM_LAUNCHER_PATH"\nexit 7\n', - ); + yield* fs.writeFileString(path.join(root, "release", stem, "t3"), executable); yield* fs.chmod(path.join(root, "release", stem, "t3"), 0o755); const tar = yield* run("tar", ["-czf", archiveName, stem], { cwd: path.join(root, "release"), @@ -136,6 +141,7 @@ it.layer(NodeServices.layer)("build-npm-launcher", (it) => { const env = { PATH: process.env.PATH, T3CODE_HOME: home, + T3CODE_TEST_CALLS: path.join(root, "calls"), T3CODE_RELEASE_BASE_URL: release.baseUrl, }; const launcher = path.join(packageDir, "bin/t3.mjs"); @@ -145,6 +151,10 @@ it.layer(NodeServices.layer)("build-npm-launcher", (it) => { }); assert.equal(first.stdout.trim(), `stub serve a b launcher=${launcher}`, first.stderr); assert.equal(first.exitCode, 7); + assert.deepStrictEqual( + (yield* fs.readFileString(env.T3CODE_TEST_CALLS)).trim().split("\n"), + ["--version|", `serve a b|${launcher}`], + ); const versionDir = path.join(home, "runtime/versions", VERSION); assert.equal( (yield* fs.readFileString(path.join(versionDir, ".install-complete"))).trim(), @@ -158,8 +168,12 @@ it.layer(NodeServices.layer)("build-npm-launcher", (it) => { const requestsAfterFirst = release.requests.length; const second = yield* run(process.execPath, [launcher, "--version"], { cwd: root, env }); - assert.equal(second.exitCode, 7); + assert.equal(second.exitCode, 0); assert.equal(release.requests.length, requestsAfterFirst); + assert.deepStrictEqual( + (yield* fs.readFileString(env.T3CODE_TEST_CALLS)).trim().split("\n"), + ["--version|", `serve a b|${launcher}`, `--version|${launcher}`], + ); const ipcServer = path.join(home, "runtime/versions", VERSION, "t3"); yield* fs.writeFileString( @@ -189,8 +203,8 @@ it.layer(NodeServices.layer)("build-npm-launcher", (it) => { env: { ...env, T3CODE_HOME: raceHome }, }); const [one, two] = yield* Effect.all([launch, launch], { concurrency: "unbounded" }); - assert.equal(one.exitCode, 7, one.stderr); - assert.equal(two.exitCode, 7, two.stderr); + assert.equal(one.exitCode, 0, one.stderr); + assert.equal(two.exitCode, 0, two.stderr); assert.deepStrictEqual(yield* fs.readDirectory(path.join(raceHome, "runtime/versions")), [ VERSION, ]); @@ -296,12 +310,8 @@ fs.readFileSync = (file, ...rest) => { cwd: root, env: { ...env, T3CODE_HOME: staleOnlyHome }, }); - assert.equal( - replaced.stdout.trim(), - `stub --version launcher=${launcher}`, - replaced.stderr, - ); - assert.equal(replaced.exitCode, 7); + assert.equal(replaced.stdout.trim(), VERSION, replaced.stderr); + assert.equal(replaced.exitCode, 0); assert.deepStrictEqual( yield* fs.readDirectory(path.join(staleOnlyHome, "runtime/versions")), [VERSION], @@ -320,4 +330,59 @@ fs.readFileSync = (file, ...rest) => { ); }), ); + it.effect.skipIf(HostProcess.Platform.defaultValue() === "win32")( + "does not publish an unusable release or redownload a complete unusable cache", + () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + for (const executable of [ + "#!/t3-test-missing-loader\n", + '#!/bin/sh\necho "runtime loader failed" >&2\nexit 42\n', + "#!/bin/sh\nkill -TERM $$\n", + ]) { + const root = yield* fs.makeTempDirectoryScoped({ prefix: "t3-npm-validation-test-" }); + const output = yield* buildNpmLauncher({ + version: VERSION, + outputDir: path.join(root, "out"), + }); + const release = yield* serveFakeRelease(root, executable); + const home = path.join(root, "home"); + const dir = path.join(home, "runtime/versions", VERSION); + const env = { + PATH: process.env.PATH, + T3CODE_HOME: home, + T3CODE_RELEASE_BASE_URL: release.baseUrl, + }; + const launcher = path.join(output.packageDir, "bin/t3.mjs"); + const failed = yield* run(process.execPath, [launcher, "serve"], { cwd: root, env }); + assert.equal(failed.exitCode, 1, failed.stderr); + assert.deepStrictEqual(yield* fs.readDirectory(path.dirname(dir)), [], failed.stderr); + assert.include(failed.stderr, "could not run"); + if (executable.includes("exit 42")) { + assert.include(failed.stderr, "runtime loader failed"); + } + if ((yield* HostProcess.Platform) === "linux") { + assert.include(failed.stderr, "glibc"); + assert.include(failed.stderr, "musl"); + } + + yield* fs.makeDirectory(dir); + yield* fs.writeFileString(path.join(dir, "t3"), "#!/t3-test-missing-loader\n"); + yield* fs.chmod(path.join(dir, "t3"), 0o755); + yield* fs.writeFileString(path.join(dir, ".install-complete"), `${VERSION}\n`); + const requests = release.requests.length; + for (const _attempt of [1, 2]) { + const cached = yield* run(process.execPath, [launcher, "serve"], { cwd: root, env }); + assert.equal(cached.exitCode, 1, cached.stderr); + assert.include(cached.stderr, "could not run"); + assert.equal(release.requests.length, requests); + assert.equal( + yield* fs.readFileString(path.join(dir, ".install-complete")), + `${VERSION}\n`, + ); + } + } + }), + ); }); diff --git a/scripts/npm-launcher/t3.mjs b/scripts/npm-launcher/t3.mjs index f8828210862e3..edd034300ad62 100644 --- a/scripts/npm-launcher/t3.mjs +++ b/scripts/npm-launcher/t3.mjs @@ -90,6 +90,19 @@ async function install(dir, key) { }); if (extract.status !== 0) throw new Error(`could not extract ${archive}`); fs.rmSync(file); + const validate = spawnSync( + path.join(staging, process.platform === "win32" ? "t3.exe" : "t3"), + ["--version"], + { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8", timeout: 30_000 }, + ); + if (validate.status !== 0) { + throw executableError( + key, + validate.error?.message || + validate.stderr?.trim() || + `--version exited with ${validate.signal || validate.status}`, + ); + } fs.writeFileSync(path.join(staging, ".install-complete"), `${version}\n`); publish(staging, dir); } finally { @@ -172,12 +185,21 @@ async function main() { for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) { process.on(signal, () => (ipc || signal !== "SIGINT") && child.kill(signal)); } - child.on("error", (error) => fail(error)); + child.on("error", (error) => fail(executableError(key, error.message))); child.on("exit", (code, signal) => process.exit(code ?? 128 + (os.constants.signals[signal] || 1)), ); } +function executableError(key, detail) { + return new Error( + `could not run T3 Code ${version} for ${key}: ${detail}` + + (process.platform === "linux" + ? "\nThe Linux release requires glibc; musl-based systems such as Alpine are not supported. Use a glibc-based Linux environment." + : ""), + ); +} + function fail(error) { process.stderr.write(`t3: ${error.message}\n`); process.exit(1); From 87607f7863bea3019201f1d6d794e8b113489128 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Tue, 3 Nov 2026 16:15:36 +0530 Subject: [PATCH 2/3] test(scripts): check unusable launcher cache once --- scripts/build-npm-launcher.test.ts | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/scripts/build-npm-launcher.test.ts b/scripts/build-npm-launcher.test.ts index c4d72bcd19357..a36ffdfab5ea8 100644 --- a/scripts/build-npm-launcher.test.ts +++ b/scripts/build-npm-launcher.test.ts @@ -175,6 +175,18 @@ it.layer(NodeServices.layer)("build-npm-launcher", (it) => { ["--version|", `serve a b|${launcher}`, `--version|${launcher}`], ); + yield* fs.writeFileString(path.join(versionDir, "t3"), "#!/t3-test-missing-loader\n"); + for (const _attempt of [1, 2]) { + const cached = yield* run(process.execPath, [launcher, "serve"], { cwd: root, env }); + assert.equal(cached.exitCode, 1, cached.stderr); + assert.include(cached.stderr, "could not run"); + assert.equal(release.requests.length, requestsAfterFirst); + assert.equal( + yield* fs.readFileString(path.join(versionDir, ".install-complete")), + `${VERSION}\n`, + ); + } + const ipcServer = path.join(home, "runtime/versions", VERSION, "t3"); yield* fs.writeFileString( ipcServer, @@ -331,7 +343,7 @@ fs.readFileSync = (file, ...rest) => { }), ); it.effect.skipIf(HostProcess.Platform.defaultValue() === "win32")( - "does not publish an unusable release or redownload a complete unusable cache", + "does not publish an unusable release", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; @@ -366,22 +378,6 @@ fs.readFileSync = (file, ...rest) => { assert.include(failed.stderr, "glibc"); assert.include(failed.stderr, "musl"); } - - yield* fs.makeDirectory(dir); - yield* fs.writeFileString(path.join(dir, "t3"), "#!/t3-test-missing-loader\n"); - yield* fs.chmod(path.join(dir, "t3"), 0o755); - yield* fs.writeFileString(path.join(dir, ".install-complete"), `${VERSION}\n`); - const requests = release.requests.length; - for (const _attempt of [1, 2]) { - const cached = yield* run(process.execPath, [launcher, "serve"], { cwd: root, env }); - assert.equal(cached.exitCode, 1, cached.stderr); - assert.include(cached.stderr, "could not run"); - assert.equal(release.requests.length, requests); - assert.equal( - yield* fs.readFileString(path.join(dir, ".install-complete")), - `${VERSION}\n`, - ); - } } }), ); From 0ff2a8002a0fe16d873a7e2c72335a8abf945817 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Tue, 3 Nov 2026 16:24:20 +0530 Subject: [PATCH 3/3] fix(scripts): enforce npm runtime validation timeout --- scripts/build-npm-launcher.test.ts | 26 ++++++++++++++++++++++++-- scripts/npm-launcher/t3.mjs | 7 ++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/scripts/build-npm-launcher.test.ts b/scripts/build-npm-launcher.test.ts index a36ffdfab5ea8..fd241e7543bae 100644 --- a/scripts/build-npm-launcher.test.ts +++ b/scripts/build-npm-launcher.test.ts @@ -352,6 +352,7 @@ fs.readFileSync = (file, ...rest) => { "#!/t3-test-missing-loader\n", '#!/bin/sh\necho "runtime loader failed" >&2\nexit 42\n', "#!/bin/sh\nkill -TERM $$\n", + '#!/bin/sh\ntrap \'echo caught > "$T3CODE_TEST_TERM"; exit 42\' TERM\necho ready > "$T3CODE_TEST_READY"\nwhile :; do :; done\n', ]) { const root = yield* fs.makeTempDirectoryScoped({ prefix: "t3-npm-validation-test-" }); const output = yield* buildNpmLauncher({ @@ -361,19 +362,40 @@ fs.readFileSync = (file, ...rest) => { const release = yield* serveFakeRelease(root, executable); const home = path.join(root, "home"); const dir = path.join(home, "runtime/versions", VERSION); + const probeTimeoutHook = path.join(root, "probe-timeout.mjs"); + yield* fs.writeFileString( + probeTimeoutHook, + `import childProcess from "node:child_process"; +import { syncBuiltinESMExports } from "node:module"; +const { spawnSync } = childProcess; +childProcess.spawnSync = (command, args, options) => spawnSync(command, args, + options?.timeout === undefined ? options : { ...options, timeout: 500 }); +syncBuiltinESMExports(); +`, + ); const env = { PATH: process.env.PATH, T3CODE_HOME: home, T3CODE_RELEASE_BASE_URL: release.baseUrl, + T3CODE_TEST_READY: path.join(root, "probe-ready"), + T3CODE_TEST_TERM: path.join(root, "probe-term"), }; const launcher = path.join(output.packageDir, "bin/t3.mjs"); - const failed = yield* run(process.execPath, [launcher, "serve"], { cwd: root, env }); + const failed = yield* run( + process.execPath, + ["--import", probeTimeoutHook, launcher, "serve"], + { cwd: root, env }, + ); assert.equal(failed.exitCode, 1, failed.stderr); assert.deepStrictEqual(yield* fs.readDirectory(path.dirname(dir)), [], failed.stderr); assert.include(failed.stderr, "could not run"); - if (executable.includes("exit 42")) { + if (executable.includes("runtime loader failed")) { assert.include(failed.stderr, "runtime loader failed"); } + if (executable.includes("trap")) { + assert.isTrue(yield* fs.exists(env.T3CODE_TEST_READY)); + assert.isFalse(yield* fs.exists(env.T3CODE_TEST_TERM)); + } if ((yield* HostProcess.Platform) === "linux") { assert.include(failed.stderr, "glibc"); assert.include(failed.stderr, "musl"); diff --git a/scripts/npm-launcher/t3.mjs b/scripts/npm-launcher/t3.mjs index edd034300ad62..207814ba9d311 100644 --- a/scripts/npm-launcher/t3.mjs +++ b/scripts/npm-launcher/t3.mjs @@ -93,7 +93,12 @@ async function install(dir, key) { const validate = spawnSync( path.join(staging, process.platform === "win32" ? "t3.exe" : "t3"), ["--version"], - { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8", timeout: 30_000 }, + { + stdio: ["ignore", "ignore", "pipe"], + encoding: "utf8", + timeout: 30_000, + killSignal: "SIGKILL", + }, ); if (validate.status !== 0) { throw executableError(