This commit is contained in:
seb
2026-10-07 22:10:24 +02:00
parent d49ec77043
commit 3d1601595e
2 changed files with 71 additions and 6 deletions

View File

@@ -65,7 +65,12 @@ function startProc(cmd, procArgs, env, tag) {
cwd: FRONT_ROOT,
env: { ...process.env, ...env },
stdio: ['ignore', 'pipe', 'pipe'],
// Own process group per child so stopProc can kill the whole tree
// (e.g. `npx vite` wrapper + the real vite process).
detached: process.platform !== 'win32',
});
// Piped stdio keeps the parent alive even after kill(); detach the streams
// so teardown below can actually exit.
child.stdout.on('data', (d) => process.stdout.write(`[${tag}] ${d}`));
child.stderr.on('data', (d) => {
const text = String(d);
@@ -73,9 +78,55 @@ function startProc(cmd, procArgs, env, tag) {
if (tag === 'vite' && text.includes('[vite-proxy]')) return;
process.stderr.write(`[${tag}] ${d}`);
});
child.stdout.unref?.();
child.stderr.unref?.();
child.unref?.();
return child;
}
async function stopProc(child, tag, timeoutMs = 8000) {
if (!child || child.exitCode != null || child.signalCode != null) return;
// Kill the whole process group (child + grandchildren like npx/vite).
const killTree = (sig) => {
try {
if (child.pid && process.platform !== 'win32') {
process.kill(-child.pid, sig);
return;
}
} catch {
// fall through to single kill
}
try {
child.kill(sig);
} catch {
// already gone
}
};
await new Promise((resolve) => {
const timer = setTimeout(() => {
killTree('SIGKILL');
resolve();
}, timeoutMs);
timer.unref?.();
child.once('exit', () => {
clearTimeout(timer);
resolve();
});
killTree('SIGTERM');
});
// Drain any buffered pipe listeners so they can't hold the loop open.
try {
child.stdout?.destroy();
} catch {
// ignore
}
try {
child.stderr?.destroy();
} catch {
// ignore
}
}
async function main() {
if (!fs.existsSync(CHROME)) {
throw new Error(`Chrome not found at ${CHROME} (pass --chrome=...)`);
@@ -135,15 +186,18 @@ async function main() {
}
}
} finally {
chrome.kill();
vite.kill();
dummy.kill();
await stopProc(chrome, 'chrome');
await stopProc(vite, 'vite');
await stopProc(dummy, 'dummy');
}
if (failed > 0) {
console.error(`[capture] ${failed} shots failed`);
process.exit(1);
}
console.log(`[capture] done → ${OUT_DIR}`);
// Explicit exit: spawned grandchildren (vite/esbuild, chrome helpers) can
// otherwise keep the event loop alive after teardown.
process.exit(0);
}
main().catch((err) => {