`_marker_only_restart_obsolete` bailed out with "a newer pull moved HEAD" whenever `checkout_sha != expected_sha`, and otherwise held the fleet to `expected_sha` exactly. A cherry-picked hotfix on top of the pulled SHA moves HEAD without any pull arming a fresh obligation, so the recorded one could never discharge: every CLI start warned and every no-op `hermes update` exited 1 through the armed veto in `_pending_fleet_restart_needed`, while the gateway verifiably served HEAD. The bail-out now fires only when HEAD no longer CONTAINS `expected_sha` (`checkout_contains`, `git merge-base --is-ancestor`, fail-closed on any probe failure), and the live fleet is held to the checkout SHA — the code it actually runs. A gateway on genuinely stale code still keeps the obligation armed. Closes #119367
31 lines
1.1 KiB
Python
31 lines
1.1 KiB
Python
"""Checkout ancestry for the update-restart obligation (``update_cmd_fleet`` sibling).
|
|
|
|
The obligation records the SHA a pull landed on; the checkout may later sit past it by a carried
|
|
local commit (a cherry-picked hotfix). Whether that recorded SHA is still *contained* in HEAD is
|
|
the question these readers ask, so the live fleet can be held to the code on disk (#119367).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import subprocess
|
|
|
|
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.
|
|
|
|
Fail-closed on purpose: an unknown ancestry is not evidence that the fleet serves the update.
|
|
"""
|
|
from hermes_cli.update_cmd import _m
|
|
try:
|
|
result = subprocess.run(
|
|
["git", "merge-base", "--is-ancestor", sha, "HEAD"],
|
|
cwd=_m().PROJECT_ROOT, capture_output=True, text=True, timeout=10,
|
|
)
|
|
return result.returncode == 0
|
|
except Exception as exc:
|
|
logger.debug("Checkout ancestry probe for %s failed: %s", sha[:10], exc)
|
|
return False
|