From 9dfcfb46a368b8c45b6a397d25984e9eac94b90d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 05:51:10 +0000 Subject: [PATCH] ci(board-snapshot): sync the snapshot directory so the R2 mirror matches the documented prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The R2 step synced the archive worktree ROOT while the snapshot is written to `archive/board`, so every key landed at `objectstack/board/board/...` — one segment deeper than the `mirror` prefix on the line above it documents, and visible only from the bucket side. Sync `archive/board/` instead, which maps `archive/board/X` to `objectstack/board/X`. `--delete` is kept and is what migrates the bucket: the stale `objectstack/board/board/` tree is inside the sync target and has no local counterpart, so the first scheduled run after this change removes it. The target is deliberately NOT widened to `objectstack/` to get the same mapping — that would put `objectstack/snapshots/` inside a `--delete` target and eat the daily tarballs. The two `.git` excludes are dropped as dead: they matched `archive/.git`, the linked worktree's gitdir FILE, which is outside the new source. The daily tarball step, the four secrets and the workflow permissions are untouched. Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8 Co-authored-by: Claude --- .github/workflows/board-snapshot.yml | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/board-snapshot.yml b/.github/workflows/board-snapshot.yml index 2d5380abc1..fccb7801e7 100644 --- a/.github/workflows/board-snapshot.yml +++ b/.github/workflows/board-snapshot.yml @@ -339,14 +339,31 @@ jobs: endpoint="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" mirror="s3://${R2_BUCKET}/objectstack/board/" - # `archive/` is a linked worktree, so its `.git` is a FILE and the - # pattern `.git/*` does not match it; both spellings are excluded. + # The SOURCE is the snapshot directory, NOT the worktree root that + # contains it. The snapshot above is written to `archive/board`, so + # syncing `archive/` would map `archive/board/X` onto the `mirror` + # prefix as `objectstack/board/` + `board/X` — one segment deeper + # than the prefix on the line above says the mirror is, and visible + # only from the bucket side. Syncing `archive/board/` maps + # `archive/board/X` to `objectstack/board/X`, which is the layout a + # restore driven from the documented path looks for. + # + # `--delete` is kept and needs no migration step of its own: the + # stale `objectstack/board/board/` tree is inside this sync target + # and has no local counterpart, so the first run after this change + # removes it. ⛔ The target is never widened to `objectstack/` to + # achieve the same mapping — that would put `objectstack/snapshots/` + # inside a `--delete` target and eat the daily tarballs, the failure + # the comment above already warns about. + # + # No `.git` filter is needed here: `archive/` is a linked worktree, + # so its `.git` is a FILE at `archive/.git`, which is outside this + # source (filters match paths relative to the source directory). # No pipe between the command and `$?` — see the snapshot step. set +e - aws s3 sync archive/ "$mirror" \ + aws s3 sync archive/board/ "$mirror" \ --endpoint-url "$endpoint" \ --delete --no-progress \ - --exclude '.git' --exclude '.git/*' \ > "$RUNNER_TEMP/r2-sync.log" 2>&1 code=$? set -e