fix(update): prevent stale post-core state reuse (#122309)

* fix(update): isolate post-core handoff env

Co-authored-by: Rohit <rohitjavvadi2@gmail.com>

Punchcard-Session: clear-river-orchard-dd

* chore: leave changelog release-owned
This commit is contained in:
Vincent Koc
2026-08-12 08:10:17 +08:00
committed by GitHub
parent 72d55fc12a
commit ea5f397d37
7 changed files with 205 additions and 22 deletions
+8 -11
View File
@@ -40,6 +40,7 @@ import {
type ControlPlaneUpdateSentinelMetaFile,
} from "../../infra/update-control-plane-sentinel.js";
import {
buildPostCoreHandoffEnv,
POST_CORE_UPDATE_ENV,
POST_CORE_UPDATE_SOURCE_CONFIG_PATH_ENV,
type PreUpdateConfigRestoreInput,
@@ -91,7 +92,6 @@ import {
const DEFAULT_UPDATE_STEP_TIMEOUT_MS = 30 * 60_000;
export { POST_CORE_UPDATE_ENV };
export const POST_CORE_UPDATE_CHANNEL_ENV = "OPENCLAW_UPDATE_POST_CORE_CHANNEL";
export const POST_CORE_UPDATE_REQUESTED_CHANNEL_ENV = "OPENCLAW_UPDATE_POST_CORE_REQUESTED_CHANNEL";
export const POST_CORE_UPDATE_RESULT_PATH_ENV = "OPENCLAW_UPDATE_POST_CORE_RESULT_PATH";
export const POST_CORE_UPDATE_INSTALL_RECORDS_PATH_ENV =
"OPENCLAW_UPDATE_POST_CORE_INSTALL_RECORDS_PATH";
@@ -560,25 +560,22 @@ export async function continuePostCoreUpdateInFreshProcess(params: {
await writePostCoreSourceConfigFile(sourceConfigPath, params.preUpdateConfig);
const jsonMode = params.opts.json === true;
const childStdio = resolvePostCoreUpdateChildStdio(process.platform, jsonMode);
const handoffEnv = buildPostCoreHandoffEnv({
baseEnv: stripGatewayServiceMarkerEnv(disableUpdatedPackageCompileCacheEnv(process.env)),
compatHostVersion: postCoreHostVersion,
requestedChannel: params.requestedChannel,
sourceConfigPath: params.preUpdateConfig ? sourceConfigPath : undefined,
});
const child = spawn(params.nodeRunner ?? resolveNodeRunner(), argv, {
stdio: childStdio,
env: {
...stripGatewayServiceMarkerEnv(disableUpdatedPackageCompileCacheEnv(process.env)),
...handoffEnv,
OPENCLAW_UPDATE_IN_PROGRESS: "1",
[POST_CORE_UPDATE_ENV]: "1",
[POST_CORE_UPDATE_CHANNEL_ENV]: params.channel,
...(params.requestedChannel
? { [POST_CORE_UPDATE_REQUESTED_CHANNEL_ENV]: params.requestedChannel }
: {}),
[POST_CORE_UPDATE_RESULT_PATH_ENV]: resultPath,
[POST_CORE_UPDATE_INSTALL_RECORDS_PATH_ENV]: installRecordsPath,
[POST_CORE_UPDATE_STARTED_AT_ENV]: String(params.updateStartedAtMs),
...(postCoreHostVersion === null
? {}
: { OPENCLAW_COMPATIBILITY_HOST_VERSION: postCoreHostVersion }),
...(params.preUpdateConfig
? { [POST_CORE_UPDATE_SOURCE_CONFIG_PATH_ENV]: sourceConfigPath }
: {}),
},
});
// JSON callers own stdout, so child diagnostics must remain off that protocol stream.
+4 -2
View File
@@ -1,6 +1,9 @@
import { readConfigFileSnapshot } from "../../config/config.js";
import { normalizeUpdateChannel } from "../../infra/update-channels.js";
import { POST_CORE_UPDATE_SOURCE_CONFIG_PATH_ENV } from "../../infra/update-post-core-context.js";
import {
POST_CORE_UPDATE_REQUESTED_CHANNEL_ENV,
POST_CORE_UPDATE_SOURCE_CONFIG_PATH_ENV,
} from "../../infra/update-post-core-context.js";
import type { UpdateRunResult } from "../../infra/update-runner.js";
import { loadInstalledPluginIndexInstallRecords } from "../../plugins/installed-plugin-index-records.js";
import { readPersistedInstalledPluginIndex } from "../../plugins/installed-plugin-index-store.js";
@@ -21,7 +24,6 @@ import {
import { updatePluginsAfterCoreUpdate } from "./update-command-plugins.js";
import {
POST_CORE_UPDATE_INSTALL_RECORDS_PATH_ENV,
POST_CORE_UPDATE_REQUESTED_CHANNEL_ENV,
POST_CORE_UPDATE_RESULT_PATH_ENV,
POST_CORE_UPDATE_STARTED_AT_ENV,
readPostCorePluginInstallRecordsFile,