|
1 | 1 | // Runs a command with inline KEY=value assignments while preserving signal behavior. |
2 | | -import { spawn } from "node:child_process"; |
| 2 | +import { spawn, spawnSync } from "node:child_process"; |
3 | 3 |
|
4 | 4 | const ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/u; |
5 | 5 | const USAGE = "Usage: node scripts/run-with-env.mjs KEY=value [KEY=value ...] -- command [args...]"; |
@@ -80,6 +80,42 @@ export function resolveForceKillDelayMs(env = process.env) { |
80 | 80 | return parsed; |
81 | 81 | } |
82 | 82 |
|
| 83 | +/** |
| 84 | + * Signals the wrapped command tree when this small parent wrapper is stopped. |
| 85 | + */ |
| 86 | +export function signalRunWithEnvChild( |
| 87 | + child, |
| 88 | + signal, |
| 89 | + { |
| 90 | + platform = process.platform, |
| 91 | + runTaskkill = spawnSync, |
| 92 | + useChildProcessGroup = platform !== "win32", |
| 93 | + } = {}, |
| 94 | +) { |
| 95 | + if (useChildProcessGroup && typeof child.pid === "number") { |
| 96 | + try { |
| 97 | + process.kill(-child.pid, signal); |
| 98 | + return; |
| 99 | + } catch (error) { |
| 100 | + if (error?.code !== "ESRCH") { |
| 101 | + child.kill(signal); |
| 102 | + return; |
| 103 | + } |
| 104 | + } |
| 105 | + } |
| 106 | + if (platform === "win32" && typeof child.pid === "number") { |
| 107 | + const args = ["/PID", String(child.pid), "/T"]; |
| 108 | + if (signal === "SIGKILL") { |
| 109 | + args.push("/F"); |
| 110 | + } |
| 111 | + const result = runTaskkill("taskkill", args, { stdio: "ignore" }); |
| 112 | + if (!result?.error && result?.status === 0) { |
| 113 | + return; |
| 114 | + } |
| 115 | + } |
| 116 | + child.kill(signal); |
| 117 | +} |
| 118 | + |
83 | 119 | function main(argv = process.argv.slice(2)) { |
84 | 120 | if (isRunWithEnvHelpRequest(argv)) { |
85 | 121 | console.log(USAGE); |
@@ -120,20 +156,7 @@ function main(argv = process.argv.slice(2)) { |
120 | 156 | const forwardedSignals = useChildProcessGroup |
121 | 157 | ? ["SIGTERM", "SIGHUP", "SIGINT"] |
122 | 158 | : ["SIGTERM", "SIGHUP"]; |
123 | | - const signalChild = (signal) => { |
124 | | - if (useChildProcessGroup && typeof child.pid === "number") { |
125 | | - try { |
126 | | - process.kill(-child.pid, signal); |
127 | | - return; |
128 | | - } catch (error) { |
129 | | - if (error?.code !== "ESRCH") { |
130 | | - child.kill(signal); |
131 | | - return; |
132 | | - } |
133 | | - } |
134 | | - } |
135 | | - child.kill(signal); |
136 | | - }; |
| 159 | + const signalChild = (signal) => signalRunWithEnvChild(child, signal, { useChildProcessGroup }); |
137 | 160 | const childProcessGroupAlive = () => { |
138 | 161 | if (!useChildProcessGroup || typeof child.pid !== "number") { |
139 | 162 | return false; |
|
0 commit comments