feat(bsky): EXPECTED_NA['upgrade'] suppresses the upgrade-tier base deploy — single deploy = PR head; bluesky-pds declares it (no deployable base: every published tag pins the republished moving :0.4). upgrade_base() extracted pure + 6 unit tests; meta-key doc regenerated. 253 unit tests + repo lint PASS
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing

This commit is contained in:
2026-06-11 11:51:12 +00:00
parent f88c6bc78d
commit e9745c8c74
5 changed files with 130 additions and 14 deletions

View File

@ -76,7 +76,7 @@ KEYS: tuple[Key, ...] = (
"EXPECTED_NA",
"dict",
None,
"Declare a non-run rung an INTENTIONAL skip: `{rung: reason}` — the level climbs past it; an undeclared non-run rung is *unverified* and blocks the level above it (classification table: machine-docs/DECISIONS.md phase lvl5). Never overrides an exercised pass/fail; the `lint` rung has no escape hatch.",
"Declare a non-run rung an INTENTIONAL skip: `{rung: reason}` — the level climbs past it; an undeclared non-run rung is *unverified* and blocks the level above it (classification table: machine-docs/DECISIONS.md phase lvl5). Never overrides an exercised pass/fail; the `lint` rung has no escape hatch. Declaring `upgrade` also suppresses the upgrade-tier BASE deploy — the single deploy is the PR head itself — for recipes whose published versions exist but are genuinely undeployable (phase bsky).",
),
Key(
"READY_PROBE",

View File

@ -88,6 +88,38 @@ def sso_dep_unverified(declared, deps_ready: bool, requires_deps_skipped: int) -
return bool(declared) and not deps_ready and requires_deps_skipped > 0
def upgrade_base(stages, meta, recipe: str) -> str | None:
"""Deploy-once base version decision (pure given meta + the published-version lookup):
previous published version when the upgrade tier will run and one exists (so upgrade goes
previous→target in place), else None (the caller falls back to the target / PR head).
(DECISIONS.)
A recipe may override the base via recipe_meta UPGRADE_BASE_VERSION when the harness default
(recipe_versions[-2]) is NOT the PR's true predecessor — e.g. a PR that adds a version ABOVE the
newest published tag, where the correct base is [-1] (the newest published), not [-2]. The
override must be an exact published version tag (deployed as a pinned base). (Adversary §7.1.)
A recipe that declares the upgrade rung in EXPECTED_NA gets NO base: published versions may
exist yet be genuinely undeployable — e.g. bluesky-pds, where every published tag pins the
moving image tag `:0.4` that upstream republished with incompatible main builds, so no
published version can come up as an upgrade base (phase bsky, DECISIONS). Deploying one would
fail the INSTALL tier before the PR-head code is ever exercised. With no base, the single
deploy is the PR head itself and the upgrade tier records "skip", which derive_rungs
classifies as the DECLARED intentional skip (reason from EXPECTED_NA — visible in
results.json `skips.intentional`, never reported as a pass)."""
if "upgrade" not in stages:
return None
if "upgrade" in (meta.EXPECTED_NA or {}):
print(
"== upgrade tier: declared EXPECTED_NA['upgrade'] — no upgrade base will be "
f"deployed; the single deploy is the target/PR head. Reason: "
f"{(meta.EXPECTED_NA or {}).get('upgrade')}",
flush=True,
)
return None
return meta.UPGRADE_BASE_VERSION or lifecycle.previous_version(recipe)
def _truthy(v: str | None) -> bool:
return str(v or "").strip().lower() in ("1", "true", "yes", "on")
@ -905,16 +937,7 @@ def main() -> int:
domain = naming.app_domain(recipe, os.environ.get("PR", "0"), ref)
# Deploy-once base version: previous published version when the upgrade tier will run and one
# exists (so upgrade goes previous→target in place), else the target (current/$REF). (DECISIONS.)
# A recipe may override the base via recipe_meta UPGRADE_BASE_VERSION when the harness default
# (recipe_versions[-2]) is NOT the PR's true predecessor — e.g. a PR that adds a version ABOVE the
# newest published tag, where the correct base is [-1] (the newest published), not [-2]. The
# override must be an exact published version tag (deployed as a pinned base). (Adversary §7.1.)
want_upgrade = "upgrade" in stages
prev = (
(meta.UPGRADE_BASE_VERSION or lifecycle.previous_version(recipe)) if want_upgrade else None
)
prev = upgrade_base(stages, meta, recipe)
base = prev or target
backup_cap = generic.backup_capable(recipe, meta)
hook = discovery.install_steps(recipe, repo_local)
@ -1091,7 +1114,7 @@ def main() -> int:
junit_dir=junit_dir,
)
if prev
else "skip" # only one published version → nothing to upgrade from
else "skip" # no upgrade base: single published version, or declared EXPECTED_NA
)
# ---- BACKUP + RESTORE tiers (backup-capable only; else clean N/A) ----
if "backup" in stages:
@ -1274,7 +1297,7 @@ def main() -> int:
records=records,
results=results,
backup_capable=backup_cap,
has_upgrade_target=prev is not None, # structural: a previous published version exists
has_upgrade_target=prev is not None, # structural: a deployable upgrade base exists
lint=lint_result, # L5 rung (phase lvl5)
clean_teardown=clean_teardown,
no_secret_leak=True, # narrowed below by an actual scan of the serialised artifact