Files
fusion/packages/dashboard/scripts/run-vitest-with-heap.mjs
gsxdsm aa8bd3dc92 fix(FN-6043): recover stuck task processing
Fusion-Task-Id: FN-6043
2026-06-08 18:14:48 -07:00

142 lines
3.8 KiB
JavaScript

#!/usr/bin/env node
/* global clearInterval, clearTimeout, console, process, setInterval, setTimeout */
import { spawn } from "node:child_process";
const rawArgs = process.argv.slice(2);
const heapArg = rawArgs.find((arg) => arg.startsWith("--heap="));
const heapMb = heapArg?.slice("--heap=".length) || "6144";
const vitestArgs = rawArgs.filter((arg) => !arg.startsWith("--heap="));
if (vitestArgs.length === 0) {
console.error("Usage: node scripts/run-vitest-with-heap.mjs [--heap=6144] <vitest args...>");
process.exit(1);
}
const nodeOptions = [`--max-old-space-size=${heapMb}`, process.env.NODE_OPTIONS || ""]
.join(" ")
.trim();
const timeoutMs = Number.parseInt(process.env.FUSION_RUN_VITEST_TIMEOUT_MS || "900000", 10);
const forceKillGraceMs = Number.parseInt(process.env.FUSION_RUN_VITEST_KILL_GRACE_MS || "5000", 10);
function resolveSpawnCommand() {
const override = process.env.FUSION_RUN_VITEST_SPAWN_OVERRIDE;
if (!override) {
return { command: "pnpm", args: ["exec", "vitest", ...vitestArgs] };
}
const parsedOverride = JSON.parse(override);
if (
!parsedOverride ||
typeof parsedOverride.command !== "string" ||
parsedOverride.command.length === 0 ||
!Array.isArray(parsedOverride.args) ||
parsedOverride.args.some((arg) => typeof arg !== "string")
) {
throw new Error(
"FUSION_RUN_VITEST_SPAWN_OVERRIDE must be valid JSON with string command and string[] args",
);
}
// Test seam for process-lifecycle coverage without launching real vitest.
return { command: parsedOverride.command, args: parsedOverride.args };
}
const { command, args } = resolveSpawnCommand();
// process-supervisor-allowlist: foreground wrapper signals the entire vitest process group on death/timeout; not a background daemon
const child = spawn(command, args, {
detached: true,
stdio: "inherit",
env: { ...process.env, NODE_OPTIONS: nodeOptions },
});
const heartbeat = setInterval(() => {
console.log(`[dashboard-vitest] still running: ${vitestArgs.join(" ")}`);
}, 5_000);
let timeoutExitCode = null;
let forceKillTimer = null;
const timeout = Number.isFinite(timeoutMs) && timeoutMs > 0
? setTimeout(() => {
timeoutExitCode = 124;
console.error(`[dashboard-vitest] timeout after ${timeoutMs}ms: ${vitestArgs.join(" ")}`);
forwardSignal("SIGTERM");
forceKillTimer = setTimeout(() => {
forwardSignal("SIGKILL");
}, Math.max(1, forceKillGraceMs));
forceKillTimer.unref();
}, timeoutMs)
: null;
timeout?.unref();
function clearHeartbeat() {
clearInterval(heartbeat);
}
function clearTimers() {
clearHeartbeat();
if (timeout) clearTimeout(timeout);
if (forceKillTimer) clearTimeout(forceKillTimer);
}
function forwardSignal(signal) {
clearHeartbeat();
try {
process.kill(-child.pid, signal);
return;
} catch (error) {
if (!(error instanceof Error) || !("code" in error)) {
throw error;
}
if (error.code !== "ESRCH" && error.code !== "EPERM") {
throw error;
}
}
try {
child.kill(signal);
} catch (error) {
if (!(error instanceof Error) || !("code" in error) || error.code !== "ESRCH") {
throw error;
}
}
}
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
process.on(signal, () => forwardSignal(signal));
}
process.on("exit", () => {
clearTimers();
try {
process.kill(-child.pid, "SIGTERM");
} catch (error) {
if (
!(error instanceof Error) ||
!("code" in error) ||
(error.code !== "ESRCH" && error.code !== "EPERM")
) {
throw error;
}
}
});
child.on("error", (error) => {
clearTimers();
console.error(error);
process.exit(1);
});
child.on("close", (code, signal) => {
clearTimers();
if (timeoutExitCode !== null) {
process.exit(timeoutExitCode);
}
if (signal) {
process.kill(process.pid, signal);
return;
}
process.exit(code ?? 1);
});