refactor(hermes_cli/update): fold code-identity probes into one helper; fleet matrix row table

This commit is contained in:
Teknium
2026-09-02 23:59:44 -07:00
parent 64262d10b3
commit d9d1858c50

View File

@@ -29,6 +29,15 @@ def _utc_now_iso() -> str:
return datetime.now(timezone.utc).isoformat()
def _code_identity(refresh: bool = False) -> dict[str, Any]:
"""Running-code identity, or ``{}`` when the probe fails."""
with suppress(Exception):
from hermes_cli.build_info import get_code_identity
return get_code_identity(refresh=refresh) or {}
return {}
def _str_records(entries: Any, keys: tuple[str, ...], *, pid: bool = False) -> list[dict[str, Any]]:
"""Dict entries reduced to stringified ``keys`` (plus an int ``pid`` first when requested)."""
records = []
@@ -49,13 +58,9 @@ class UpdateReceipt:
"schema": 1, "started_at": _utc_now_iso(), "finished_at": None,
"argv": list(sys.argv), "pid": os.getpid(),
"outcome": "running", # running | success | partial | failed
"pre_update": {}, "post_update": {},
"pre_update": _code_identity(), "post_update": {},
"steps": [], "skips": [], "gateway_restart": {}, "fleet": [],
}
with suppress(Exception):
from hermes_cli.build_info import get_code_identity
self.data["pre_update"] = get_code_identity()
def step(self, name: str, ok: bool, detail: str = "") -> None:
self.data["steps"].append({"name": name, "ok": bool(ok), "detail": detail, "at": _utc_now_iso()})
@@ -107,10 +112,7 @@ class UpdateReceipt:
def finalize(self, outcome: str) -> None:
self.data["outcome"] = outcome
self.data["finished_at"] = _utc_now_iso()
with suppress(Exception):
from hermes_cli.build_info import get_code_identity
self.data["post_update"] = get_code_identity(refresh=True)
self.data["post_update"] = _code_identity(refresh=True)
def _receipt_dir() -> Path:
@@ -175,8 +177,7 @@ def finalize_update_receipt(outcome: str, fleet: list | None = None, stop_reason
path = directory / f"update_{time.strftime('%Y%m%d_%H%M%S')}_{os.getpid()}.json"
body = json.dumps(receipt.data, indent=2, default=str)
path.write_text(body, encoding="utf-8")
# Stable pointer for the dashboard/desktop: latest receipt.
with suppress(OSError):
with suppress(OSError): # stable pointer for the dashboard/desktop
(directory / "latest.json").write_text(body, encoding="utf-8")
_prune_old_receipts(directory)
return path
@@ -265,6 +266,10 @@ def _fleet_row(
}
# Runtime-status states that do not describe a gateway that should be running now — no down row.
_NOT_EXPECTED_STATES = {"stopped", "startup_failed"}
def collect_fleet_versions(*, pre_restart_pids: Optional[list[int]] = None) -> list[dict[str, Any]]:
"""Snapshot every profile's gateway code identity vs. the current tree.
@@ -272,15 +277,9 @@ def collect_fleet_versions(*, pre_restart_pids: Optional[list[int]] = None) -> l
long-dead gateway (machine reboot, manual kill weeks ago) must NOT fail every future update.
Without a pre-restart snapshot (``None``/empty) dead PIDs are skipped (historical behavior).
"""
# Runtime-status states that do not describe a gateway that should be running now — no down row.
_NOT_EXPECTED_STATES = {"stopped", "startup_failed"}
_pre_restart = {int(p) for p in (pre_restart_pids or []) if isinstance(p, int)}
results: list[dict[str, Any]] = []
expected_sha = None
with suppress(Exception):
from hermes_cli.build_info import get_code_identity
expected_sha = (get_code_identity(refresh=True) or {}).get("sha")
expected_sha = _code_identity(refresh=True).get("sha")
try:
from gateway.status import read_runtime_status, runtime_status_pid_is_live
@@ -318,6 +317,14 @@ def collect_fleet_versions(*, pre_restart_pids: Optional[list[int]] = None) -> l
return results
_FLEET_ROW_LINES = {
"current": " ✓ {profile} (pid {pid}) @ {short} — up to date",
"stale": " ✗ {profile} (pid {pid}) @ {short} — STALE (pre-update code)",
"down": " ✗ {profile} — DOWN (gateway was running before the update; pid {pid} is gone and nothing replaced it)",
}
_FLEET_ROW_UNKNOWN = " ? {profile} (pid {pid}) — version unknown (gateway predates version stamping; restart to enable)"
def print_fleet_version_matrix(fleet: list[dict[str, Any]]) -> bool:
"""Print the post-update fleet version matrix.
@@ -329,29 +336,16 @@ def print_fleet_version_matrix(fleet: list[dict[str, Any]]) -> bool:
"""
if not fleet:
return False
any_stale = any_down = False
print()
print("Fleet version check:")
states = set()
for entry in fleet:
sha = entry.get("code_sha")
short = sha[:8] if isinstance(sha, str) and sha else "?"
state, profile, pid = entry.get("state"), entry.get("profile"), entry.get("pid")
if state == "current":
print(f" ✓ {profile} (pid {pid}) @ {short} — up to date")
elif state == "stale":
any_stale = True
print(f" ✗ {profile} (pid {pid}) @ {short} — STALE (pre-update code)")
elif state == "down":
any_down = True
print(
f" ✗ {profile} — DOWN (gateway was running before the "
f"update; pid {pid} is gone and nothing replaced it)"
)
else:
print(
f" ? {profile} (pid {pid}) — version unknown "
"(gateway predates version stamping; restart to enable)"
)
states.add(entry.get("state"))
print(_FLEET_ROW_LINES.get(entry.get("state"), _FLEET_ROW_UNKNOWN).format(
profile=entry.get("profile"), pid=entry.get("pid"), short=sha[:8] if isinstance(sha, str) and sha else "?",
))
any_stale, any_down = "stale" in states, "down" in states
if any_stale or any_down:
print()
if any_stale: