diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index 55d1f58f2f8d..9d38f93a2bd4 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -2877,6 +2877,34 @@ describe("update-cli", () => { expect(updateCall?.beforeGitMutation).toEqual(expect.any(Function)); }); + it("stops a running managed git gateway when wrapper commands hide the service root", async () => { + const wrapperPath = path.join( + createCaseDir("openclaw-update-wrapper-service"), + "gateway-wrapper", + ); + serviceReadCommand.mockResolvedValue({ + programArguments: [wrapperPath, "gateway", "run"], + environment: { + OPENCLAW_SERVICE_MARKER: "openclaw", + OPENCLAW_SERVICE_KIND: "gateway", + }, + }); + serviceLoaded.mockResolvedValue(true); + serviceReadRuntime.mockResolvedValue({ + status: "running", + pid: 4242, + state: "running", + }); + mockGitUpdateAfterMutation(); + + await updateCommand({ yes: true }); + + expect(serviceStop).toHaveBeenCalledTimes(1); + expect(runGatewayUpdate).toHaveBeenCalledTimes(1); + expect(prepareRestartScript).toHaveBeenCalled(); + expect(runDaemonRestart).not.toHaveBeenCalled(); + }); + it("fails managed git restart when the gateway responds but the service stays stopped", async () => { const serviceEntrypoint = path.join(process.cwd(), "dist", "index.js"); serviceReadCommand.mockResolvedValue({ diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index 04dbd7580ee1..6baa090db823 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -926,7 +926,8 @@ async function maybeStopManagedServiceBeforeMutableUpdate(params: { if ( params.updateInstallKind === "git" && - !(await gatewayServiceCommandUsesRoot({ root: params.root, command: serviceState.command })) + (await gatewayServiceCommandUsesRoot({ root: params.root, command: serviceState.command })) === + false ) { if (!params.jsonMode) { defaultRuntime.log( @@ -1459,10 +1460,10 @@ async function gatewayServiceCommandUsesRoot(params: { root: string | undefined; env?: NodeJS.ProcessEnv; command?: GatewayServiceCommandConfig | null; -}): Promise { +}): Promise { const expectedRoot = normalizeOptionalString(params.root); if (!expectedRoot) { - return false; + return null; } const command = params.command === undefined @@ -1473,7 +1474,7 @@ async function gatewayServiceCommandUsesRoot(params: { const layout = await summarizeGatewayServiceLayout(command); const serviceRoot = layout?.packageRoot; if (!serviceRoot) { - return false; + return null; } const [expectedRootReal, serviceRootReal] = await Promise.all([ tryRealpathOrResolve(expectedRoot), @@ -2242,10 +2243,10 @@ async function maybeRestartService(params: { }); if ( updatedInstallRestartNeedsServiceRootProof && - !(await gatewayServiceCommandUsesRoot({ + (await gatewayServiceCommandUsesRoot({ root: params.result.root, env: params.serviceEnv, - })) + })) !== true ) { if (!params.opts.json) { defaultRuntime.log( @@ -3820,7 +3821,7 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise serviceState.installed && serviceState.loaded && preManagedServiceStop?.stopped !== true && - serviceMatchesUpdateRoot !== true; + serviceMatchesUpdateRoot === false; if ( shouldPrepareUpdatedInstallRestart({ updateMode: resultWithPostUpdate.mode,