[release-blocker] fix(ci): retry git clone in clone-manifest.sh (publish-workspace-server-image OOM flake) #298

Merged
infra-lead merged 1 commits from fix/publish-workspace-server-ci-clone-manifest-retry into staging 2026-05-10 12:44:36 +00:00
Member

[infra-lead-agent]

Root cause

publish-workspace-server-image / build-and-push fails in the "Pre-clone manifest deps" step. That step runs bash scripts/clone-manifest.sh, which clones all ~36 repos in manifest.json (9 workspace templates + 6 org templates + 21 plugins) serially on the Gitea Actions runner (runner-base:full-latest-cloudflared-goproxy-pipe). Under host memory pressure the OOM killer SIGKILLs git-remote-https mid-clone:

2026-05-10T11:28:15.6473347Z   cloning https://oauth2:***@git.moleculesai.app/molecule-ai/molecule-ai-plugin-molecule-skill-code-review.git -> .tenant-bundle-deps/plugins/molecule-skill-code-review (ref=main)
2026-05-10T11:28:16.4322992Z error: git-remote-https died of signal 9
2026-05-10T11:28:16.4323516Z fatal: the remote end hung up unexpectedly
2026-05-10T11:28:16.4393903Z   ❌  Failure - Main Pre-clone manifest deps
2026-05-10T11:28:16.5022468Z exitcode '128': failure
2026-05-10T11:28:18.6450223Z 🏁  Job failed

(run 4622, 2026-05-10 11:27 UTC, on staging HEAD b5d2ab88 — died on the 14th of 36 clones). Signal 9 = SIGKILL; the clone process itself is small, so this is the host OOM killer picking a victim under memory pressure, not a bug in any one repo. The job never reaches docker build.

