From cba1ccce6df2e2f360c6841720fdc174f2dc96db Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 12 Mar 2026 14:41:33 -0700 Subject: [PATCH] Add killTree to recursively kill child processes --- src/run.ts | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/run.ts b/src/run.ts index a66968d..d0f9beb 100644 --- a/src/run.ts +++ b/src/run.ts @@ -28,6 +28,32 @@ type RunOptions = { onCommand?: (cmd: Command) => void } +function getDescendants(pid: number): number[] { + try { + const result = Bun.spawnSync(["ps", "-o", "pid=", "--ppid", String(pid)]) + const output = new TextDecoder().decode(result.stdout) + const children = output.trim().split("\n").filter(Boolean).map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n)) + const all: number[] = [] + for (const child of children) { + all.push(...getDescendants(child)) + all.push(child) + } + return all + } catch { + return [] + } +} + +function killTree(pid: number): void { + const descendants = getDescendants(pid) + // Kill process group first + try { process.kill(-pid, "SIGKILL") } catch {} + // Then kill any descendants that escaped the process group + for (const d of descendants) { + try { process.kill(d, "SIGKILL") } catch {} + } +} + const SENTINEL_PREFIX = "__SHOUT_SENTINEL_" function buildScript(commands: Command[], sentinel: string): string { @@ -190,7 +216,7 @@ export async function runFile( } } finally { if (proc.pid) { - try { process.kill(-proc.pid, "SIGKILL") } catch {} + killTree(proc.pid) } } }