mirror of https://github.com/openclaw/openclaw.git
fix(gateway): restart watch after child sigterm
This commit is contained in:
parent
1a65c3b06d
commit
cb76e5c899
|
|
@ -8,6 +8,8 @@ import { isRestartRelevantRunNodePath, runNodeWatchedPaths } from "./run-node.mj
|
|||
|
||||
const WATCH_NODE_RUNNER = "scripts/run-node.mjs";
|
||||
const WATCH_RESTART_SIGNAL = "SIGTERM";
|
||||
const WATCH_RESTARTABLE_CHILD_EXIT_CODES = new Set([143]);
|
||||
const WATCH_RESTARTABLE_CHILD_SIGNALS = new Set(["SIGTERM"]);
|
||||
|
||||
const buildRunnerArgs = (args) => [WATCH_NODE_RUNNER, ...args];
|
||||
|
||||
|
|
@ -27,6 +29,10 @@ const resolveRepoPath = (filePath, cwd) => {
|
|||
const isIgnoredWatchPath = (filePath, cwd) =>
|
||||
!isRestartRelevantRunNodePath(resolveRepoPath(filePath, cwd));
|
||||
|
||||
const shouldRestartAfterChildExit = (exitCode, exitSignal) =>
|
||||
(typeof exitCode === "number" && WATCH_RESTARTABLE_CHILD_EXIT_CODES.has(exitCode)) ||
|
||||
(typeof exitSignal === "string" && WATCH_RESTARTABLE_CHILD_SIGNALS.has(exitSignal));
|
||||
|
||||
export async function runWatchMain(params = {}) {
|
||||
const deps = {
|
||||
spawn: params.spawn ?? spawn,
|
||||
|
|
@ -90,7 +96,7 @@ export async function runWatchMain(params = {}) {
|
|||
if (shuttingDown) {
|
||||
return;
|
||||
}
|
||||
if (restartRequested) {
|
||||
if (restartRequested || shouldRestartAfterChildExit(exitCode, exitSignal)) {
|
||||
restartRequested = false;
|
||||
startRunner();
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -147,6 +147,38 @@ describe("watch-node script", () => {
|
|||
expect(fakeProcess.listenerCount("SIGTERM")).toBe(0);
|
||||
});
|
||||
|
||||
it("restarts when the runner exits with a SIGTERM-derived code unexpectedly", async () => {
|
||||
const childA = Object.assign(new EventEmitter(), {
|
||||
kill: vi.fn(),
|
||||
});
|
||||
const childB = Object.assign(new EventEmitter(), {
|
||||
kill: vi.fn(() => {}),
|
||||
});
|
||||
const spawn = vi.fn().mockReturnValueOnce(childA).mockReturnValueOnce(childB);
|
||||
const watcher = Object.assign(new EventEmitter(), {
|
||||
close: vi.fn(async () => {}),
|
||||
});
|
||||
const createWatcher = vi.fn(() => watcher);
|
||||
const fakeProcess = createFakeProcess();
|
||||
|
||||
const runPromise = runWatchMain({
|
||||
args: ["gateway", "--force"],
|
||||
createWatcher,
|
||||
process: fakeProcess,
|
||||
spawn,
|
||||
});
|
||||
|
||||
childA.emit("exit", 143, null);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
expect(spawn).toHaveBeenCalledTimes(2);
|
||||
|
||||
fakeProcess.emit("SIGINT");
|
||||
const exitCode = await runPromise;
|
||||
expect(exitCode).toBe(130);
|
||||
expect(childB.kill).toHaveBeenCalledWith("SIGTERM");
|
||||
expect(watcher.close).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("forces no-respawn for watch children even when supervisor hints are inherited", async () => {
|
||||
const { child, spawn, watcher, createWatcher, fakeProcess } = createWatchHarness();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue