fix(cli): honor update restart overrides

This commit is contained in:
Sebastian
2026-02-17 08:46:59 -05:00
parent dff8692613
commit 366da7569a
4 changed files with 93 additions and 5 deletions

View File

@@ -41,6 +41,22 @@ describe("restart-helper", () => {
}
});
it("uses OPENCLAW_SYSTEMD_UNIT override for systemd scripts", async () => {
Object.defineProperty(process, "platform", { value: "linux" });
const scriptPath = await prepareRestartScript({
OPENCLAW_PROFILE: "default",
OPENCLAW_SYSTEMD_UNIT: "custom-gateway",
});
expect(scriptPath).toBeTruthy();
const content = await fs.readFile(scriptPath!, "utf-8");
expect(content).toContain("systemctl --user restart 'custom-gateway.service'");
if (scriptPath) {
await fs.unlink(scriptPath);
}
});
it("creates a launchd restart script on macOS", async () => {
Object.defineProperty(process, "platform", { value: "darwin" });
process.getuid = () => 501;
@@ -62,6 +78,24 @@ describe("restart-helper", () => {
}
});
it("uses OPENCLAW_LAUNCHD_LABEL override on macOS", async () => {
Object.defineProperty(process, "platform", { value: "darwin" });
process.getuid = () => 501;
const scriptPath = await prepareRestartScript({
OPENCLAW_PROFILE: "default",
OPENCLAW_LAUNCHD_LABEL: "com.custom.openclaw",
});
expect(scriptPath).toBeTruthy();
const content = await fs.readFile(scriptPath!, "utf-8");
expect(content).toContain("launchctl kickstart -k 'gui/501/com.custom.openclaw'");
if (scriptPath) {
await fs.unlink(scriptPath);
}
});
it("creates a schtasks restart script on Windows", async () => {
Object.defineProperty(process, "platform", { value: "win32" });
@@ -84,6 +118,24 @@ describe("restart-helper", () => {
}
});
it("uses OPENCLAW_WINDOWS_TASK_NAME override on Windows", async () => {
Object.defineProperty(process, "platform", { value: "win32" });
const scriptPath = await prepareRestartScript({
OPENCLAW_PROFILE: "default",
OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Gateway (custom)",
});
expect(scriptPath).toBeTruthy();
const content = await fs.readFile(scriptPath!, "utf-8");
expect(content).toContain('schtasks /End /TN "OpenClaw Gateway (custom)"');
expect(content).toContain('schtasks /Run /TN "OpenClaw Gateway (custom)"');
if (scriptPath) {
await fs.unlink(scriptPath);
}
});
it("uses custom profile in service names", async () => {
Object.defineProperty(process, "platform", { value: "linux" });
const scriptPath = await prepareRestartScript({

View File

@@ -23,6 +23,30 @@ function isBatchSafe(value: string): boolean {
return /^[A-Za-z0-9 _\-().]+$/.test(value);
}
function resolveSystemdUnit(env: NodeJS.ProcessEnv): string {
const override = env.OPENCLAW_SYSTEMD_UNIT?.trim();
if (override) {
return override.endsWith(".service") ? override : `${override}.service`;
}
return `${resolveGatewaySystemdServiceName(env.OPENCLAW_PROFILE)}.service`;
}
function resolveLaunchdLabel(env: NodeJS.ProcessEnv): string {
const override = env.OPENCLAW_LAUNCHD_LABEL?.trim();
if (override) {
return override;
}
return resolveGatewayLaunchAgentLabel(env.OPENCLAW_PROFILE);
}
function resolveWindowsTaskName(env: NodeJS.ProcessEnv): string {
const override = env.OPENCLAW_WINDOWS_TASK_NAME?.trim();
if (override) {
return override;
}
return resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE);
}
/**
* Prepares a standalone script to restart the gateway service.
* This script is written to a temporary directory and does not depend on
@@ -41,8 +65,8 @@ export async function prepareRestartScript(
try {
if (platform === "linux") {
const serviceName = resolveGatewaySystemdServiceName(env.OPENCLAW_PROFILE);
const escaped = shellEscape(`${serviceName}.service`);
const unitName = resolveSystemdUnit(env);
const escaped = shellEscape(unitName);
filename = `openclaw-restart-${timestamp}.sh`;
scriptContent = `#!/bin/sh
# Standalone restart script — survives parent process termination.
@@ -53,7 +77,7 @@ systemctl --user restart '${escaped}'
rm -f "$0"
`;
} else if (platform === "darwin") {
const label = resolveGatewayLaunchAgentLabel(env.OPENCLAW_PROFILE);
const label = resolveLaunchdLabel(env);
const escaped = shellEscape(label);
// Fallback to 501 if getuid is not available (though it should be on macOS)
const uid = process.getuid ? process.getuid() : 501;
@@ -67,7 +91,7 @@ launchctl kickstart -k 'gui/${uid}/${escaped}'
rm -f "$0"
`;
} else if (platform === "win32") {
const taskName = resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE);
const taskName = resolveWindowsTaskName(env);
if (!isBatchSafe(taskName)) {
return null;
}

View File

@@ -400,9 +400,10 @@ async function maybeRestartService(params: {
try {
let restarted = false;
let restartInitiated = false;
if (params.restartScriptPath) {
await runRestartScript(params.restartScriptPath);
restarted = true;
restartInitiated = true;
} else {
restarted = await runDaemonRestart();
}
@@ -423,6 +424,16 @@ async function maybeRestartService(params: {
delete process.env.OPENCLAW_UPDATE_IN_PROGRESS;
}
}
if (!params.opts.json && restartInitiated) {
defaultRuntime.log(theme.success("Daemon restart initiated."));
defaultRuntime.log(
theme.muted(
`Verify with \`${replaceCliName(formatCliCommand("openclaw gateway status"), CLI_NAME)}\` once the gateway is back.`,
),
);
defaultRuntime.log("");
}
} catch (err) {
if (!params.opts.json) {
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));