merge-to-master: a landed master is pushed to both box mirrors (the stale-mirror class: build-2's /srv/igneum.git master sat at 15:27's 4e2745ab while GitHub carried 2b9d50cc, 7 October 2026, 18:25 UTC; three branches were cut from the stale tip)
mirror_master runs after the push to origin: one fast-forward push per mirror in IGNEUM_MIRRORS (default: the two box files' hosts at /srv/igneum.git), best effort, a line per mirror, a down or diverged mirror never fails the landing. Self-test: a bare mirror behind master is fast-forwarded; a non-fast-forward push leaves it and is reported. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
74bb841dfe
commit
a15c1381f5
1 changed files with 26 additions and 2 deletions
|
|
@ -55,6 +55,19 @@ master_gate() {
|
|||
esac
|
||||
}
|
||||
|
||||
# After a landing, master goes to the box mirrors too (7 October 2026, 18:25 UTC: build-2's /srv/igneum.git master sat at 15:27's
|
||||
# a4bca198 while GitHub's carried cf7d6ccb, and three branches were cut from the stale tip; the mirrors only ever received what a
|
||||
# build happened to push). One push per mirror in IGNEUM_MIRRORS (default: both box files), fast-forward only, best effort: a mirror
|
||||
# that is down prints a line and never fails the landing.
|
||||
mirror_master() { # <sha>
|
||||
local sha="$1" m host key="${BS_KEY:-$HOME/.ssh/igneum_ed25519}" list="${IGNEUM_MIRRORS:-}"
|
||||
if [ -z "$list" ]; then for f in "$HOME/.config/igneum/build-server" "$HOME/.config/igneum/build-server-2"; do [ -s "$f" ] && list="$list $(head -1 "$f" | tr -d '[:space:]'):/srv/igneum.git"; done; fi
|
||||
for m in $list; do
|
||||
if GIT_SSH_COMMAND="ssh -i $key -o BatchMode=yes -o ConnectTimeout=10" git push -q "$m" "$sha:refs/heads/master" 2>/dev/null; then echo "merge-to-master: mirror $m master -> ${sha:0:8}"
|
||||
else echo "merge-to-master: mirror $m did not take master ${sha:0:8} (down, or not a fast-forward); the next landing tries again"; fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$SELF_TEST" = 1 ]; then
|
||||
fails=0; d=$(mktemp -d); fake="$d/ci-state.sh"
|
||||
# the fake answers from $d/answer-<sha> (one line per call, consumed top to bottom; the last line repeats) and $d/answer-master
|
||||
|
|
@ -102,8 +115,18 @@ success 4 u push run
|
|||
FIXES_MASTER=0 master_gate >/dev/null || { echo "self-test failed: an unknown master read that turned green refused a merge"; fails=1; }
|
||||
printf 'unknown - - gh: exit 1\n' > "$d/answer-master"; rm -f "$d/i-master"
|
||||
FIXES_MASTER=0 master_gate >/dev/null 2>&1 && { echo "self-test failed: a lasting unknown master read let a merge through"; fails=1; }
|
||||
# the mirror push: a bare repository behind master is fast-forwarded; one that is not a fast-forward is left alone with a line
|
||||
mkdir -p "$d/src" && ( cd "$d/src" && git init -q -b master . && git -c user.name=t -c user.email=t@t commit -q --allow-empty -m one && git -c user.name=t -c user.email=t@t commit -q --allow-empty -m two ) 2>/dev/null
|
||||
git clone -q --bare "$d/src" "$d/mirror.git" && ( cd "$d/mirror.git" && git update-ref refs/heads/master "$(git rev-parse master~1)" )
|
||||
tip=$(git -C "$d/src" rev-parse master)
|
||||
out=$( cd "$d/src" && IGNEUM_MIRRORS="$d/mirror.git" mirror_master "$tip" )
|
||||
[ "$(git -C "$d/mirror.git" rev-parse master)" = "$tip" ] || { echo "self-test failed: the mirror was not fast-forwarded to the landed master: $out"; fails=1; }
|
||||
( cd "$d/src" && git checkout -q -b other master~1 && git -c user.name=t -c user.email=t@t commit -q --allow-empty -m fork ); fork=$(git -C "$d/src" rev-parse other)
|
||||
out=$( cd "$d/src" && IGNEUM_MIRRORS="$d/mirror.git" mirror_master "$fork" )
|
||||
[ "$(git -C "$d/mirror.git" rev-parse master)" = "$tip" ] || { echo "self-test failed: a non-fast-forward push moved the mirror's master"; fails=1; }
|
||||
case "$out" in *"did not take master"*) ;; *) echo "self-test failed: the refused mirror push was not reported: $out"; fails=1 ;; esac
|
||||
rm -rf "$d"
|
||||
[ "$fails" = 0 ] && echo "self-test passed: a green branch run merges; a red one is refused; an unknown read is waited through and refused only at the deadline; a queued run is waited for with the clock; an unrun branch is pushed once and then waited for; a red master refuses every merge but the declared fix"
|
||||
[ "$fails" = 0 ] && echo "self-test passed: a landed master is fast-forwarded to every mirror and a refused mirror push is a line, not a failure; a green branch run merges; a red one is refused; an unknown read is waited through and refused only at the deadline; a queued run is waited for with the clock; an unrun branch is pushed once and then waited for; a red master refuses every merge but the declared fix"
|
||||
exit $fails
|
||||
fi
|
||||
[ -z "$(git status --porcelain --untracked-files=no)" ] || { echo "merge-to-master: the tree has uncommitted tracked changes; commit first" >&2; exit 1; }
|
||||
|
|
@ -125,7 +148,8 @@ for i in $(seq 1 "$TRIES"); do
|
|||
if ( cd "$W" && git "${AUTHOR[@]}" merge -q --no-ff -m "Merge $BRANCH ${SHA:0:8} into master (gate: green on ${SHA:0:8}, recorded by tools/ci/pre-push.sh; the full gate runs in CI on this merge)" "$SHA" ); then
|
||||
if ( cd "$W" && git push -q origin HEAD:master ); then # the hook asks ci-state about ${SHA:0:8} once more on this push
|
||||
git worktree remove --force "$W"; git fetch -q origin master
|
||||
echo "merge-to-master: pushed on try $i: origin/master $(git log -1 --format='%h %ci' origin/master) $(TZ=Europe/London date '+%H:%M %Z')"; exit 0
|
||||
echo "merge-to-master: pushed on try $i: origin/master $(git log -1 --format='%h %ci' origin/master) $(TZ=Europe/London date '+%H:%M %Z')"
|
||||
mirror_master "$(git rev-parse origin/master)"; exit 0
|
||||
fi
|
||||
echo "merge-to-master: try $i: the push was rejected (master moved or the hook was red); again"
|
||||
else
|
||||
|
|
|
|||
Loading…
Reference in a new issue