fix(ci): clean up Windows packaged smoke process trees (#10453)

This commit is contained in:
backryun
2026-08-16 01:51:11 +09:00
committed by GitHub
parent 370c1b9ae7
commit 4adf50dbcb
2 changed files with 46 additions and 9 deletions

View File

@@ -255,20 +255,33 @@ async function signalProcessTree(child, signal) {
}
}
async function stopApp(child) {
export async function stopApp(
child,
{
currentPlatform = platform(),
signalProcessTreeFn = signalProcessTree,
waitForProcessTreeExitFn = waitForProcessTreeExit,
} = {}
) {
if (!child.pid) return;
await signalProcessTree(child, "SIGTERM");
await waitForProcessTreeExit(child, 5_000);
// On Windows, terminating only the direct Electron process can orphan the
// packaged server when the parent exits before the follow-up liveness check.
// Kill the process tree in one operation while the root PID is still valid.
if (currentPlatform === "win32") {
await signalProcessTreeFn(child, "SIGKILL");
await waitForProcessTreeExitFn(child, 2_000);
return;
}
const isStillRunning =
platform() === "win32"
? child.exitCode === null && child.signalCode === null
: isProcessGroupAlive(child.pid);
await signalProcessTreeFn(child, "SIGTERM");
await waitForProcessTreeExitFn(child, 5_000);
const isStillRunning = isProcessGroupAlive(child.pid);
if (isStillRunning) {
await signalProcessTree(child, "SIGKILL");
await waitForProcessTreeExit(child, 2_000);
await signalProcessTreeFn(child, "SIGKILL");
await waitForProcessTreeExitFn(child, 2_000);
}
}

View File

@@ -5,6 +5,7 @@ import {
buildSmokeEnv,
FATAL_LOG_PATTERNS,
LINUX_EXECUTABLE_NAMES,
stopApp,
} from "../../scripts/dev/smoke-electron-packaged.mjs";
test("electron smoke discovers the default Linux executable name", () => {
@@ -47,3 +48,26 @@ test("electron smoke treats Electron process errors as fatal startup logs", () =
);
}
});
test("electron smoke force-terminates the Windows process tree before the parent can exit", async () => {
const signals: string[] = [];
const waits: number[] = [];
const child = {
pid: 4242,
exitCode: 0,
signalCode: null,
};
await stopApp(child, {
currentPlatform: "win32",
signalProcessTreeFn: async (_child, signal) => {
signals.push(signal);
},
waitForProcessTreeExitFn: async (_child, timeoutMs) => {
waits.push(timeoutMs);
},
});
assert.deepEqual(signals, ["SIGKILL"]);
assert.deepEqual(waits, [2_000]);
});