Audit fixes: a compat verdict must never be able to brick a working box
The audit found the new machinery could do more harm than the bugs it prevents. - apply.sh reused the 'nothing left to do' exit -- which touches the PERMANENT kill switch, cleared only by install.sh, never by update.sh -- for the incompatible case. On TrueNAS 26 (providers ok, nested opt-out) both modules go quiet, so the switch would fire and the release that fixed 26 could never re-enable itself. Retirement and incompatibility now take different exits. - A network blip, a re-export, or a conditional def all read as BROKEN. Each is now 'unknown', which changes nothing, rather than evidence strong enough to disable a module. - 'native' outranked BROKEN everywhere but apply.sh, so a TrueNAS that reworded the guard AND reshaped the functions rendered as good news. - compat.py --tree read B2_BLOCK's own 'restic = True' as native support, so the documented way to check a live box lied on every patched machine. - The signature check was a name-subset test. It passed reorders, kw-only conversions, and added required params -- and it had already passed a real bug: restic_backup takes 4 args on 24.10/25.04, and the wrapper forwarded 5. Nested backups have been raising TypeError on those releases the whole time. The wrapper now forwards *args/**kwargs. - release.sh --promote was unreachable: it died if the tag existed, the gate died if it did not. The tests hid it by always tagging first.
This commit is contained in:
+42
-6
@@ -77,9 +77,16 @@ printf '%s' "$target" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' \
|
||||
|
||||
[ -d .git ] || die "not a git checkout"
|
||||
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
die "working tree is dirty. Commit or stash first -- a release must be
|
||||
reproducible from a commit, not from whatever happened to be on disk."
|
||||
# UNTRACKED files count as dirty, because --rc runs `git add -A`: an untracked file
|
||||
# lying around would be swept into the release commit and pushed. This checkout is
|
||||
# routinely shared between sessions, so stray files are the normal state here, not
|
||||
# an exotic one. (Anything genuinely ignorable belongs in .gitignore.)
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo " Working tree is not clean:" >&2
|
||||
git status --short >&2
|
||||
die "commit, stash, or ignore the above first -- a release must be reproducible
|
||||
from a commit, not from whatever happened to be on disk. --rc runs
|
||||
'git add -A', so an untracked file here ships inside the release."
|
||||
fi
|
||||
|
||||
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
@@ -103,6 +110,17 @@ if [ -n "$(git log --oneline "origin/$branch..$branch" 2>/dev/null)" ]; then
|
||||
at a commit the world can actually fetch."
|
||||
fi
|
||||
|
||||
# BEHIND is just as bad as ahead, and less obvious. --rc commits the version stamp,
|
||||
# creates the tag, and only THEN pushes -- so on a stale main the push is rejected
|
||||
# (non-fast-forward) *after* the tag exists and `## Unreleased` has already been
|
||||
# consumed. Re-running then sees rc1, cuts rc2, and silently skips the stamping
|
||||
# step; the rc1 tag dangles locally forever.
|
||||
if [ -n "$(git log --oneline "$branch..origin/$branch" 2>/dev/null)" ]; then
|
||||
die "local main is BEHIND origin. Pull first: git pull --ff-only
|
||||
Releasing from a stale main half-completes: the tag is cut locally, the push
|
||||
is rejected, and '## Unreleased' has already been consumed."
|
||||
fi
|
||||
|
||||
# ── the gates: identical to the ones CI will run ─────────────────────────────
|
||||
|
||||
run_gates() {
|
||||
@@ -167,11 +185,25 @@ fi
|
||||
if [ "$mode" = "rc" ]; then
|
||||
tag="$(python3 tools/release_gate.py "$target" --next-rc -C .)"
|
||||
|
||||
# Tests BEFORE the stamping commit, deliberately.
|
||||
#
|
||||
# Stamping consumes `## Unreleased` and makes a "release vX.Y.Z" commit. If the
|
||||
# suite then failed, that commit was already on main and a re-run died inside
|
||||
# promote() with "no `## Unreleased` content" -- the release was wedged, and the
|
||||
# only way out was to hand-unpick a commit. Failing first leaves the tree
|
||||
# untouched.
|
||||
run_tests
|
||||
|
||||
# The first candidate promotes `## Unreleased` and stamps the version into every
|
||||
# script. Later candidates (rc2+) are re-cuts of an already-stamped version, so
|
||||
# they only tag -- the CHANGELOG section for this version already exists, and
|
||||
# fixes found during rc go into it.
|
||||
if git rev-parse -q --verify "refs/tags/v$target-rc1" >/dev/null; then
|
||||
#
|
||||
# "Already stamped" is decided by the TREE, not by the rc1 tag: if a previous run
|
||||
# stamped and committed but died before tagging (or before pushing), the tag is
|
||||
# absent while the stamp is present, and re-stamping would try to promote an
|
||||
# `## Unreleased` section that is no longer there.
|
||||
if python3 tools/release_notes.py check "v$target-rc0" >/dev/null 2>&1; then
|
||||
note "v$target is already stamped; cutting a follow-up candidate"
|
||||
else
|
||||
note "promoting '## Unreleased' -> v$target and stamping the scripts"
|
||||
@@ -215,8 +247,8 @@ PY
|
||||
|
||||
# Gated as the rc tag it is: content is checked against the base version, and the
|
||||
# provenance gate is a no-op for candidates -- being one is the whole point.
|
||||
# (run_tests already ran, above, before anything was committed.)
|
||||
run_gates "$tag"
|
||||
run_tests
|
||||
|
||||
echo
|
||||
note "about to cut $tag"
|
||||
@@ -227,8 +259,12 @@ PY
|
||||
echo " run: bash release.sh $target --promote"
|
||||
confirm "cut $tag?"
|
||||
|
||||
git tag -a "$tag" -m "$tag"
|
||||
# Push the branch FIRST. If it is rejected, no tag has been created yet -- a tag
|
||||
# pointing at a commit nobody else has is worse than no tag, because the next run
|
||||
# sees it, counts it as a candidate, and cuts rc2 against a commit that was never
|
||||
# published.
|
||||
git push --quiet origin main
|
||||
git tag -a "$tag" -m "$tag"
|
||||
git push --quiet origin "$tag"
|
||||
ok "pushed $tag"
|
||||
echo
|
||||
|
||||
Reference in New Issue
Block a user