mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
fix: bound dev update cleanup
This commit is contained in:
@@ -1149,6 +1149,7 @@ run_dev_channel_update() {
|
||||
rm -rf $(shell_quote "$update_root")
|
||||
export PATH=$(shell_quote "$bootstrap_bin:$GUEST_EXEC_PATH")
|
||||
/usr/bin/env NODE_OPTIONS=--max-old-space-size=4096 \
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 \
|
||||
$GUEST_NODE_BIN $GUEST_OPENCLAW_ENTRY update --channel dev --yes --json
|
||||
EOF
|
||||
)" "$update_log" "$update_done" "$TIMEOUT_UPDATE_DEV_S" "$update_runner"
|
||||
|
||||
@@ -2398,8 +2398,9 @@ New-Item -ItemType Directory -Path $stateDir -Force | Out-Null
|
||||
Remove-Item (Join-Path $workspace 'BOOTSTRAP.md') -Force -ErrorAction SilentlyContinue
|
||||
EOF
|
||||
)"
|
||||
stop_gateway
|
||||
guest_run_openclaw "$API_KEY_ENV" "$API_KEY_VALUE" \
|
||||
agent --agent main --session-id parallels-windows-smoke --message "Reply with exact ASCII text OK only." --json
|
||||
agent --local --agent main --session-id parallels-windows-smoke --message "Reply with exact ASCII text OK only." --json
|
||||
}
|
||||
|
||||
capture_latest_ref_failure() {
|
||||
|
||||
@@ -710,6 +710,7 @@ describe("runGatewayUpdate", () => {
|
||||
it("does not fail a good windows dev preflight only because worktree cleanup hit long paths", async () => {
|
||||
await setupGitPackageManagerFixture();
|
||||
const calls: string[] = [];
|
||||
const cleanupTimeouts: Array<number | undefined> = [];
|
||||
const upstreamSha = "upstream123";
|
||||
const doctorNodePath = await resolveStableNodePath(process.execPath);
|
||||
const doctorCommand = `${doctorNodePath} ${path.join(tempDir, "openclaw.mjs")} doctor --non-interactive --fix`;
|
||||
@@ -718,7 +719,7 @@ describe("runGatewayUpdate", () => {
|
||||
try {
|
||||
const runCommand = async (
|
||||
argv: string[],
|
||||
_options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
|
||||
options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
|
||||
) => {
|
||||
const key = argv.join(" ");
|
||||
calls.push(key);
|
||||
@@ -772,6 +773,7 @@ describe("runGatewayUpdate", () => {
|
||||
key.startsWith(`git -C ${tempDir} worktree remove --force `) &&
|
||||
preflightPrefixPattern.test(key)
|
||||
) {
|
||||
cleanupTimeouts.push(options?.timeoutMs);
|
||||
return {
|
||||
stdout: "",
|
||||
stderr: "error: failed to delete worktree: Filename too long",
|
||||
@@ -798,6 +800,7 @@ describe("runGatewayUpdate", () => {
|
||||
expect(result.status).toBe("ok");
|
||||
const cleanupStep = result.steps.find((step) => step.name === "preflight cleanup");
|
||||
expect(cleanupStep?.exitCode).toBe(0);
|
||||
expect(cleanupTimeouts[0]).toBeLessThanOrEqual(60_000);
|
||||
expect(cleanupStep?.stderrTail ?? "").toContain(
|
||||
"windows fallback cleanup removed preflight tree",
|
||||
);
|
||||
@@ -806,6 +809,101 @@ describe("runGatewayUpdate", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("falls back when dev preflight worktree cleanup times out", async () => {
|
||||
await setupGitPackageManagerFixture();
|
||||
const calls: string[] = [];
|
||||
const cleanupTimeouts: Array<number | undefined> = [];
|
||||
const upstreamSha = "upstream123";
|
||||
const doctorNodePath = await resolveStableNodePath(process.execPath);
|
||||
const doctorCommand = `${doctorNodePath} ${path.join(tempDir, "openclaw.mjs")} doctor --non-interactive --fix`;
|
||||
|
||||
const runCommand = async (
|
||||
argv: string[],
|
||||
options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
|
||||
) => {
|
||||
const key = argv.join(" ");
|
||||
calls.push(key);
|
||||
|
||||
if (key === `git -C ${tempDir} rev-parse --show-toplevel`) {
|
||||
return { stdout: tempDir, stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse HEAD`) {
|
||||
return { stdout: "abc123", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse --abbrev-ref HEAD`) {
|
||||
return { stdout: "main", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} status --porcelain -- :!dist/control-ui/`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse --abbrev-ref --symbolic-full-name @{upstream}`) {
|
||||
return { stdout: "origin/main", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} fetch --all --prune --tags`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse @{upstream}`) {
|
||||
return { stdout: upstreamSha, stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-list --max-count=10 ${upstreamSha}`) {
|
||||
return { stdout: `${upstreamSha}\n`, stderr: "", code: 0 };
|
||||
}
|
||||
if (key === "pnpm --version") {
|
||||
return { stdout: "10.0.0", stderr: "", code: 0 };
|
||||
}
|
||||
if (
|
||||
key.startsWith(`git -C ${tempDir} worktree add --detach /tmp/`) &&
|
||||
key.endsWith(` ${upstreamSha}`) &&
|
||||
preflightPrefixPattern.test(key)
|
||||
) {
|
||||
return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
|
||||
}
|
||||
if (
|
||||
key.startsWith("git -C /tmp/") &&
|
||||
preflightPrefixPattern.test(key) &&
|
||||
key.includes(" checkout --detach ") &&
|
||||
key.endsWith(upstreamSha)
|
||||
) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === "pnpm install" || key === "pnpm build" || key === "pnpm lint") {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (
|
||||
key.startsWith(`git -C ${tempDir} worktree remove --force `) &&
|
||||
preflightPrefixPattern.test(key)
|
||||
) {
|
||||
cleanupTimeouts.push(options?.timeoutMs);
|
||||
return {
|
||||
stdout: "",
|
||||
stderr: "Command timed out after 60000ms",
|
||||
code: null,
|
||||
};
|
||||
}
|
||||
if (key === `git -C ${tempDir} worktree prune`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rebase ${upstreamSha}`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === doctorCommand) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === "pnpm ui:build") {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
};
|
||||
|
||||
const result = await runWithCommand(runCommand, { channel: "dev" });
|
||||
|
||||
expect(result.status).toBe("ok");
|
||||
const cleanupStep = result.steps.find((step) => step.name === "preflight cleanup");
|
||||
expect(cleanupStep?.exitCode).toBe(0);
|
||||
expect(cleanupTimeouts[0]).toBeLessThanOrEqual(60_000);
|
||||
expect(cleanupStep?.stderrTail ?? "").toContain("fallback cleanup removed preflight tree");
|
||||
});
|
||||
|
||||
it("adds heap headroom to windows pnpm build steps during dev updates", async () => {
|
||||
await setupGitPackageManagerFixture();
|
||||
const upstreamSha = "upstream123";
|
||||
|
||||
@@ -138,6 +138,7 @@ const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME]);
|
||||
const PREFLIGHT_TEMP_PREFIX =
|
||||
process.platform === "win32" ? "ocu-pf-" : "openclaw-update-preflight-";
|
||||
const PREFLIGHT_WORKTREE_DIRNAME = process.platform === "win32" ? "wt" : "worktree";
|
||||
const PREFLIGHT_CLEANUP_TIMEOUT_MS = 60_000;
|
||||
const WINDOWS_PREFLIGHT_BASE_DIR = "ocu";
|
||||
const WINDOWS_BUILD_MAX_OLD_SPACE_MB = 4096;
|
||||
|
||||
@@ -215,10 +216,7 @@ async function removePathRecursive(target: string) {
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function repairWindowsPreflightCleanup(worktreeDir: string, preflightRoot: string) {
|
||||
if (process.platform !== "win32") {
|
||||
return false;
|
||||
}
|
||||
async function repairPreflightCleanup(worktreeDir: string, preflightRoot: string) {
|
||||
try {
|
||||
await fs.rm(worktreeDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 });
|
||||
await fs.rm(preflightRoot, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 });
|
||||
@@ -938,22 +936,25 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
const removeStep = await runStep(
|
||||
step(
|
||||
const removeStep = await runStep({
|
||||
...step(
|
||||
"preflight cleanup",
|
||||
["git", "-C", gitRoot, "worktree", "remove", "--force", worktreeDir],
|
||||
gitRoot,
|
||||
),
|
||||
);
|
||||
timeoutMs: Math.min(timeoutMs, PREFLIGHT_CLEANUP_TIMEOUT_MS),
|
||||
});
|
||||
if (
|
||||
removeStep.exitCode !== 0 &&
|
||||
(await repairWindowsPreflightCleanup(worktreeDir, preflightRoot))
|
||||
(await repairPreflightCleanup(worktreeDir, preflightRoot))
|
||||
) {
|
||||
removeStep.exitCode = 0;
|
||||
const fallbackMessage =
|
||||
process.platform === "win32"
|
||||
? "windows fallback cleanup removed preflight tree"
|
||||
: "fallback cleanup removed preflight tree";
|
||||
removeStep.stderrTail = trimLogTail(
|
||||
[removeStep.stderrTail, "windows fallback cleanup removed preflight tree"]
|
||||
.filter(Boolean)
|
||||
.join("\n"),
|
||||
[removeStep.stderrTail, fallbackMessage].filter(Boolean).join("\n"),
|
||||
MAX_LOG_CHARS,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user