Skip to content

Commit 09ac03a

Browse files
committed
fix(cli): unify shutdown sequence, guard PID-file fallback against reuse
1 parent cb1cc61 commit 09ac03a

File tree

1 file changed

+16
-11
lines changed

1 file changed

+16
-11
lines changed

packages/cli-v3/src/dev/devSupervisor.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,7 @@ class DevSupervisor implements WorkerRuntime {
161161
#handleSigterm = async () => {
162162
logger.debug("[DevSupervisor] Received SIGTERM/SIGINT, stopping all run controllers");
163163

164-
// The detached watchdog handles cancelling runs on the platform.
165-
// Here we just stop local run controllers.
166-
const stopPromises = Array.from(this.runControllers.values()).map((controller) =>
167-
controller.stop()
168-
);
169-
170-
await Promise.allSettled(stopPromises);
164+
await this.shutdown();
171165

172166
// Must exit explicitly since registering a custom SIGINT handler
173167
// overrides Node's default process termination behavior.
@@ -178,6 +172,12 @@ class DevSupervisor implements WorkerRuntime {
178172
process.off("SIGTERM", this.#handleSigterm);
179173
process.off("SIGINT", this.#handleSigterm);
180174

175+
// Stop all local run controllers first so active-runs.json is up-to-date
176+
const stopPromises = Array.from(this.runControllers.values()).map((controller) =>
177+
controller.stop()
178+
);
179+
await Promise.allSettled(stopPromises);
180+
181181
// Kill watchdog on clean shutdown — no disconnect needed since runs are stopped locally
182182
this.#killWatchdog();
183183

@@ -247,23 +247,28 @@ class DevSupervisor implements WorkerRuntime {
247247
}
248248

249249
#killWatchdog() {
250-
if (this.watchdogProcess?.pid) {
250+
const knownPid = this.watchdogProcess?.pid;
251+
252+
if (knownPid) {
251253
try {
252-
process.kill(this.watchdogProcess.pid, "SIGTERM");
254+
process.kill(knownPid, "SIGTERM");
253255
} catch {
254256
// Already dead
255257
}
256258
this.watchdogProcess = undefined;
257259
}
258260

259-
// Also try via PID file in case the process reference is stale
261+
// Fallback: try via PID file, but only if the PID matches our spawned watchdog
262+
// to avoid killing an unrelated process that reused a stale PID
260263
if (this.watchdogPidPath) {
261264
try {
262265
const content = readFileSync(this.watchdogPidPath, "utf8");
263266
const prefix = "trigger-watchdog:";
264267
if (content.startsWith(prefix)) {
265268
const pid = parseInt(content.slice(prefix.length), 10);
266-
if (pid) process.kill(pid, "SIGTERM");
269+
if (pid && (!knownPid || pid === knownPid)) {
270+
process.kill(pid, "SIGTERM");
271+
}
267272
}
268273
} catch {
269274
// Already dead or no file

0 commit comments

Comments
 (0)