fix(scripts): keep closed runtime command groups tracked

This commit is contained in:
Vincent Koc
2026-06-20 11:08:26 +02:00
parent 0767118c26
commit 6069a030c4
2 changed files with 69 additions and 1 deletions

View File

@@ -552,7 +552,9 @@ function trackGatewayChild(child) {
function trackCommandChild(child) {
activeCommandChildren.add(child);
const untrack = () => {
activeCommandChildren.delete(child);
if (!processTreeIsAlive(child)) {
activeCommandChildren.delete(child);
}
};
child.once("error", untrack);
child.once("close", untrack);

View File

@@ -767,6 +767,72 @@ describe("bundled plugin install/uninstall probe", () => {
},
);
it.runIf(process.platform !== "win32")(
"keeps closed runtime command groups tracked for parent cleanup",
async () => {
const root = makePackageRoot();
const commandPath = path.join(root, "closed-command.mjs");
const runnerPath = path.join(root, "run-closed-runtime-command.mjs");
const commandSettledPath = path.join(root, "command-settled");
const descendantPidPath = path.join(root, "closed-command-descendant.pid");
const descendantScript = [
"import fs from 'node:fs';",
`fs.writeFileSync(${JSON.stringify(descendantPidPath)}, String(process.pid));`,
"process.on('SIGTERM', () => {});",
"setInterval(() => {}, 1000);",
].join("\n");
fs.writeFileSync(
commandPath,
[
"import childProcess from 'node:child_process';",
`const child = childProcess.spawn(process.execPath, ["--input-type=module", "--eval", ${JSON.stringify(
descendantScript,
)}], { stdio: "ignore" });`,
"child.unref();",
"",
].join("\n"),
"utf8",
);
fs.writeFileSync(
runnerPath,
[
"import fs from 'node:fs';",
`const runtimeSmoke = await import(${JSON.stringify(pathToFileURL(runtimeSmokePath).href)});`,
`runtimeSmoke.runCommand(process.execPath, [${JSON.stringify(commandPath)}], {`,
" timeoutMs: 60_000,",
"}).finally(() => {",
` fs.writeFileSync(${JSON.stringify(commandSettledPath)}, "1");`,
"});",
"setInterval(() => {}, 1000);",
"",
].join("\n"),
"utf8",
);
const runner = spawn(process.execPath, [runnerPath], {
stdio: "ignore",
});
let descendantPid: number | undefined;
try {
await waitForFile(descendantPidPath, 1000);
descendantPid = Number(fs.readFileSync(descendantPidPath, "utf8"));
expect(pidIsAlive(descendantPid)).toBe(true);
await waitForFile(commandSettledPath, 1000);
runner.kill("SIGTERM");
await waitForDead(descendantPid, 2000);
} finally {
if (runner.pid && pidIsAlive(runner.pid)) {
runner.kill("SIGKILL");
}
if (descendantPid !== undefined && pidIsAlive(descendantPid)) {
process.kill(descendantPid, "SIGKILL");
}
}
},
);
it.runIf(process.platform !== "win32")(
"cleans detached runtime gateway groups when the parent is signaled",
async () => {