fix(update): checkout_contains reads the build stamp on a no-.git image, so the pending-restart catch-up stops contradicting itself
On a Docker / Hermes Cloud image there is no .git; the code identity is the baked build stamp (build_info.get_code_identity, source == "build-file"). checkout_contains walked git merge-base regardless and was therefore always False on an image, which made _marker_only_restart_obsolete and _live_fleet_covers_receipt disagree with the fleet matrix: the update catch-up printed "Every running gateway already serves the checkout code" and "gateways are still off the checkout code" in the same run (found in the s6 update-tail audit for #120516). With no history to walk, contained collapses to equal-to-the-stamp (either side may be the short form an older writer recorded). A source install keeps asking git, so a carried hotfix past the pulled SHA still counts (#119367). Red on main / green here: tests/hermes_cli/test_update_fleet_checkout_build_stamp.py
This commit is contained in:
@@ -14,11 +14,23 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def checkout_contains(sha: str) -> bool:
|
||||
"""True when ``sha`` is an ancestor of (or equal to) the checkout HEAD; False on any probe failure.
|
||||
"""True when ``sha`` is an ancestor of (or equal to) the code this checkout runs; False on any
|
||||
probe failure.
|
||||
|
||||
Fail-closed on purpose: an unknown ancestry is not evidence that the fleet serves the update.
|
||||
|
||||
A Docker/Cloud image has no ``.git``; its identity is the baked build stamp
|
||||
(``build_info.get_code_identity`` → ``source == "build-file"``). There is no history to walk, so
|
||||
"contained" collapses to "equal to the stamp" — without this the probe was always False on an
|
||||
image and the pending-restart catch-up printed "every gateway serves the checkout" and "still
|
||||
off the checkout code" in the same breath.
|
||||
"""
|
||||
from hermes_cli.build_info import get_code_identity
|
||||
from hermes_cli.update_cmd import _m
|
||||
identity = get_code_identity() or {}
|
||||
if identity.get("source") == "build-file":
|
||||
stamped = str(identity.get("sha") or "")
|
||||
return bool(stamped) and (stamped == sha or stamped.startswith(sha) or sha.startswith(stamped))
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "merge-base", "--is-ancestor", sha, "HEAD"],
|
||||
|
||||
36
tests/hermes_cli/test_update_fleet_checkout_build_stamp.py
Normal file
36
tests/hermes_cli/test_update_fleet_checkout_build_stamp.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""``checkout_contains`` on a build-stamped image (no ``.git``): ancestry collapses to stamp equality.
|
||||
|
||||
On a Docker/Cloud image ``git merge-base`` has nothing to walk, so the probe was always False and the
|
||||
pending-restart catch-up printed "every gateway already serves the checkout" and "still off the checkout
|
||||
code" in the same run. The stamp IS the checkout there.
|
||||
"""
|
||||
|
||||
from hermes_cli import update_cmd_fleet_checkout as chk
|
||||
|
||||
|
||||
def _identity(monkeypatch, sha, source):
|
||||
import hermes_cli.build_info as bi
|
||||
monkeypatch.setattr(bi, "get_code_identity", lambda refresh=False: {"sha": sha, "short_sha": sha[:8], "source": source})
|
||||
|
||||
|
||||
def test_build_stamped_image_contains_exactly_its_stamp(monkeypatch):
|
||||
stamped = "b936546561aa0d2e6d0f7c3d1a9c5e8f2b4d6a70"
|
||||
_identity(monkeypatch, stamped, "build-file")
|
||||
# no git call may be attempted on an image: make one blow up if it is
|
||||
monkeypatch.setattr(chk.subprocess, "run", lambda *a, **k: (_ for _ in ()).throw(AssertionError("git probe on a build-stamped image")))
|
||||
assert chk.checkout_contains(stamped) is True
|
||||
assert chk.checkout_contains(stamped[:12]) is True # short form recorded by an older writer
|
||||
assert chk.checkout_contains("0000000000aa0d2e6d0f7c3d1a9c5e8f2b4d6a70") is False
|
||||
|
||||
|
||||
def test_git_checkout_still_walks_ancestry(monkeypatch):
|
||||
"""Control: a source install keeps asking git, so a carried hotfix past the pulled SHA still counts."""
|
||||
_identity(monkeypatch, "deadbeef" * 5, "git")
|
||||
calls = []
|
||||
|
||||
class _R:
|
||||
returncode = 0
|
||||
|
||||
monkeypatch.setattr(chk.subprocess, "run", lambda cmd, **k: calls.append(cmd) or _R())
|
||||
assert chk.checkout_contains("cafebabe" * 5) is True
|
||||
assert calls and calls[0][:3] == ["git", "merge-base", "--is-ancestor"]
|
||||
Reference in New Issue
Block a user