test(release): recover known Windows packaged upgrade timeout

(cherry picked from commit 8f7399e9e9)
This commit is contained in:
Peter Steinberger
2026-05-04 14:51:14 +01:00
parent 3921e1b0b7
commit 2e399e6f1a
2 changed files with 60 additions and 8 deletions

View File

@@ -766,15 +766,35 @@ async function runUpgradeLane(params) {
logLanePhase(lane, "update");
const updateEnv = buildRealUpdateEnv(env);
const updateArgs = buildPackagedUpgradeUpdateArgs(params.candidateUrl);
const updateResult = await runOpenClaw({
lane,
env: updateEnv,
args: updateArgs,
logPath: join(params.logsDir, "upgrade-update.log"),
timeoutMs: updateTimeoutMs(),
check: false,
});
const updateLogPath = join(params.logsDir, "upgrade-update.log");
let updateResult;
let usedWindowsPackagedUpgradeTimeoutFallback = false;
try {
updateResult = await runOpenClaw({
lane,
env: updateEnv,
args: updateArgs,
logPath: updateLogPath,
timeoutMs: updateTimeoutMs(),
check: false,
});
} catch (error) {
if (!isRecoverableWindowsPackagedUpgradeTimeoutError(error, process.platform)) {
throw error;
}
usedWindowsPackagedUpgradeTimeoutFallback = true;
appendFileSync(
updateLogPath,
`\n[release-checks] Windows baseline updater timed out after fetching candidate; falling back to direct candidate install: ${formatError(error)}\n`,
);
updateResult = {
exitCode: 124,
stdout: "",
stderr: formatError(error),
};
}
const usedWindowsPackagedUpgradeFallback =
usedWindowsPackagedUpgradeTimeoutFallback ||
isRecoverableWindowsPackagedUpgradeSwapCleanupFailure(updateResult, process.platform);
if (usedWindowsPackagedUpgradeFallback) {
logLanePhase(lane, "update-fallback-install");
@@ -1367,6 +1387,22 @@ export function isRecoverableWindowsPackagedUpgradeSwapCleanupFailure(
);
}
export function isRecoverableWindowsPackagedUpgradeTimeoutError(
error,
platform = process.platform,
) {
if (platform !== "win32") {
return false;
}
const message = error instanceof Error ? error.message : String(error);
return (
/\bCommand timed out:/u.test(message) &&
/[/\\]openclaw\.mjs update --tag http:\/\/127\.0\.0\.1:\d+\/openclaw-current\.tgz --yes --json --timeout \d+/u.test(
message,
)
);
}
export function shouldRunPackagedUpgradeStatusProbe({
platform = process.platform,
usedWindowsPackagedUpgradeFallback,

View File

@@ -38,6 +38,7 @@ import {
CROSS_OS_AGENT_TURN_TIMEOUT_SECONDS,
isImmutableReleaseRef,
isRecoverableWindowsPackagedUpgradeSwapCleanupFailure,
isRecoverableWindowsPackagedUpgradeTimeoutError,
looksLikeReleaseVersionRef,
normalizeRequestedRef,
normalizeWindowsCommandShimPath,
@@ -744,6 +745,21 @@ describe("scripts/openclaw-cross-os-release-checks", () => {
).toBe(true);
});
it("recognizes the shipped Windows updater packaged-upgrade timeout", () => {
const error = new Error(
"Command timed out: C:\\hostedtoolcache\\windows\\node\\24.15.0\\x64\\node.exe C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\openclaw-upgrade-q9DsA7\\prefix\\node_modules\\openclaw\\openclaw.mjs update --tag http://127.0.0.1:49951/openclaw-current.tgz --yes --json --timeout 1500",
);
expect(isRecoverableWindowsPackagedUpgradeTimeoutError(error, "win32")).toBe(true);
expect(isRecoverableWindowsPackagedUpgradeTimeoutError(error, "linux")).toBe(false);
expect(
isRecoverableWindowsPackagedUpgradeTimeoutError(
new Error("Command timed out: node openclaw.mjs update --tag openclaw@beta"),
"win32",
),
).toBe(false);
});
it("skips the packaged upgrade status probe after the Windows fallback install", () => {
expect(
shouldRunPackagedUpgradeStatusProbe({