Fix dev server Ctrl-C shutdown wait
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { spawn } from "node:child_process";
|
||||
import { fileURLToPath } from "url";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
@@ -12,34 +12,44 @@ const inspectArg = process.env.PASEO_NODE_INSPECT ?? "--inspect";
|
||||
const inspectArgs =
|
||||
inspectArg === "0" || inspectArg === "false" || inspectArg === "off" ? [] : [inspectArg];
|
||||
|
||||
// The supervisor handles SIGINT/SIGTERM itself and needs time to drain the
|
||||
// worker gracefully. Ignore them here so spawnSync blocks until the supervisor
|
||||
// finishes shutting down, instead of the parent dying on Ctrl-C and releasing
|
||||
// the shell while the daemon is still logging.
|
||||
process.on("SIGINT", () => {});
|
||||
process.on("SIGTERM", () => {});
|
||||
const supervisorArgs = [
|
||||
...inspectArgs,
|
||||
"--heapsnapshot-near-heap-limit=3",
|
||||
"--max-old-space-size=3072",
|
||||
"--report-on-fatalerror",
|
||||
"--report-directory=/tmp/paseo-reports",
|
||||
...process.execArgv,
|
||||
daemonRunnerEntry,
|
||||
"--dev",
|
||||
...process.argv.slice(2),
|
||||
];
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[
|
||||
...inspectArgs,
|
||||
"--heapsnapshot-near-heap-limit=3",
|
||||
"--max-old-space-size=3072",
|
||||
"--report-on-fatalerror",
|
||||
"--report-directory=/tmp/paseo-reports",
|
||||
...process.execArgv,
|
||||
daemonRunnerEntry,
|
||||
"--dev",
|
||||
...process.argv.slice(2),
|
||||
],
|
||||
{
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
},
|
||||
);
|
||||
const supervisor = spawn(process.execPath, supervisorArgs, {
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
function exitCodeForSignal(signal: NodeJS.Signals): number {
|
||||
return signal === "SIGINT" ? 130 : 1;
|
||||
}
|
||||
|
||||
process.exit(result.status ?? 1);
|
||||
function forwardSignal(signal: NodeJS.Signals): void {
|
||||
if (supervisor.exitCode !== null || supervisor.signalCode !== null || supervisor.killed) {
|
||||
return;
|
||||
}
|
||||
supervisor.kill(signal);
|
||||
}
|
||||
|
||||
// The supervisor handles SIGINT/SIGTERM itself and needs time to drain the
|
||||
// worker gracefully. Keep this wrapper alive until the supervisor exits so npm
|
||||
// does not release the shell while the daemon is still logging final shutdown.
|
||||
process.on("SIGINT", () => forwardSignal("SIGINT"));
|
||||
process.on("SIGTERM", () => forwardSignal("SIGTERM"));
|
||||
|
||||
supervisor.on("error", (error) => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
supervisor.on("exit", (code, signal) => {
|
||||
process.exit(code ?? (signal ? exitCodeForSignal(signal) : 1));
|
||||
});
|
||||
|
||||
@@ -14,4 +14,12 @@ describe("supervision parity", () => {
|
||||
|
||||
expect(daemonRunnerCalls + devRunnerCalls).toBe(1);
|
||||
});
|
||||
|
||||
test("dev runner waits asynchronously for supervisor shutdown", () => {
|
||||
const devRunner = readFileSync(new URL("./dev-runner.ts", import.meta.url), "utf8");
|
||||
|
||||
expect(devRunner).toContain('import { spawn } from "node:child_process"');
|
||||
expect(devRunner).not.toContain("spawnSync");
|
||||
expect(devRunner).toContain('supervisor.on("exit"');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user