upgrade base: prefer the newest published release over a stale canonical #16

Open
autonomic-bot wants to merge 1 commits from test/upgrade-base-prefer-newest-release-20260810 into main
+30
View File
@@ -175,6 +175,36 @@ def resolve_upgrade_base(
flush=True,
)
rec = None
# STALE-CANONICAL GUARD (phase relbase): the canonical is only "last-green", NOT "current". Its
# promotion can fail for reasons unrelated to any PR (the WC5 promote deploys a warm-<recipe> app
# and health-checks it), and a failed promote leaves the canonical pinned at an OLD release
# indefinitely — gitea sat at 3.5.3+1.24.2 from 2026-06-17 to 2026-08-10 because warm-gitea
# crash-looped on a read-only app.ini. Basing the upgrade tier on that stale release tests a
# transition no deployment performs, and can silently MISS breaks in the transition users do
# perform: gitea 3.5.3→head crosses an APP_INI_VERSION change (v21→v22) so Swarm creates a fresh
# config, while the real 3.6.1→3.6.2 upgrade keeps v22 and aborts on Swarm's immutable-config
# rule. Prefer the newest published release older than head whenever it is newer than the
# canonical: that is what real installs upgrade from.
if rec and rec.get("version") and not skip_canonicals and head_version:
_rel_tags = warm_reconcile.recipe_tags(recipe)
if floor:
_rel_tags = [t for t in _rel_tags if not _below_floor(t)]
newest_rel = warm_reconcile.newest_older_version(_rel_tags, head_version)
if newest_rel and warm_reconcile.version_key(newest_rel) > warm_reconcile.version_key(
rec["version"]
):
print(
f"== upgrade base: newest published release {newest_rel} is NEWER than the "
f"last-green canonical {rec['version']} — using the release (what deployments "
f"actually upgrade from); canonical is stale, check its WC5 promote",
flush=True,
)
return BasePlan(
"version",
newest_rel,
None,
f"newest published release older than head (canonical {rec['version']} is stale)",
)
if rec and rec.get("version") and not skip_canonicals:
canon = rec["version"]
same = head_version is not None and warm_reconcile.version_key(