From 0be9455a53a174538d8367adbecbe8065c9e824d Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sat, 18 Apr 2026 18:58:15 +0200 Subject: [PATCH] fix(sovereign-ci): self-heal broken sibling clone cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause (bashrs#197 coverage failure): The sibling checkout step had `|| true` on fetch, silently masking a broken cached clone. Script flow: if [ -d "$repo" ]; then git fetch ... && reset --hard ... || true # swallows errors else for attempt ...; do git clone ...; done fi When cache dir exists but is empty/broken (runner workspace leftover), the fetch fails but `|| true` eats the error. Next step (cargo metadata) then fails with 'No such file or directory' pointing at the missing Cargo.toml — 20s into coverage, not at the sibling-clone step. Five-whys: 1. Why did cargo metadata fail? Missing provable-contracts Cargo.toml 2. Why missing? Sibling dir exists but is empty 3. Why empty? Fetch into existing broken cache failed 4. Why silent? `|| true` swallowed the error 5. Why defensive? To tolerate offline/transient failures — but it hid real ones too Fix: validate cache with `git rev-parse HEAD`. If HEAD missing OR fetch fails, rm -rf + re-clone. Drop --quiet from clone so output is visible on failure. Applied to all 5 jobs (test, lint, coverage, security, provenance). No caller changes needed — `uses: @main` picks up fix automatically. Refs paiml/bashrs#197 (coverage blocker after 10d cache staleness) --- .github/workflows/sovereign-ci.yml | 80 ++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/.github/workflows/sovereign-ci.yml b/.github/workflows/sovereign-ci.yml index e1d7fd8..c3f8291 100644 --- a/.github/workflows/sovereign-ci.yml +++ b/.github/workflows/sovereign-ci.yml @@ -106,12 +106,20 @@ jobs: [ "$repo" = "${{ inputs.repo }}" ] && continue # Remove stale partial clones (file instead of dir) if [ -e "$repo" ] && [ ! -d "$repo" ]; then rm -f "$repo"; fi + # Validate cached clone. If HEAD missing or fetch fails, nuke + re-clone. + # Silent `|| true` on fetch previously masked broken caches (bashrs#197). if [ -d "$repo" ]; then - # Pull latest to avoid stale cached versions - git -C "$repo" fetch --depth 1 origin main 2>/dev/null && git -C "$repo" reset --hard FETCH_HEAD 2>/dev/null || true - else + if ! git -C "$repo" rev-parse HEAD >/dev/null 2>&1; then + echo "::warning::$repo cache invalid (no HEAD) — re-cloning" + rm -rf "$repo" + elif ! (git -C "$repo" fetch --depth 1 origin main 2>&1 && git -C "$repo" reset --hard FETCH_HEAD >/dev/null 2>&1); then + echo "::warning::$repo fetch failed — re-cloning" + rm -rf "$repo" + fi + fi + if [ ! -d "$repo" ]; then for attempt in 1 2 3; do - git clone --depth 1 --quiet "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break + git clone --depth 1 "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break rm -rf "$repo" # Clean failed clone before retry echo "::warning::Retry $attempt for $repo clone" sleep 2 @@ -226,12 +234,20 @@ jobs: [ "$repo" = "${{ inputs.repo }}" ] && continue # Remove stale partial clones (file instead of dir) if [ -e "$repo" ] && [ ! -d "$repo" ]; then rm -f "$repo"; fi + # Validate cached clone. If HEAD missing or fetch fails, nuke + re-clone. + # Silent `|| true` on fetch previously masked broken caches (bashrs#197). if [ -d "$repo" ]; then - # Pull latest to avoid stale cached versions - git -C "$repo" fetch --depth 1 origin main 2>/dev/null && git -C "$repo" reset --hard FETCH_HEAD 2>/dev/null || true - else + if ! git -C "$repo" rev-parse HEAD >/dev/null 2>&1; then + echo "::warning::$repo cache invalid (no HEAD) — re-cloning" + rm -rf "$repo" + elif ! (git -C "$repo" fetch --depth 1 origin main 2>&1 && git -C "$repo" reset --hard FETCH_HEAD >/dev/null 2>&1); then + echo "::warning::$repo fetch failed — re-cloning" + rm -rf "$repo" + fi + fi + if [ ! -d "$repo" ]; then for attempt in 1 2 3; do - git clone --depth 1 --quiet "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break + git clone --depth 1 "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break rm -rf "$repo" # Clean failed clone before retry echo "::warning::Retry $attempt for $repo clone" sleep 2 @@ -349,12 +365,20 @@ jobs: [ "$repo" = "${{ inputs.repo }}" ] && continue # Remove stale partial clones (file instead of dir) if [ -e "$repo" ] && [ ! -d "$repo" ]; then rm -f "$repo"; fi + # Validate cached clone. If HEAD missing or fetch fails, nuke + re-clone. + # Silent `|| true` on fetch previously masked broken caches (bashrs#197). if [ -d "$repo" ]; then - # Pull latest to avoid stale cached versions - git -C "$repo" fetch --depth 1 origin main 2>/dev/null && git -C "$repo" reset --hard FETCH_HEAD 2>/dev/null || true - else + if ! git -C "$repo" rev-parse HEAD >/dev/null 2>&1; then + echo "::warning::$repo cache invalid (no HEAD) — re-cloning" + rm -rf "$repo" + elif ! (git -C "$repo" fetch --depth 1 origin main 2>&1 && git -C "$repo" reset --hard FETCH_HEAD >/dev/null 2>&1); then + echo "::warning::$repo fetch failed — re-cloning" + rm -rf "$repo" + fi + fi + if [ ! -d "$repo" ]; then for attempt in 1 2 3; do - git clone --depth 1 --quiet "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break + git clone --depth 1 "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break rm -rf "$repo" # Clean failed clone before retry echo "::warning::Retry $attempt for $repo clone" sleep 2 @@ -466,12 +490,20 @@ jobs: [ "$repo" = "${{ inputs.repo }}" ] && continue # Remove stale partial clones (file instead of dir) if [ -e "$repo" ] && [ ! -d "$repo" ]; then rm -f "$repo"; fi + # Validate cached clone. If HEAD missing or fetch fails, nuke + re-clone. + # Silent `|| true` on fetch previously masked broken caches (bashrs#197). if [ -d "$repo" ]; then - # Pull latest to avoid stale cached versions - git -C "$repo" fetch --depth 1 origin main 2>/dev/null && git -C "$repo" reset --hard FETCH_HEAD 2>/dev/null || true - else + if ! git -C "$repo" rev-parse HEAD >/dev/null 2>&1; then + echo "::warning::$repo cache invalid (no HEAD) — re-cloning" + rm -rf "$repo" + elif ! (git -C "$repo" fetch --depth 1 origin main 2>&1 && git -C "$repo" reset --hard FETCH_HEAD >/dev/null 2>&1); then + echo "::warning::$repo fetch failed — re-cloning" + rm -rf "$repo" + fi + fi + if [ ! -d "$repo" ]; then for attempt in 1 2 3; do - git clone --depth 1 --quiet "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break + git clone --depth 1 "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break rm -rf "$repo" # Clean failed clone before retry echo "::warning::Retry $attempt for $repo clone" sleep 2 @@ -584,12 +616,20 @@ jobs: [ "$repo" = "${{ inputs.repo }}" ] && continue # Remove stale partial clones (file instead of dir) if [ -e "$repo" ] && [ ! -d "$repo" ]; then rm -f "$repo"; fi + # Validate cached clone. If HEAD missing or fetch fails, nuke + re-clone. + # Silent `|| true` on fetch previously masked broken caches (bashrs#197). if [ -d "$repo" ]; then - # Pull latest to avoid stale cached versions - git -C "$repo" fetch --depth 1 origin main 2>/dev/null && git -C "$repo" reset --hard FETCH_HEAD 2>/dev/null || true - else + if ! git -C "$repo" rev-parse HEAD >/dev/null 2>&1; then + echo "::warning::$repo cache invalid (no HEAD) — re-cloning" + rm -rf "$repo" + elif ! (git -C "$repo" fetch --depth 1 origin main 2>&1 && git -C "$repo" reset --hard FETCH_HEAD >/dev/null 2>&1); then + echo "::warning::$repo fetch failed — re-cloning" + rm -rf "$repo" + fi + fi + if [ ! -d "$repo" ]; then for attempt in 1 2 3; do - git clone --depth 1 --quiet "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break + git clone --depth 1 "https://github.com/paiml/$repo.git" "$repo" 2>&1 && break rm -rf "$repo" # Clean failed clone before retry echo "::warning::Retry $attempt for $repo clone" sleep 2