diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd97502c4529..625da2013dcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1784,7 +1784,13 @@ jobs: if swift build --package-path apps/macos --product OpenClaw --configuration release; then exit 0 fi + if [[ "$attempt" -eq 3 ]]; then + break + fi echo "swift build failed (attempt $attempt/3). Retrying…" + # SwiftPM can invalidate a restored binary artifact while planning. + # Reset so the next attempt downloads a complete dependency graph. + swift package --package-path apps/macos reset sleep $((attempt * 20)) done exit 1 diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 63926874c173..c1aca530d9b2 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -455,6 +455,20 @@ describe("ci workflow guards", () => { } }); + it("resets SwiftPM state between macOS release build retries", () => { + const workflow = readCiWorkflow(); + const buildStep = workflow.jobs["macos-swift"].steps.find( + (step) => step.name === "Swift build (release)", + ); + + expect(buildStep.run).toContain("for attempt in 1 2 3"); + expect(buildStep.run).toContain('if [[ "$attempt" -eq 3 ]]; then'); + expect(buildStep.run).toContain("swift package --package-path apps/macos reset"); + expect(buildStep.run.indexOf("swift package --package-path apps/macos reset")).toBeGreaterThan( + buildStep.run.indexOf("swift build failed"), + ); + }); + it("bounds the Windows Crabbox hydrate main fetch", () => { const workflow = readFileSync(".github/workflows/crabbox-hydrate.yml", "utf8");