mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(gateway): sync HERMES_HOME before refreshing system systemd units
Under sudo, start/restart refreshed the unit from /root/.hermes before adopting the unit's pinned home, so TimeoutStopSec and env drifted and status stayed stuck on "service definition is outdated".
This commit is contained in:
parent
55dbc3ffb5
commit
cbf685356d
1 changed files with 60 additions and 4 deletions
|
|
@ -938,6 +938,32 @@ def _read_systemd_unit_environment(system: bool = False) -> dict[str, str]:
|
|||
return parsed
|
||||
|
||||
|
||||
def _hermes_home_from_systemd_unit_file(system: bool = False) -> str | None:
|
||||
"""Read ``HERMES_HOME`` from the on-disk unit file (not ``systemctl show``).
|
||||
|
||||
Prefer the file when refreshing/comparing: under ``sudo``, ``systemctl``
|
||||
may be slow/unavailable in tests, and the on-disk unit is what
|
||||
``systemd_unit_is_current`` / ``refresh_systemd_unit_if_needed`` already
|
||||
compare against.
|
||||
"""
|
||||
unit_path = get_systemd_unit_path(system=system)
|
||||
if not unit_path.exists():
|
||||
return None
|
||||
try:
|
||||
text = unit_path.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return None
|
||||
for line in text.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped.startswith("Environment="):
|
||||
continue
|
||||
body = stripped[len("Environment=") :].strip().strip('"')
|
||||
if body.startswith("HERMES_HOME="):
|
||||
value = body.split("=", 1)[1].strip().strip('"')
|
||||
return value or None
|
||||
return None
|
||||
|
||||
|
||||
def _sync_hermes_home_from_systemd_unit(system: bool) -> None:
|
||||
"""When acting on a system-scope unit, adopt its ``HERMES_HOME``.
|
||||
|
||||
|
|
@ -949,8 +975,11 @@ def _sync_hermes_home_from_systemd_unit(system: bool) -> None:
|
|||
"""
|
||||
if not system:
|
||||
return
|
||||
env = _read_systemd_unit_environment(system=True)
|
||||
unit_home = env.get("HERMES_HOME", "").strip()
|
||||
# Prefer the on-disk unit (source of truth for refresh/compare). Fall
|
||||
# back to ``systemctl show`` for units that only exist in the manager.
|
||||
unit_home = (_hermes_home_from_systemd_unit_file(system=True) or "").strip()
|
||||
if not unit_home:
|
||||
unit_home = _read_systemd_unit_environment(system=True).get("HERMES_HOME", "").strip()
|
||||
if not unit_home:
|
||||
return
|
||||
current = os.environ.get("HERMES_HOME", "").strip()
|
||||
|
|
@ -2830,6 +2859,13 @@ def systemd_unit_is_current(system: bool = False) -> bool:
|
|||
if not unit_path.exists():
|
||||
return False
|
||||
|
||||
# Under ``sudo hermes gateway … --system``, HERMES_HOME is often stripped
|
||||
# and falls back to ``/root/.hermes``. Adopt the unit's pinned home first
|
||||
# so TimeoutStopSec / WorkingDirectory / HERMES_HOME comparisons use the
|
||||
# real operator config — otherwise start/restart "refresh" rewrites a
|
||||
# correct unit from root's defaults and ``status`` keeps warning forever.
|
||||
_sync_hermes_home_from_systemd_unit(system=system)
|
||||
|
||||
installed = unit_path.read_text(encoding="utf-8")
|
||||
expected_user = _read_systemd_user_from_unit(unit_path) if system else None
|
||||
expected = generate_systemd_unit(system=system, run_as_user=expected_user)
|
||||
|
|
@ -2907,7 +2943,15 @@ def _refuse_temp_home_service_write(definition: str, kind: str) -> bool:
|
|||
def refresh_systemd_unit_if_needed(system: bool = False) -> bool:
|
||||
"""Rewrite the installed systemd unit when the generated definition has changed."""
|
||||
unit_path = get_systemd_unit_path(system=system)
|
||||
if not unit_path.exists() or systemd_unit_is_current(system=system):
|
||||
if not unit_path.exists():
|
||||
return False
|
||||
|
||||
# Sync before the current-check / regenerate path so a ``sudo`` shell
|
||||
# does not bake root's config into a system unit that already pins the
|
||||
# operator's HERMES_HOME. ``systemd_unit_is_current`` also syncs; doing
|
||||
# it here keeps the write path safe even if that helper changes.
|
||||
_sync_hermes_home_from_systemd_unit(system=system)
|
||||
if systemd_unit_is_current(system=system):
|
||||
return False
|
||||
|
||||
expected_user = _read_systemd_user_from_unit(unit_path) if system else None
|
||||
|
|
@ -3094,6 +3138,12 @@ def systemd_install(
|
|||
unit_path = get_systemd_unit_path(system=system)
|
||||
scope_flag = " --system" if system else ""
|
||||
|
||||
# Existing system units already pin HERMES_HOME; adopt it before any
|
||||
# current-check / regenerate so ``sudo hermes gateway install --system``
|
||||
# cannot "repair" a correct unit from root's config.
|
||||
if unit_path.exists():
|
||||
_sync_hermes_home_from_systemd_unit(system=system)
|
||||
|
||||
if unit_path.exists() and not force:
|
||||
if not systemd_unit_is_current(system=system):
|
||||
print(
|
||||
|
|
@ -3184,6 +3234,9 @@ def systemd_start(system: bool = False):
|
|||
# Raises UserSystemdUnavailableError with a remediation message.
|
||||
_preflight_user_systemd()
|
||||
_require_service_installed("start", system=system)
|
||||
# Adopt the unit's HERMES_HOME before refresh so sudo system starts do not
|
||||
# rewrite TimeoutStopSec / env from /root/.hermes.
|
||||
_sync_hermes_home_from_systemd_unit(system=system)
|
||||
refresh_systemd_unit_if_needed(system=system)
|
||||
_run_systemctl(["start", get_service_name()], system=system, check=True, timeout=30)
|
||||
print(f"✓ {_service_scope_label(system).capitalize()} service started")
|
||||
|
|
@ -3224,8 +3277,11 @@ def systemd_restart(system: bool = False):
|
|||
else:
|
||||
_preflight_user_systemd()
|
||||
_require_service_installed("restart", system=system)
|
||||
refresh_systemd_unit_if_needed(system=system)
|
||||
# Sync BEFORE refresh. Under sudo, refreshing first used to regenerate the
|
||||
# unit from /root/.hermes (wrong drain timeout / env), then sync for PID
|
||||
# lookup — leaving ``status`` stuck on "service definition is outdated".
|
||||
_sync_hermes_home_from_systemd_unit(system=system)
|
||||
refresh_systemd_unit_if_needed(system=system)
|
||||
from gateway.status import get_running_pid
|
||||
|
||||
pid = get_running_pid() or _systemd_main_pid(system=system)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue