From bde0031e338514781d3a0953de0346d83f1db4d2 Mon Sep 17 00:00:00 2001 From: "guys-inc-ops[bot]" <321481384+guys-inc-ops[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 03:54:32 +0000 Subject: [PATCH] Stop the state pull from swallowing its own failure The step ended in '|| true', so a failed pull was indistinguishable from an empty repository: reprepro would add the single incoming package, export, and the sync would overwrite dists/ with a one-package index. Every previously published version stops being listed, the pool objects survive unreferenced, and the run stays green. This is the failure mode recorded in archivist's ADR 0001 as the reason that tool regenerates from scratch rather than pulling mutable state. It was still live here. Two changes. A genuine failure now fails the job, since there is no longer a reason to suppress it - 'aws s3 sync' from an absent prefix already exits 0, so the first publish into a new prefix was never the problem '|| true' solved. And because a zero exit code alone cannot prove the pull was complete, the step now compares the remote object count against what actually landed, and refuses to continue if the remote has content and nothing arrived. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_015AH1v3tR8Xw2DmKqJSipPd --- .github/workflows/publish-apt.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-apt.yml b/.github/workflows/publish-apt.yml index 99ce2f62d3..445bf1460e 100644 --- a/.github/workflows/publish-apt.yml +++ b/.github/workflows/publish-apt.yml @@ -103,10 +103,31 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: auto + R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} run: | + set -euo pipefail mkdir -p repo - aws s3 sync "s3://$BUCKET/$PREFIX" repo \ - --endpoint-url "https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com" || true + endpoint="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" + + # This step used to end in `|| true`. A failed pull therefore looked + # exactly like an empty repository: reprepro would add the one incoming + # package, export, and the sync would overwrite dists/ with a + # single-package index. Every previously published version silently + # stops being listed, and the run stays green. + # + # So: let a genuine failure fail, and separately catch the case where + # the remote has content but nothing arrived - which a zero exit code + # alone would not reveal. + remote=$(aws s3 ls "s3://$BUCKET/$PREFIX/" --recursive --endpoint-url "$endpoint" | wc -l) + aws s3 sync "s3://$BUCKET/$PREFIX" repo --endpoint-url "$endpoint" --no-progress + pulled=$(find repo -type f | wc -l) + echo "remote objects: $remote, pulled: $pulled" + + if [ "$remote" -gt 0 ] && [ "$pulled" -eq 0 ]; then + echo "::error::Remote holds $remote objects but none were pulled. Refusing to" + echo "::error::republish, which would drop every existing package from the index." + exit 1 + fi - name: Prepare reprepro config run: |