reconcile-upstream.sh: one deterministic entry point, mandated before PR work
Working against a stale mirror has cost us three different ways: - mailu #6 was linked as the fix for two internet-facing Roundcube CVEs while upstream had already merged AND released it (3.1.3+2024.06.57). The work was done; only our mirror was behind. Reconciling closed the PR automatically. - a stale mirror makes a survey report 'no upgrades available', so the recipe silently drops out of the weekly run. - reading the wrong branch: several coopcloud recipes keep a stale 'main' beside the real default 'master'. gitea's main is 1.24.2-rootless while master has 1.27.1-rootless and the merged PRs, so reading main manufactures a false 'three releases behind, missing two CVSS-9.8 RCEs' finding. The reconcile logic already existed inside open-recipe-pr.sh --reconcile-only and already resolves the default branch itself. What was missing was a single obvious entry point and a rule saying to run it. reconcile-upstream.sh takes recipes or --all, and is idempotent — recipe work lives in branches, never on mirror main, so force-syncing main discards nothing. /ci-test-review and /cc-ci-tests-update had NO reconcile step at all; both now require it. /cve-check, /recipe-upgrade and /upgrade-all already reconciled and now point at the shared script.
This commit is contained in:
@@ -89,6 +89,19 @@ For each real (non-flaky) finding, write the actual fix and open a PR. **Never m
|
|||||||
branch in a SEPARATE clone — **never push `main`, never touch the build loops' working clones**
|
branch in a SEPARATE clone — **never push `main`, never touch the build loops' working clones**
|
||||||
(`/cc-ci`, `/cc-ci-adv`) or their in-flight state.
|
(`/cc-ci`, `/cc-ci-adv`) or their in-flight state.
|
||||||
|
|
||||||
|
> ### ⚠️ RECONCILE FROM UPSTREAM FIRST — always, before any PR work or upgrade check
|
||||||
|
> ```
|
||||||
|
> cc-ci-plan/reconcile-upstream.sh <recipe>... # or --all
|
||||||
|
> ```
|
||||||
|
> Deterministic, idempotent, and safe (recipe work lives in branches, never on mirror `main`). It
|
||||||
|
> force-syncs each mirror to coopcloud's **default branch — resolved from the API, `main` OR
|
||||||
|
> `master`** — and closes any mirror PR whose changes upstream already merged. Skipping it has cost
|
||||||
|
> us three distinct ways: mailu #6 was reported as the fix for two internet-facing CVEs while
|
||||||
|
> upstream had already merged AND released it; a stale mirror makes a survey report "no upgrades
|
||||||
|
> available" so the recipe drops out of the weekly run; and reading the wrong branch on a recipe with
|
||||||
|
> a stale `main` beside a live `master` (gitea) manufactures a false "three releases behind, missing
|
||||||
|
> two CVSS-9.8 RCEs" finding.
|
||||||
|
|
||||||
### 5. VERIFY each PR on the CI server (deterministic; still never merge)
|
### 5. VERIFY each PR on the CI server (deterministic; still never merge)
|
||||||
A PR is only "working" once **cc-ci verifies it green** (operator rule) — dogfood the CI that found
|
A PR is only "working" once **cc-ci verifies it green** (operator rule) — dogfood the CI that found
|
||||||
the bug. Verification is deterministic (the harness), not an AI judgement.
|
the bug. Verification is deterministic (the harness), not an AI judgement.
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ This is `/recipe-upgrade` step 1's research, stopping before it implements anyth
|
|||||||
> vectorchord/pgvectors versions immich-server is built against, so taking the newest would break
|
> vectorchord/pgvectors versions immich-server is built against, so taking the newest would break
|
||||||
> the deploy. `all_resolved: false` means an image could NOT be resolved — that is a `?`, never a 0.
|
> the deploy. `all_resolved: false` means an image could NOT be resolved — that is a `?`, never a 0.
|
||||||
|
|
||||||
**Reconcile the mirror from true upstream FIRST — ALWAYS, no exceptions.** This is the same reconcile
|
**Reconcile the mirror from true upstream FIRST — ALWAYS, no exceptions** — one command,
|
||||||
|
`cc-ci-plan/reconcile-upstream.sh <recipe>... | --all`. This is the same reconcile
|
||||||
`/upgrade-all` does. Do not skip it in the name of keeping the sweep read-only: skipping it makes you
|
`/upgrade-all` does. Do not skip it in the name of keeping the sweep read-only: skipping it makes you
|
||||||
research a stale checkout, and on the first real run that produced **two recipes with no survey output
|
research a stale checkout, and on the first real run that produced **two recipes with no survey output
|
||||||
at all**, which is indistinguishable from "no upgrades" unless you check. It is safe — recipe work
|
at all**, which is indistinguishable from "no upgrades" unless you check. It is safe — recipe work
|
||||||
|
|||||||
@@ -83,6 +83,19 @@ failure (AI — this is the `ci-test-review` step-3 diagnosis):
|
|||||||
changed upstream, what the test currently asserts.
|
changed upstream, what the test currently asserts.
|
||||||
- **FLAKY** → re-run once or twice; if it passes, drop it (not stale, just flaky).
|
- **FLAKY** → re-run once or twice; if it passes, drop it (not stale, just flaky).
|
||||||
|
|
||||||
|
> ### ⚠️ RECONCILE FROM UPSTREAM FIRST — always, before any PR work or upgrade check
|
||||||
|
> ```
|
||||||
|
> cc-ci-plan/reconcile-upstream.sh <recipe>... # or --all
|
||||||
|
> ```
|
||||||
|
> Deterministic, idempotent, and safe (recipe work lives in branches, never on mirror `main`). It
|
||||||
|
> force-syncs each mirror to coopcloud's **default branch — resolved from the API, `main` OR
|
||||||
|
> `master`** — and closes any mirror PR whose changes upstream already merged. Skipping it has cost
|
||||||
|
> us three distinct ways: mailu #6 was reported as the fix for two internet-facing CVEs while
|
||||||
|
> upstream had already merged AND released it; a stale mirror makes a survey report "no upgrades
|
||||||
|
> available" so the recipe drops out of the weekly run; and reading the wrong branch on a recipe with
|
||||||
|
> a stale `main` beside a live `master` (gitea) manufactures a false "three releases behind, missing
|
||||||
|
> two CVSS-9.8 RCEs" finding.
|
||||||
|
|
||||||
### 2. For each stale test — author the minimal test update (AI; never weaken)
|
### 2. For each stale test — author the minimal test update (AI; never weaken)
|
||||||
|
|
||||||
> **Read `tests/STYLE.md` in the cc-ci repo before writing the update.** It is the rulebook for test
|
> **Read `tests/STYLE.md` in the cc-ci repo before writing the update.** It is the rulebook for test
|
||||||
|
|||||||
Executable
+64
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# reconcile-upstream — sync recipe mirrors from TRUE upstream. Run this FIRST, always.
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
# Every recipe we maintain is a MIRROR of a coopcloud recipe. Work done against a stale
|
||||||
|
# mirror is wasted or wrong, in three ways we have actually hit:
|
||||||
|
#
|
||||||
|
# 1. A PR whose changes upstream ALREADY MERGED. mailu #6 (2024.06.57 + redis 8.10,
|
||||||
|
# two internet-facing Roundcube CVEs) sat open and was reported as the fix for
|
||||||
|
# those CVEs — while upstream had merged and released it as 3.1.3+2024.06.57. The
|
||||||
|
# work was done; only our mirror was behind.
|
||||||
|
# 2. A survey that reads the stale mirror and reports "no upgrades available", so a
|
||||||
|
# recipe silently drops out of the weekly run.
|
||||||
|
# 3. Reading the WRONG BRANCH. Several coopcloud recipes keep a stale `main` beside
|
||||||
|
# the real default `master` — gitea's `main` is at 1.24.2-rootless while `master`
|
||||||
|
# has 1.27.1-rootless plus the merged PRs. Reading `main` there says the recipe is
|
||||||
|
# three releases behind and missing two CVSS-9.8 RCE fixes, which reads exactly
|
||||||
|
# like a real finding. open-recipe-pr.sh resolves the default branch itself
|
||||||
|
# (main OR master) — never hand-pick one.
|
||||||
|
#
|
||||||
|
# This is deterministic: it force-syncs each mirror's `main` to upstream's default
|
||||||
|
# branch and closes any mirror PR whose changes are already upstream. No AI judgement.
|
||||||
|
#
|
||||||
|
# reconcile-upstream.sh <recipe>... # specific recipes
|
||||||
|
# reconcile-upstream.sh --all # every recipe in used-recipes.md
|
||||||
|
#
|
||||||
|
# Safe to run repeatedly; a mirror already in sync is a no-op. Recipe work lives in
|
||||||
|
# BRANCHES, never on mirror `main`, so force-syncing `main` discards nothing.
|
||||||
|
set -o errexit -o nounset -o pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ORCH="$(dirname "$HERE")"
|
||||||
|
SSH="${SSH:-cc-ci}"
|
||||||
|
TESTENV="${TESTENV:-/srv/cc-ci/.testenv}"
|
||||||
|
RECONCILE="${RECONCILE:-$ORCH/.claude/skills/recipe-upgrade/open-recipe-pr.sh}"
|
||||||
|
USED_RECIPES="${USED_RECIPES:-$HERE/used-recipes.md}"
|
||||||
|
|
||||||
|
[ -f "$RECONCILE" ] || { echo "ERROR: reconcile helper not found: $RECONCILE" >&2; exit 1; }
|
||||||
|
set -a; . "$TESTENV"; set +a
|
||||||
|
: "${GITEA_USERNAME:?}"; : "${GITEA_PASSWORD:?}"; : "${GITEA_URL:?}"
|
||||||
|
|
||||||
|
if [ "${1:-}" = "--all" ]; then
|
||||||
|
mapfile -t RECIPES < <(awk '!/^[[:space:]]*#/ && ($2=="weekly" || $2=="external") {print $1}' "$USED_RECIPES")
|
||||||
|
else
|
||||||
|
[ "$#" -gt 0 ] || { echo "usage: reconcile-upstream.sh <recipe>... | --all" >&2; exit 2; }
|
||||||
|
RECIPES=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
synced=0; closed=0; failed=0
|
||||||
|
for r in "${RECIPES[@]}"; do
|
||||||
|
echo "── $r"
|
||||||
|
if out="$(ssh "$SSH" "GITEA_USERNAME='$GITEA_USERNAME' GITEA_PASSWORD='$GITEA_PASSWORD' GITEA_URL='$GITEA_URL' bash -s $r --reconcile-only" < "$RECONCILE" 2>&1)"; then
|
||||||
|
printf '%s\n' "$out" | grep -E "Force-syncing|already in sync|closed PR|still open|✓" | sed 's/^/ /' || true
|
||||||
|
synced=$((synced + 1))
|
||||||
|
closed=$((closed + $(printf '%s' "$out" | grep -c "closed PR" || true)))
|
||||||
|
else
|
||||||
|
printf '%s\n' "$out" | tail -3 | sed 's/^/ /'
|
||||||
|
echo " ✗ FAILED — do NOT proceed against this mirror until it reconciles"
|
||||||
|
failed=$((failed + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "reconcile-upstream: ${synced} mirror(s) synced, ${closed} already-upstream PR(s) closed, ${failed} failed"
|
||||||
|
[ "$failed" -eq 0 ]
|
||||||
Reference in New Issue
Block a user