installer v1.23.0 — publishing becomes an act, not a side-effect (R-110, R-183)
gates / gates (push) Successful in 8s
gates / gates (push) Successful in 8s
Two channels moved off main in the same change, because either one left behind makes the other cosmetic. Channel 1 — the served script. webpage.yaml git-synced /scripts/ from --branch=main every 30s and nginx served that tree, so pushing this file WAS publishing it: within half a minute it was what every new machine downloaded and ran as root, with no staging and no rollback but another push. The sync is now SPLIT: the website keeps tracking main at the same cadence (a copy edit must never need a release) and /scripts/ tracks the tag installer-v<SCRIPT_VERSION>. PROVEN before the manifest was touched: git-sync v4.4.0 follows a tag AND notices a MOVED one — measured on a throwaway sync against this repo, "update required ... local:<old> remote:<new>" -> "updated successfully", within one period. The moved-tag half is what the publish model rests on. Channel 2 — the sixteen files fetched at run time. fetch_raw pulled from $AGENT_REPO/raw/branch/main; it now pulls raw/tag/v$ART_AGENT_VER. That is a correctness fix, not only a channel one (R-183): a fresh install fetched the vouched agent BINARY while taking its unit file, sudoers and guarded wrappers from whatever main held. Two refs, one install, nothing compared them. Their correct ref was never SCRIPT_VERSION — they do not live in this repo. No fallback to a branch: a vouched version whose tag is missing fails loudly rather than quietly serving main. Channel 3 — the URL — needed no change, recorded rather than left silent: https://felhom.eu/scripts/felhom-host-install.sh never carried a ref, so both producers follow the tag with no edit. No hub change, no hub version bump. Gate 6 in hostinstall_gates.py pins all three structurally with no network, so it stays in --fast and runs in CI. It deliberately does NOT assert "a tag exists for the current SCRIPT_VERSION": that would go red on the very push that bumps the version, before publishing — and publishing being separate is the ruling.
This commit is contained in:
@@ -146,6 +146,62 @@ if re.search(r'^PVE_STORAGES=\([^)]*felhom-pbs[^)]*\)', src, re.M):
|
||||
else:
|
||||
fail("felhom-pbs missing from the default PVE_STORAGES — narrowing it 403s the PBS-DR apply-bridge")
|
||||
|
||||
# ── 6. the publish channel is pinned, not floating (R-110 / R-183) ──────────────
|
||||
#
|
||||
# WHAT THIS ASSERTS, AND WHAT IT DELIBERATELY DOES NOT.
|
||||
#
|
||||
# It does NOT assert "a tag exists for the current SCRIPT_VERSION". That gate would fail the very
|
||||
# push that bumps SCRIPT_VERSION, before publishing has happened — and publishing being a SEPARATE
|
||||
# deliberate act is the whole point of R-110's ruling. A gate that goes red on the normal path is a
|
||||
# gate people learn to ignore, which is the reasoning the task's own §8.4 applies to the agent-side
|
||||
# gate; it applies here identically. "Is the vouched version actually downloadable" is a real
|
||||
# invariant and it lives where a missing artifact genuinely breaks day-0 — `felhom-agent`'s
|
||||
# `agent_gates.py`, which has the network access to answer it.
|
||||
#
|
||||
# What it asserts instead are the two STRUCTURAL regressions that would silently return the
|
||||
# installer to a floating channel, both answerable by reading files (no network, so this stays in
|
||||
# `--fast` and therefore runs in CI on every push):
|
||||
#
|
||||
# 6a. no `raw/branch/` ref anywhere in the installer — one of the sixteen agent-config fetches
|
||||
# slipping back to `main` is exactly how a channel stays floating unnoticed, and it is
|
||||
# invisible in a diff that touches one line.
|
||||
# 6b. `fetch_raw` still pins to the resolved agent version — the positive form, so the mechanism
|
||||
# cannot be quietly deleted rather than regressed.
|
||||
# 6c. the website manifest still syncs `/scripts/` from a TAG ref and the website from `main` —
|
||||
# the split is the deploy-side half of the same channel, and reverting it is one word.
|
||||
branch_refs = [l for l in lines if "raw/branch/" in l and not l.lstrip().startswith("#")]
|
||||
if branch_refs:
|
||||
fail("installer still fetches from a BRANCH ref — the run-time channel is floating again "
|
||||
"(R-110/R-183). Offending line(s): %s" % "; ".join(l.strip()[:90] for l in branch_refs))
|
||||
else:
|
||||
ok("no raw/branch/ ref in the installer — every run-time fetch is pinned")
|
||||
|
||||
if re.search(r'raw/tag/v\$ART_AGENT_VER/', src):
|
||||
ok("fetch_raw pins the agent configs to the vouched agent version")
|
||||
else:
|
||||
fail("fetch_raw no longer pins to $ART_AGENT_VER — the agent's configs and its binary can "
|
||||
"again come from different refs in one install (R-183)")
|
||||
|
||||
WEBPAGE = os.path.join(ROOT, "manifests", "webpage.yaml")
|
||||
try:
|
||||
with io.open(WEBPAGE, "r", encoding="utf-8") as f:
|
||||
wp = f.read()
|
||||
except IOError as e:
|
||||
fail("cannot read manifests/webpage.yaml to check the publish channel: %s" % e)
|
||||
wp = None
|
||||
if wp is not None:
|
||||
# The scripts sync must name a tag ref; the website sync must still track main.
|
||||
if re.search(r'--ref=installer-v', wp):
|
||||
ok("manifest: /scripts/ syncs from an installer tag")
|
||||
else:
|
||||
fail("manifests/webpage.yaml has no `--ref=installer-v…` sync — /scripts/ is not served "
|
||||
"from a tag, so pushing the installer publishes it again (R-110)")
|
||||
if re.search(r'--(branch|ref)=main', wp):
|
||||
ok("manifest: the website still tracks main (a copy edit must not need a release)")
|
||||
else:
|
||||
fail("manifests/webpage.yaml no longer tracks main for the website — pinning the SITE to "
|
||||
"the installer tag turns every copy edit into a release")
|
||||
|
||||
print()
|
||||
if fails:
|
||||
print("hostinstall gates: %d FAILURE(S)" % len(fails))
|
||||
|
||||
Reference in New Issue
Block a user