fix(daemon): address clanker review findings for kickstart restart
Bug 1 (high): replace fixed sleep 1 with caller-PID polling in both
kickstart and start-after-exit handoff modes. The helper now waits until
kill -0 $caller_pid fails before issuing launchctl kickstart -k.
Bug 2 (medium): gate enable+bootstrap fallback on isLaunchctlNotLoaded().
Only attempt re-registration when kickstart -k fails because the job is
absent; all other kickstart failures now re-throw the original error.
Follows up on 3c0fd3dffe.
Fixes #43311, #43406, #43035, #43049
This commit is contained in:
@@ -65,7 +65,7 @@ describe("runServiceRestart config pre-flight (#35862)", () => {
|
||||
service.restart.mockClear();
|
||||
service.isLoaded.mockResolvedValue(true);
|
||||
service.readCommand.mockResolvedValue({ environment: {} });
|
||||
service.restart.mockResolvedValue(undefined);
|
||||
service.restart.mockResolvedValue({ outcome: "completed" });
|
||||
vi.unstubAllEnvs();
|
||||
vi.stubEnv("OPENCLAW_GATEWAY_TOKEN", "");
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_TOKEN", "");
|
||||
@@ -163,7 +163,7 @@ describe("runServiceStart config pre-flight (#35862)", () => {
|
||||
service.isLoaded.mockClear();
|
||||
service.restart.mockClear();
|
||||
service.isLoaded.mockResolvedValue(true);
|
||||
service.restart.mockResolvedValue(undefined);
|
||||
service.restart.mockResolvedValue({ outcome: "completed" });
|
||||
});
|
||||
|
||||
it("aborts start when config is invalid", async () => {
|
||||
|
||||
@@ -64,7 +64,7 @@ describe("runServiceRestart token drift", () => {
|
||||
service.readCommand.mockResolvedValue({
|
||||
environment: { OPENCLAW_GATEWAY_TOKEN: "service-token" },
|
||||
});
|
||||
service.restart.mockResolvedValue(undefined);
|
||||
service.restart.mockResolvedValue({ outcome: "completed" });
|
||||
vi.unstubAllEnvs();
|
||||
vi.stubEnv("OPENCLAW_GATEWAY_TOKEN", "");
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_TOKEN", "");
|
||||
@@ -176,4 +176,24 @@ describe("runServiceRestart token drift", () => {
|
||||
expect(payload.result).toBe("restarted");
|
||||
expect(payload.message).toContain("unmanaged process");
|
||||
});
|
||||
|
||||
it("skips restart health checks when restart is only scheduled", async () => {
|
||||
const postRestartCheck = vi.fn(async () => {});
|
||||
service.restart.mockResolvedValue({ outcome: "scheduled" });
|
||||
|
||||
const result = await runServiceRestart({
|
||||
serviceNoun: "Gateway",
|
||||
service,
|
||||
renderStartHints: () => [],
|
||||
opts: { json: true },
|
||||
postRestartCheck,
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(postRestartCheck).not.toHaveBeenCalled();
|
||||
const jsonLine = runtimeLogs.find((line) => line.trim().startsWith("{"));
|
||||
const payload = JSON.parse(jsonLine ?? "{}") as { result?: string; message?: string };
|
||||
expect(payload.result).toBe("scheduled");
|
||||
expect(payload.message).toBe("restart scheduled, gateway will restart momentarily");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import { readBestEffortConfig, readConfigFileSnapshot } from "../../config/confi
|
||||
import { formatConfigIssueLines } from "../../config/issue-format.js";
|
||||
import { resolveIsNixMode } from "../../config/paths.js";
|
||||
import { checkTokenDrift } from "../../daemon/service-audit.js";
|
||||
import type { GatewayServiceRestartResult } from "../../daemon/service-types.js";
|
||||
import type { GatewayService } from "../../daemon/service.js";
|
||||
import { renderSystemdUnavailableHints } from "../../daemon/systemd-hints.js";
|
||||
import { isSystemdUserServiceAvailable } from "../../daemon/systemd.js";
|
||||
@@ -402,8 +403,23 @@ export async function runServiceRestart(params: {
|
||||
}
|
||||
|
||||
try {
|
||||
let restartResult: GatewayServiceRestartResult = { outcome: "completed" };
|
||||
if (loaded) {
|
||||
await params.service.restart({ env: process.env, stdout });
|
||||
restartResult = await params.service.restart({ env: process.env, stdout });
|
||||
}
|
||||
if (restartResult.outcome === "scheduled") {
|
||||
const message = `restart scheduled, ${params.serviceNoun.toLowerCase()} will restart momentarily`;
|
||||
emit({
|
||||
ok: true,
|
||||
result: "scheduled",
|
||||
message,
|
||||
service: buildDaemonServiceSnapshot(params.service, loaded),
|
||||
warnings: warnings.length ? warnings : undefined,
|
||||
});
|
||||
if (!json) {
|
||||
defaultRuntime.log(message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (params.postRestartCheck) {
|
||||
await params.postRestartCheck({ json, stdout, warnings, fail });
|
||||
|
||||
Reference in New Issue
Block a user