From 65a96453fc838f8e5ef5ca1088b40d28cb3a7a31 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Fri, 5 Jun 2026 01:44:45 +0000 Subject: [PATCH] fix(recipe-upgrade): reconcile mirror from TRUE coopcloud upstream, not from the mirror itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reconcile that's supposed to make the mirror main == upstream main was fetching origin/main — but origin is the cc-ci MIRROR, so it synced the mirror to itself (a no-op) and never pulled real upstream. Fix: fetch coopcloud explicitly (git.coopcloud.tech/coop-cloud/, default branch main OR master) via an 'upstream' remote and force-sync the mirror main + tags from it. Every recipe has a coopcloud correspondent; none are forked. Also reorder the skill so the reconcile runs BEFORE the upgrade check, so the check sees the real current recipe. Verified by divergence test (diverged a mirror, reconcile snapped it back to coopcloud HEAD). Co-Authored-By: Claude Opus 4.8 --- .claude/skills/recipe-upgrade/SKILL.md | 46 +++++++++++-------- .../skills/recipe-upgrade/open-recipe-pr.sh | 31 +++++++++---- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/.claude/skills/recipe-upgrade/SKILL.md b/.claude/skills/recipe-upgrade/SKILL.md index 4e50f4e..2dba526 100644 --- a/.claude/skills/recipe-upgrade/SKILL.md +++ b/.claude/skills/recipe-upgrade/SKILL.md @@ -51,31 +51,37 @@ test** to make a PR green. > 2. **Dirty worktree** is usually just the untracked cc-ci overlay (`compose.ccci.yml`) — don't skip on > that. `git stash -u` before the checks and `stash pop` after. -On cc-ci, refresh the recipe and see what's available (creds + stash first; each abra call pty-wrapped): -``` -ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; R=; \ - url=$(git -C ~/.abra/recipes/$R config --get remote.origin.url); \ - case "$url" in https://git.autonomic.zone/*) \ - git -C ~/.abra/recipes/$R remote set-url origin "https://'"$GITEA_USERNAME"':'"$GITEA_PASSWORD"'@${url#https://}";; esac; \ - git -C ~/.abra/recipes/$R stash -u >/dev/null 2>&1 || true; \ - script -qec "abra recipe fetch $R --force -n" /dev/null; \ - git -C ~/.abra/recipes/$R fetch origin main; \ - script -qec "abra recipe versions $R -m -n" /dev/null; \ - script -qec "abra recipe upgrade $R -m -n" /dev/null; \ - git -C ~/.abra/recipes/$R stash pop >/dev/null 2>&1 || true' -``` -- Skip a recipe as `SKIPPED — dirty-worktree` **only if it is still dirty with TRACKED changes after the - stash** (real local edits), not for the untracked overlay. -- **Reconcile the mirror first (always, even if up to date).** Run: +**Sync the mirror from TRUE upstream FIRST, then check for upgrades against that** — so the check sees +the real current recipe, not a stale mirror. + +- **(a) Reconcile the mirror from coopcloud (always — do this BEFORE the check).** Run: ``` set -a; . /srv/cc-ci/.testenv; set +a ssh cc-ci "GITEA_USERNAME='$GITEA_USERNAME' GITEA_PASSWORD='$GITEA_PASSWORD' GITEA_URL='$GITEA_URL' bash -s --reconcile-only" \ < /srv/cc-ci/.claude/skills/recipe-upgrade/open-recipe-pr.sh ``` - This force-syncs the `recipe-maintainers/` mirror `main` to be **identical to true upstream - main**, and **closes any open mirror PR whose changes are already in upstream main** (merged - upstream — the mirror just never reflected it). Do this before the up-to-date check so a stale - already-merged PR gets cleaned up even when there's nothing new to upgrade. + This fetches **true upstream from coopcloud** (`git.coopcloud.tech/coop-cloud/`, default branch + main OR master — every recipe has a coopcloud correspondent and none are forked) and force-syncs the + `recipe-maintainers/` mirror's `main` + tags to be **identical to it**. (The checkout's + `origin` is the mirror, so the reconcile fetches coopcloud *explicitly* via an `upstream` remote — it + does NOT sync the mirror to itself.) It also **closes any open mirror PR already in upstream main**. + +- **(b) Refresh the local checkout + check for upgrades** (creds + stash per the box; each abra call + pty-wrapped) — the checkout pulls from the now-coopcloud-synced mirror: + ``` + ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; R=; \ + url=$(git -C ~/.abra/recipes/$R config --get remote.origin.url); \ + case "$url" in https://git.autonomic.zone/*) \ + git -C ~/.abra/recipes/$R remote set-url origin "https://'"$GITEA_USERNAME"':'"$GITEA_PASSWORD"'@${url#https://}";; esac; \ + git -C ~/.abra/recipes/$R stash -u >/dev/null 2>&1 || true; \ + script -qec "abra recipe fetch $R --force -n" /dev/null; \ + git -C ~/.abra/recipes/$R fetch origin main; \ + script -qec "abra recipe versions $R -m -n" /dev/null; \ + script -qec "abra recipe upgrade $R -m -n" /dev/null; \ + git -C ~/.abra/recipes/$R stash pop >/dev/null 2>&1 || true' + ``` + Skip a recipe as `SKIPPED — dirty-worktree` **only if it is still dirty with TRACKED changes after the + stash** (real local edits), not for the untracked overlay. - **No upgrades available → stop** (status `SKIPPED — up-to-date`) — after the reconcile above. - Check `git log HEAD..origin/main` and upstream PRs (`git.coopcloud.tech/coop-cloud//pulls`) — if someone already started the bump, **re-plan from the tip of `origin/main`**, not from scratch. diff --git a/.claude/skills/recipe-upgrade/open-recipe-pr.sh b/.claude/skills/recipe-upgrade/open-recipe-pr.sh index 88f365a..ab86dfa 100755 --- a/.claude/skills/recipe-upgrade/open-recipe-pr.sh +++ b/.claude/skills/recipe-upgrade/open-recipe-pr.sh @@ -11,9 +11,10 @@ # < /srv/cc-ci/.claude/skills/recipe-upgrade/open-recipe-pr.sh # # Always (both modes): -# - Force-syncs the mirror's `main` to be IDENTICAL to true upstream `main` -# (origin/main of the abra checkout = git.coopcloud.tech). This guarantees -# every open PR is measured against the real current upstream. +# - Force-syncs the mirror's `main` to be IDENTICAL to true upstream `main`, fetched EXPLICITLY from +# coopcloud (`git.coopcloud.tech/coop-cloud/`, default branch main OR master) via an +# `upstream` remote — NOT from the checkout's `origin`, which is the mirror itself. This guarantees +# the mirror really mirrors upstream and every open PR is measured against the real current upstream. # - Closes any open mirror PR whose changes are ALREADY IN upstream main # (i.e. merging it is a no-op) — it was merged upstream; the mirror just # never reflected it. @@ -49,11 +50,24 @@ AUTH=(-u "${GITEA_USERNAME}:${GITEA_PASSWORD}") [ -d "${RECIPE_DIR}/.git" ] || { echo "ERROR: ${RECIPE_DIR} is not a git repo (run 'abra recipe fetch ${RECIPE}')"; exit 1; } cd "${RECIPE_DIR}" -# --- The true current upstream main --- -echo "→ Fetching upstream main..." -git fetch --quiet origin main -NEW_MAIN_SHA=$(git rev-parse refs/remotes/origin/main) +# --- The true current upstream main = COOPCLOUD --- +# Every recipe has a coop-cloud correspondent and NONE are intentionally forked, so upstream is always +# git.coopcloud.tech/coop-cloud/. NOTE: the abra checkout's `origin` is the cc-ci MIRROR +# (recipe-maintainers/), so we must fetch coopcloud EXPLICITLY via an `upstream` remote — using +# `origin` here would sync the mirror to itself (a no-op) and never pick up real upstream changes. +# coopcloud is public, so no creds are needed for this fetch. +UPSTREAM_URL="${UPSTREAM_URL:-https://git.coopcloud.tech/coop-cloud/${RECIPE}.git}" +echo "→ Fetching true upstream ${UPSTREAM_URL} ..." +git remote | grep -qx upstream && git remote set-url upstream "${UPSTREAM_URL}" || git remote add upstream "${UPSTREAM_URL}" +git fetch --quiet --prune --tags upstream 2>/dev/null || { + echo "ERROR: cannot fetch coopcloud upstream for ${RECIPE} (${UPSTREAM_URL}). Every recipe must have a coop-cloud correspondent — fix the URL/mirror."; exit 1; } +# coopcloud's default branch (main OR master) is what the mirror's `main` must mirror exactly. +UP_REF=$(git ls-remote --symref upstream HEAD 2>/dev/null | sed -n 's#^ref:[[:space:]]*refs/heads/\([^[:space:]]*\).*#upstream/\1#p' | head -1) +[ -n "${UP_REF}" ] || for b in main master; do git rev-parse --verify -q "upstream/$b" >/dev/null 2>&1 && { UP_REF="upstream/$b"; break; }; done +[ -n "${UP_REF}" ] || { echo "ERROR: no default branch found on coopcloud ${RECIPE}"; exit 1; } +NEW_MAIN_SHA=$(git rev-parse "${UP_REF}") MAIN_TREE=$(git rev-parse "${NEW_MAIN_SHA}^{tree}") +echo " upstream ${UP_REF} = ${NEW_MAIN_SHA:0:8}" # --- Ensure the mirror repo exists --- STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${AUTH[@]}" "${API}/repos/${NAMESPACE}/${RECIPE}") @@ -77,9 +91,10 @@ fi REMOTE_URL="https://${GITEA_USERNAME}:${PASS_ENC}@${GITEA_URL}/${NAMESPACE}/${RECIPE}.git" git remote | grep -qx gitea && git remote set-url gitea "${REMOTE_URL}" || git remote add gitea "${REMOTE_URL}" -# --- Mirror main := true upstream main (identical) --- +# --- Mirror main := true upstream main (identical); mirror also gets upstream's published tags --- echo "→ Force-syncing gitea/main to upstream main (${NEW_MAIN_SHA:0:8})..." git push --force gitea "${NEW_MAIN_SHA}:refs/heads/main" +git push --quiet gitea "refs/tags/*:refs/tags/*" 2>/dev/null || true # coopcloud published versions git fetch --quiet gitea '+refs/heads/*:refs/remotes/gitea/*' || true # --- Preconditions for open mode ---