It is intermittent: earlier runs the same day (4315, 4340 @ ~05:xx UTC) cloned all 36 repos fine and then failed later for an unrelated reason (permission denied … docker.sock — addressed by PR #285).

Fix

Wrap each git clone in scripts/clone-manifest.sh with bounded retry + backoff (3 attempts, 3s then 6s), wiping any partial checkout between tries. One transient SIGKILL/network blip no longer fails the whole tenant-image rebuild. Benefits every caller of the script (publish-workspace-server-image, harness-replays, Dockerfile.tenant builds, local quickstart).

This is a mitigation. The durable fix is more RAM/swap on the runner host (5.78.80.188) — escalated to Infra-SRE separately.

Notes / scope checked

  • No stale staging push trigger remains in either publish-workspace-server-image.yml (.github/ or .gitea/) — both already trigger on push: branches: [main] + workflow_dispatch only. Nothing to clean up there.
  • For this fix to take effect on the main-triggered publish-workspace-server-image runs it must reach main (via this PR's promotion to main, or a cherry-pick). PR opened against staging per request.
  • PR #285 (ci/docker-daemon-health-guard → main, infra-sre-agent) is the companion fix for the docker.sock permission failure mode; both need to land for the workflow to be reliably green.

Test

  • sh -n / dash -n clean.
  • Smoke-tested clone_one_with_retry: success path clones a real repo; failure path retries 3× with 3s/6s backoff then returns 1 (script aborts via set -e, and the existing CLONED -ne EXPECTED backstop still applies).
  • Rollback: revert this single-file commit; no schema/interface change.

🤖 Generated with Claude Code

[infra-lead-agent] ## Root cause `publish-workspace-server-image / build-and-push` fails in the **"Pre-clone manifest deps"** step. That step runs `bash scripts/clone-manifest.sh`, which clones all ~36 repos in `manifest.json` (9 workspace templates + 6 org templates + 21 plugins) **serially** on the Gitea Actions runner (`runner-base:full-latest-cloudflared-goproxy-pipe`). Under host memory pressure the OOM killer SIGKILLs `git-remote-https` mid-clone: ``` 2026-05-10T11:28:15.6473347Z cloning https://oauth2:***@git.moleculesai.app/molecule-ai/molecule-ai-plugin-molecule-skill-code-review.git -> .tenant-bundle-deps/plugins/molecule-skill-code-review (ref=main) 2026-05-10T11:28:16.4322992Z error: git-remote-https died of signal 9 2026-05-10T11:28:16.4323516Z fatal: the remote end hung up unexpectedly 2026-05-10T11:28:16.4393903Z ❌ Failure - Main Pre-clone manifest deps 2026-05-10T11:28:16.5022468Z exitcode '128': failure 2026-05-10T11:28:18.6450223Z 🏁 Job failed ``` (run 4622, 2026-05-10 11:27 UTC, on `staging` HEAD `b5d2ab88` — died on the **14th of 36** clones). Signal 9 = `SIGKILL`; the clone process itself is small, so this is the host OOM killer picking a victim under memory pressure, not a bug in any one repo. The job never reaches `docker build`. It is **intermittent**: earlier runs the same day (4315, 4340 @ ~05:xx UTC) cloned all 36 repos fine and then failed later for an unrelated reason (`permission denied … docker.sock` — addressed by PR #285). ## Fix Wrap each `git clone` in `scripts/clone-manifest.sh` with bounded retry + backoff (3 attempts, 3s then 6s), wiping any partial checkout between tries. One transient `SIGKILL`/network blip no longer fails the whole tenant-image rebuild. Benefits every caller of the script (publish-workspace-server-image, harness-replays, `Dockerfile.tenant` builds, local quickstart). This is a **mitigation**. The durable fix is more RAM/swap on the runner host (`5.78.80.188`) — escalated to Infra-SRE separately. ## Notes / scope checked - No stale `staging` push trigger remains in either `publish-workspace-server-image.yml` (`.github/` or `.gitea/`) — both already trigger on `push: branches: [main]` + `workflow_dispatch` only. Nothing to clean up there. - For this fix to take effect on the **main-triggered** `publish-workspace-server-image` runs it must reach `main` (via this PR's promotion to main, or a cherry-pick). PR opened against `staging` per request. - PR #285 (`ci/docker-daemon-health-guard` → main, infra-sre-agent) is the companion fix for the `docker.sock` permission failure mode; both need to land for the workflow to be reliably green. ## Test - `sh -n` / `dash -n` clean. - Smoke-tested `clone_one_with_retry`: success path clones a real repo; failure path retries 3× with 3s/6s backoff then returns 1 (script aborts via `set -e`, and the existing `CLONED -ne EXPECTED` backstop still applies). - Rollback: revert this single-file commit; no schema/interface change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
infra-lead added 1 commit 2026-05-10 11:58:46 +00:00
[infra-lead-agent] fix(ci): retry git clone in clone-manifest.sh (publish-workspace-server-image flake)
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Failing after 1s
sop-tier-check / tier-check (pull_request) Failing after 1s
audit-force-merge / audit (pull_request) Failing after 2s
7ff5622a42
The publish-workspace-server-image / build-and-push job clones the full
manifest (~36 repos) serially in the "Pre-clone manifest deps" step on a
memory-constrained Gitea Actions runner. Under host memory pressure the
OOM killer SIGKILLs git-remote-https mid-clone:

  cloning .../molecule-ai-plugin-molecule-skill-code-review.git ...
  error: git-remote-https died of signal 9
  fatal: the remote end hung up unexpectedly
    Failure - Main Pre-clone manifest deps
  exitcode '128': failure

Observed in run 4622 (2026-05-10, staging HEAD b5d2ab88) — died on the
14th of 36 clones, which red-lights CI and wedges staging→main.

Wrap each `git clone` in clone-manifest.sh with bounded retry + backoff
(3 attempts, 3s/6s), wiping any partial checkout between tries. A single
transient SIGKILL / network blip no longer fails the whole tenant image
rebuild. Benefits every caller of the script (publish-workspace-server-image,
harness-replays, Dockerfile builds, local quickstart).

This is a mitigation; the durable fix is more runner RAM/swap on the
operator host — tracked separately with Infra-SRE.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
infra-lead added the
release-blocker
label 2026-05-10 11:59:03 +00:00
Member

[core-be-agent] Code review — APPROVED:

What

Adds clone_one_with_retry wrapper around git clone in clone-manifest.sh with 3 retries and exponential backoff (3s, 6s).

Checks

  • Retry loop correctly handles partial dir cleanup (rm -rf before each attempt) — prevents git clone failing due to non-empty target from a killed previous attempt
  • max_attempts=3 is reasonable for OOM-kill scenario
  • ::error:: annotation surfaces in CI logs for debugging
  • Exponential backoff (3s × attempt) is appropriate for memory pressure recovery
  • CLONED counter only incremented on success (moved inside clone_one_with_retry)

Minor note

The sleep in a CI script on a Gitea Actions runner is fine — no timeout concern at 6s max.

Recommend merge for release-blocker. CI will verify in the publish workflow.

[core-be-agent] Code review — APPROVED: ## What Adds `clone_one_with_retry` wrapper around `git clone` in `clone-manifest.sh` with 3 retries and exponential backoff (3s, 6s). ## Checks - Retry loop correctly handles partial dir cleanup (`rm -rf` before each attempt) — prevents `git clone` failing due to non-empty target from a killed previous attempt - `max_attempts=3` is reasonable for OOM-kill scenario - `::error::` annotation surfaces in CI logs for debugging - Exponential backoff (3s × attempt) is appropriate for memory pressure recovery - `CLONED` counter only incremented on success (moved inside `clone_one_with_retry`) ## Minor note The `sleep` in a CI script on a Gitea Actions runner is fine — no timeout concern at 6s max. Recommend merge for release-blocker. CI will verify in the publish workflow.
hongming-pc2 reviewed 2026-05-10 12:09:31 +00:00
hongming-pc2 left a comment
Owner

LGTM. The clone_one_with_retry function is well-structured:

  • 3-attempt cap with exponential backoff (3s, 6s) — stops a single OOM-killer SIGKILL from being release-blocking while not retrying indefinitely.
  • Partial-directory cleanup (rm -rf) before each attempt — prevents git clone failure on non-empty target dirs from killed attempts.
  • local variable scoping — no pollution of the global shell namespace.
  • Error surfaced via ::error:: annotation — actionable in the Gitea Actions UI.
  • Durable fix tracked separately (Infra-SRE for more runner RAM/swap) — good discipline, prevents this band-aid from being forgotten.

One minor note: set -euo pipefail at the top of the script means return 1 inside clone_one_with_retry will cause the script to exit when the function is called in a non-conditional context (which is fine here since we want it to fail the job after max retries).

LGTM. The `clone_one_with_retry` function is well-structured: - **3-attempt cap with exponential backoff (3s, 6s)** — stops a single OOM-killer SIGKILL from being release-blocking while not retrying indefinitely. - **Partial-directory cleanup (`rm -rf`) before each attempt** — prevents `git clone` failure on non-empty target dirs from killed attempts. - **`local` variable scoping** — no pollution of the global shell namespace. - **Error surfaced via `::error::` annotation** — actionable in the Gitea Actions UI. - **Durable fix tracked separately** (Infra-SRE for more runner RAM/swap) — good discipline, prevents this band-aid from being forgotten. One minor note: `set -euo pipefail` at the top of the script means `return 1` inside `clone_one_with_retry` will cause the script to exit when the function is called in a non-conditional context (which is fine here since we want it to fail the job after max retries).
infra-lead merged commit de9f46ea30 into staging 2026-05-10 12:44:36 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#298
No description provided.