






















@@ -710,6 +710,7 @@ describe("runGatewayUpdate", () => {
710710it("does not fail a good windows dev preflight only because worktree cleanup hit long paths", async () => {
711711await setupGitPackageManagerFixture();
712712const calls: string[] = [];
713+const cleanupTimeouts: Array<number | undefined> = [];
713714const upstreamSha = "upstream123";
714715const doctorNodePath = await resolveStableNodePath(process.execPath);
715716const doctorCommand = `${doctorNodePath} ${path.join(tempDir, "openclaw.mjs")} doctor --non-interactive --fix`;
@@ -718,7 +719,7 @@ describe("runGatewayUpdate", () => {
718719try {
719720const runCommand = async (
720721argv: string[],
721-_options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
722+options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
722723) => {
723724const key = argv.join(" ");
724725calls.push(key);
@@ -772,6 +773,7 @@ describe("runGatewayUpdate", () => {
772773key.startsWith(`git -C ${tempDir} worktree remove --force `) &&
773774preflightPrefixPattern.test(key)
774775) {
776+cleanupTimeouts.push(options?.timeoutMs);
775777return {
776778stdout: "",
777779stderr: "error: failed to delete worktree: Filename too long",
@@ -798,6 +800,7 @@ describe("runGatewayUpdate", () => {
798800expect(result.status).toBe("ok");
799801const cleanupStep = result.steps.find((step) => step.name === "preflight cleanup");
800802expect(cleanupStep?.exitCode).toBe(0);
803+expect(cleanupTimeouts[0]).toBeLessThanOrEqual(60_000);
801804expect(cleanupStep?.stderrTail ?? "").toContain(
802805"windows fallback cleanup removed preflight tree",
803806);
@@ -806,6 +809,101 @@ describe("runGatewayUpdate", () => {
806809}
807810});
808811812+it("falls back when dev preflight worktree cleanup times out", async () => {
813+await setupGitPackageManagerFixture();
814+const calls: string[] = [];
815+const cleanupTimeouts: Array<number | undefined> = [];
816+const upstreamSha = "upstream123";
817+const doctorNodePath = await resolveStableNodePath(process.execPath);
818+const doctorCommand = `${doctorNodePath} ${path.join(tempDir, "openclaw.mjs")} doctor --non-interactive --fix`;
819+820+const runCommand = async (
821+argv: string[],
822+options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
823+) => {
824+const key = argv.join(" ");
825+calls.push(key);
826+827+if (key === `git -C ${tempDir} rev-parse --show-toplevel`) {
828+return { stdout: tempDir, stderr: "", code: 0 };
829+}
830+if (key === `git -C ${tempDir} rev-parse HEAD`) {
831+return { stdout: "abc123", stderr: "", code: 0 };
832+}
833+if (key === `git -C ${tempDir} rev-parse --abbrev-ref HEAD`) {
834+return { stdout: "main", stderr: "", code: 0 };
835+}
836+if (key === `git -C ${tempDir} status --porcelain -- :!dist/control-ui/`) {
837+return { stdout: "", stderr: "", code: 0 };
838+}
839+if (key === `git -C ${tempDir} rev-parse --abbrev-ref --symbolic-full-name @{upstream}`) {
840+return { stdout: "origin/main", stderr: "", code: 0 };
841+}
842+if (key === `git -C ${tempDir} fetch --all --prune --tags`) {
843+return { stdout: "", stderr: "", code: 0 };
844+}
845+if (key === `git -C ${tempDir} rev-parse @{upstream}`) {
846+return { stdout: upstreamSha, stderr: "", code: 0 };
847+}
848+if (key === `git -C ${tempDir} rev-list --max-count=10 ${upstreamSha}`) {
849+return { stdout: `${upstreamSha}\n`, stderr: "", code: 0 };
850+}
851+if (key === "pnpm --version") {
852+return { stdout: "10.0.0", stderr: "", code: 0 };
853+}
854+if (
855+key.startsWith(`git -C ${tempDir} worktree add --detach /tmp/`) &&
856+key.endsWith(` ${upstreamSha}`) &&
857+preflightPrefixPattern.test(key)
858+) {
859+return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
860+}
861+if (
862+key.startsWith("git -C /tmp/") &&
863+preflightPrefixPattern.test(key) &&
864+key.includes(" checkout --detach ") &&
865+key.endsWith(upstreamSha)
866+) {
867+return { stdout: "", stderr: "", code: 0 };
868+}
869+if (key === "pnpm install" || key === "pnpm build" || key === "pnpm lint") {
870+return { stdout: "", stderr: "", code: 0 };
871+}
872+if (
873+key.startsWith(`git -C ${tempDir} worktree remove --force `) &&
874+preflightPrefixPattern.test(key)
875+) {
876+cleanupTimeouts.push(options?.timeoutMs);
877+return {
878+stdout: "",
879+stderr: "Command timed out after 60000ms",
880+code: null,
881+};
882+}
883+if (key === `git -C ${tempDir} worktree prune`) {
884+return { stdout: "", stderr: "", code: 0 };
885+}
886+if (key === `git -C ${tempDir} rebase ${upstreamSha}`) {
887+return { stdout: "", stderr: "", code: 0 };
888+}
889+if (key === doctorCommand) {
890+return { stdout: "", stderr: "", code: 0 };
891+}
892+if (key === "pnpm ui:build") {
893+return { stdout: "", stderr: "", code: 0 };
894+}
895+return { stdout: "", stderr: "", code: 0 };
896+};
897+898+const result = await runWithCommand(runCommand, { channel: "dev" });
899+900+expect(result.status).toBe("ok");
901+const cleanupStep = result.steps.find((step) => step.name === "preflight cleanup");
902+expect(cleanupStep?.exitCode).toBe(0);
903+expect(cleanupTimeouts[0]).toBeLessThanOrEqual(60_000);
904+expect(cleanupStep?.stderrTail ?? "").toContain("fallback cleanup removed preflight tree");
905+});
906+809907it("adds heap headroom to windows pnpm build steps during dev updates", async () => {
810908await setupGitPackageManagerFixture();
811909const upstreamSha = "upstream123";
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。