diff --git a/runner/run_recipe_ci.py b/runner/run_recipe_ci.py index 1987446..745e9e8 100644 --- a/runner/run_recipe_ci.py +++ b/runner/run_recipe_ci.py @@ -921,30 +921,36 @@ def should_promote_canonical( return canonical.is_enrolled(recipe) and overall == 0 and not quick and not ref and tagged -def promote_canonical(recipe: str, head_ref: str | None) -> None: - """WC5: (re)seed the canonical at the green-verified LATEST. Deploy `warm-` at latest - (reattaching the retained canonical volume if one exists — an in-place version bump — else a fresh - install), wait healthy, undeploy, snapshot + record the registry (atomic replace of the - last-known-good). The OLD known-good is replaced ONLY here, after green (never lost on a red run).""" +def promote_canonical(recipe: str, head_ref: str | None, version: str | None) -> None: + """canon §2.A / WC5: (re)seed the canonical at the GREEN-VERIFIED TESTED RELEASE `version` — the + exact version under test (head_version), which the should_promote tagged-gate guarantees is a + published release tag. Deploy `warm-` at that version (reattaching the retained canonical + volume if one exists — an in-place version bump — else a fresh install), wait healthy, undeploy, + snapshot + record the registry (atomic replace of the last-known-good). + + Promotes EXACTLY the tested version — it no longer re-derives `latest_version(recipe_tags)`, which + could differ from the version actually exercised by the run (e.g. a manual `RECIPE=` run whose + `main` checkout sits on a tag older than the newest published tag): the canonical must record the + version the tier suite proved green, not a never-tested newer tag. The OLD known-good is replaced + ONLY here, after green (never lost on a red run).""" import warm_reconcile as wr domain = canonical.canonical_domain(recipe) - wr.fetch_recipe(recipe) - latest = wr.latest_version(wr.recipe_tags(recipe)) - if not latest: - print(f"WC5 promote: no version tags for {recipe} — skip", flush=True) + if not version: + print(f"WC5 promote: no tested release version for {recipe} — skip", flush=True) return + wr.fetch_recipe(recipe) # ensure the release tag is present locally for the pinned checkout meta = meta_mod.load(recipe) # The cold run's deploy-count was already asserted + the countfile removed; don't perturb it. os.environ.pop("CCCI_DEPLOY_COUNT_FILE", None) print( - f"\n===== WC5 promote-on-green-cold: (re)seed canonical {recipe} @ {latest} =====", + f"\n===== WC5 promote-on-green-cold: (re)seed canonical {recipe} @ {version} =====", flush=True, ) lifecycle.deploy_app( recipe, domain, - version=latest, + version=version, secrets=True, deploy_timeout=int(meta.DEPLOY_TIMEOUT), meta=meta, @@ -958,9 +964,9 @@ def promote_canonical(recipe: str, head_ref: str | None) -> None: ) abra.undeploy(domain) _wait_undeployed(domain) - canonical.seed_canonical(recipe, latest, commit=head_ref) + canonical.seed_canonical(recipe, version, commit=head_ref) print( - f"WC5 promote: canonical {recipe} advanced to known-good {latest} (idle, volume retained)", + f"WC5 promote: canonical {recipe} advanced to known-good {version} (idle, volume retained)", flush=True, ) @@ -1494,7 +1500,7 @@ def main() -> int: tagged = warm_reconcile.is_released_version(recipe, head_version) if should_promote_canonical(recipe, ref, overall, quick=False, tagged=tagged): try: - promote_canonical(recipe, head_ref) + promote_canonical(recipe, head_ref, head_version) except Exception as e: # noqa: BLE001 — promote is a post-green bonus; never fail a green run print( f"!! WC5 promote failed (non-fatal; known-good unchanged): {_scrub(str(e))}",