fix(update): preserve package service state during cutover (#83026)

* fix(update): preserve package service state during cutover

* docs(changelog): mention package service state cutover fix
This commit is contained in:
Vincent Koc
2026-05-22 21:02:58 +08:00
committed by GitHub
parent 07e61fc847
commit a15797ad11
6 changed files with 239 additions and 11 deletions

View File

@@ -14,6 +14,7 @@ import {
recoverInstalledLaunchAgentAfterUpdate,
recoverLaunchAgentAndRecheckGatewayHealth,
resolvePostCoreUpdateChildStdio,
resolvePostUpdateServiceStateReadEnv,
resolvePostInstallDoctorEnv,
shouldPrepareUpdatedInstallRestart,
resolveUpdatedGatewayRestartPort,
@@ -117,6 +118,40 @@ describe("resolveUpdatedGatewayRestartPort", () => {
});
});
describe("resolvePostUpdateServiceStateReadEnv", () => {
it("keeps package restart preparation anchored to the pre-update service env", () => {
const processEnv = {
OPENCLAW_STATE_DIR: "/source/state",
OPENCLAW_CONFIG_PATH: "/source/openclaw.json",
} as NodeJS.ProcessEnv;
const prePackageServiceEnv = {
OPENCLAW_STATE_DIR: "/managed/state",
OPENCLAW_CONFIG_PATH: "/managed/openclaw.json",
} as NodeJS.ProcessEnv;
expect(
resolvePostUpdateServiceStateReadEnv({
updateMode: "npm",
processEnv,
prePackageServiceEnv,
}),
).toBe(prePackageServiceEnv);
});
it("keeps git updates tied to the caller environment", () => {
const processEnv = { OPENCLAW_STATE_DIR: "/source/state" } as NodeJS.ProcessEnv;
const prePackageServiceEnv = { OPENCLAW_STATE_DIR: "/managed/state" } as NodeJS.ProcessEnv;
expect(
resolvePostUpdateServiceStateReadEnv({
updateMode: "git",
processEnv,
prePackageServiceEnv,
}),
).toBe(processEnv);
});
});
describe("resolvePostInstallDoctorEnv", () => {
it("uses the managed service profile paths for post-install doctor", () => {
const env = resolvePostInstallDoctorEnv({

View File

@@ -1086,6 +1086,17 @@ export function resolveUpdatedGatewayRestartPort(params: {
return resolveGatewayPort(params.config, params.serviceEnv ?? params.processEnv ?? process.env);
}
export function resolvePostUpdateServiceStateReadEnv(params: {
updateMode: UpdateRunResult["mode"];
processEnv?: NodeJS.ProcessEnv;
prePackageServiceEnv?: NodeJS.ProcessEnv;
}): NodeJS.ProcessEnv {
if (isPackageManagerUpdateMode(params.updateMode) && params.prePackageServiceEnv) {
return params.prePackageServiceEnv;
}
return params.processEnv ?? process.env;
}
type UpdateDryRunPreview = {
dryRun: true;
root: string;
@@ -3502,7 +3513,11 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
if (shouldRestart) {
try {
const serviceState = await readGatewayServiceState(resolveGatewayService(), {
env: process.env,
env: resolvePostUpdateServiceStateReadEnv({
updateMode: resultWithPostUpdate.mode,
processEnv: process.env,
prePackageServiceEnv: prePackageServiceStop?.serviceEnv,
}),
});
if (
shouldPrepareUpdatedInstallRestart({