recipe-upgrade: reconcile mirror to upstream main + close merged/superseded PRs

Per operator: an open mirror PR must mean "genuinely still open against true
current upstream main". On every run the recipe-upgrade flow now:
- force-syncs the recipe-maintainers/<recipe> mirror `main` to be IDENTICAL to
  upstream main (origin/main of the abra checkout = coopcloud);
- closes any open mirror PR whose changes are already in upstream main (merged
  upstream, no-op merge detected via `git merge-tree` vs main's tree) — even
  when the recipe is up to date (new `--reconcile-only` mode, run in step 1);
- when opening a new upgrade PR, closes any other still-open PR for that recipe
  (superseded) and opens the new one IN ITS PLACE; same-version re-runs just
  update the existing same-branch PR.
open-recipe-pr.sh gains the --reconcile-only mode + the close logic (with an
auto-close comment naming the reason). upgrade-all reconciles every candidate's
mirror during the survey so merged PRs are closed fleet-wide. Still never merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 17:32:34 +01:00
co-authored by Claude Opus 4.8
parent a8b4b4c39e
commit 62b7af7a97
3 changed files with 130 additions and 32 deletions
+20 -4
View File
@@ -31,7 +31,17 @@ ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; \
abra recipe upgrade <recipe> -m -n'
```
- **Dirty worktree on cc-ci → abort this recipe** (status `SKIPPED — dirty-worktree`); don't prompt.
- **No upgrades available → stop** (status `SKIPPED — up-to-date`).
- **Reconcile the mirror first (always, even if up to date).** 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 <recipe> --reconcile-only" \
< /srv/cc-ci/.claude/skills/recipe-upgrade/open-recipe-pr.sh
```
This force-syncs the `recipe-maintainers/<recipe>` 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.
- **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/<recipe>/pulls`)
— if someone already started the bump, **re-plan from the tip of `origin/main`**, not from scratch.
- For each service with an upgrade, fetch upstream **release notes** (WebFetch) between current and
@@ -56,9 +66,12 @@ 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 <recipe>" \
< /srv/cc-ci/.claude/skills/recipe-upgrade/open-recipe-pr.sh
```
This pushes the upgrade branch to `recipe-maintainers/<recipe>` (force-syncing mirror `main` from
upstream so the diff is exactly the upgrade) and opens the PR. Capture the `PR_URL`. Optionally export
`RECIPE_PR_BODY` first (image-tag table + the planned operator-action notes).
This (a) re-syncs mirror `main` to true upstream main, (b) **closes any open PR already merged
upstream**, (c) **closes any other still-open PR for this recipe as superseded**, then pushes the
upgrade branch and opens the new PR **in its place** (so the diff is exactly this upgrade, against the
real current upstream main). Capture the `PR_URL`. Optionally export `RECIPE_PR_BODY` first (image-tag
table + the planned operator-action notes). Re-running with the same target version updates the
existing same-branch PR rather than duplicating it.
### 4. VERIFY the upgrade on the CI server (the gate)
Run the cc-ci full suite **cold** against the PR head — the dogfood gate from `ci-test-review`:
@@ -103,6 +116,9 @@ Always state explicitly that **nothing was merged** — the PR(s) await operator
## Guardrails
- **cc-ci is the gate** — deterministic harness decides pass/fail; AI plans, implements, diagnoses.
- **Create + verify, NEVER merge.** Recipe PR (and any cc-ci test PR) are operator-merged after review.
- **Mirror reflects reality.** Each run force-syncs the `recipe-maintainers/<recipe>` mirror `main` to
true upstream main, closes open PRs already merged upstream, and replaces any superseded open PR with
the new one — so an open mirror PR always means "genuinely still open against current upstream main".
- **Prefer a recipe-only PR.** Only open a cc-ci test PR when the upgrade is correct but a test is
genuinely stale — not to paper over a real upgrade regression.
- **Never weaken a test**; **bounded** changes (the upgrade + minimal test update, not rewrites).