Skip to content

Commit 0657492

Browse files
committed
fix(sdk): taskkill package e2e trees on windows
1 parent d46b64d commit 0657492

1 file changed

Lines changed: 63 additions & 4 deletions

File tree

packages/sdk/src/package.e2e.test.ts

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// OpenClaw SDK tests cover package behavior.
2-
import { spawn, type SpawnOptionsWithoutStdio } from "node:child_process";
2+
import { spawn, spawnSync, type SpawnOptionsWithoutStdio } from "node:child_process";
33
import { createReadStream } from "node:fs";
44
import fs from "node:fs/promises";
55
import { createServer, type Server } from "node:http";
66
import os from "node:os";
77
import path from "node:path";
8-
import { afterEach, describe, expect, it } from "vitest";
8+
import { afterEach, describe, expect, it, vi } from "vitest";
99
import { createPnpmRunnerSpawnSpec } from "../../../scripts/pnpm-runner.mjs";
1010
import { createNodeEvalArgs } from "../../../src/test-utils/node-process.js";
1111

@@ -89,8 +89,35 @@ function runCommand(
8989
});
9090
}
9191

92-
function signalCommandProcess(child: ReturnType<typeof spawn>, signal: NodeJS.Signals): void {
93-
if (process.platform !== "win32" && typeof child.pid === "number") {
92+
function signalCommandProcess(
93+
child: ReturnType<typeof spawn>,
94+
signal: NodeJS.Signals,
95+
runTaskkill: typeof spawnSync = spawnSync,
96+
): void {
97+
if (process.platform === "win32") {
98+
if (typeof child.pid === "number") {
99+
const args = ["/PID", String(child.pid), "/T"];
100+
if (signal === "SIGKILL") {
101+
args.push("/F");
102+
}
103+
const result = runTaskkill("taskkill", args, { stdio: "ignore", windowsHide: true });
104+
if (!result.error && result.status === 0) {
105+
return;
106+
}
107+
if (signal !== "SIGKILL") {
108+
const forceResult = runTaskkill("taskkill", [...args, "/F"], {
109+
stdio: "ignore",
110+
windowsHide: true,
111+
});
112+
if (!forceResult.error && forceResult.status === 0) {
113+
return;
114+
}
115+
}
116+
}
117+
child.kill(signal);
118+
return;
119+
}
120+
if (typeof child.pid === "number") {
94121
try {
95122
process.kill(-child.pid, signal);
96123
return;
@@ -260,6 +287,38 @@ describe("OpenClaw SDK package e2e", () => {
260287
);
261288
});
262289

290+
it("force-kills Windows package command process trees when graceful taskkill fails", () => {
291+
const platformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
292+
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
293+
try {
294+
const killMock = vi.fn();
295+
const child = {
296+
pid: 12345,
297+
kill: killMock,
298+
} as unknown as ReturnType<typeof spawn>;
299+
const runTaskkill = vi
300+
.fn()
301+
.mockReturnValueOnce({ status: 1 })
302+
.mockReturnValueOnce({ status: 0 });
303+
304+
signalCommandProcess(child, "SIGTERM", runTaskkill);
305+
306+
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
307+
stdio: "ignore",
308+
windowsHide: true,
309+
});
310+
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
311+
stdio: "ignore",
312+
windowsHide: true,
313+
});
314+
expect(killMock).not.toHaveBeenCalled();
315+
} finally {
316+
if (platformDescriptor) {
317+
Object.defineProperty(process, "platform", platformDescriptor);
318+
}
319+
}
320+
});
321+
263322
it("packs and imports from an external temp consumer", async () => {
264323
const repoRoot = process.cwd();
265324
const packageRoots = [

0 commit comments

Comments
 (0)