mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
fix(gateway): show friendly error when service is not installed
Instead of an unhelpful CalledProcessError traceback when running `hermes gateway start/stop/restart` without first installing the service, check for the unit file and exit with an actionable install hint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c5789f4309
commit
8c8f95bc8e
2 changed files with 35 additions and 0 deletions
|
|
@ -1971,6 +1971,15 @@ def systemd_uninstall(system: bool = False):
|
||||||
print(f"✓ {_service_scope_label(system).capitalize()} service uninstalled")
|
print(f"✓ {_service_scope_label(system).capitalize()} service uninstalled")
|
||||||
|
|
||||||
|
|
||||||
|
def _require_service_installed(action: str, system: bool = False) -> None:
|
||||||
|
unit_path = get_systemd_unit_path(system=system)
|
||||||
|
if not unit_path.exists():
|
||||||
|
scope_flag = " --system" if system else ""
|
||||||
|
print(f"✗ Gateway service is not installed")
|
||||||
|
print(f" Run: {'sudo ' if system else ''}hermes gateway install{scope_flag}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def systemd_start(system: bool = False):
|
def systemd_start(system: bool = False):
|
||||||
system = _select_systemd_scope(system)
|
system = _select_systemd_scope(system)
|
||||||
if system:
|
if system:
|
||||||
|
|
@ -1980,6 +1989,7 @@ def systemd_start(system: bool = False):
|
||||||
# reachable (common on fresh RHEL/Debian SSH sessions without linger).
|
# reachable (common on fresh RHEL/Debian SSH sessions without linger).
|
||||||
# Raises UserSystemdUnavailableError with a remediation message.
|
# Raises UserSystemdUnavailableError with a remediation message.
|
||||||
_preflight_user_systemd()
|
_preflight_user_systemd()
|
||||||
|
_require_service_installed("start", system=system)
|
||||||
refresh_systemd_unit_if_needed(system=system)
|
refresh_systemd_unit_if_needed(system=system)
|
||||||
_run_systemctl(["start", get_service_name()], system=system, check=True, timeout=30)
|
_run_systemctl(["start", get_service_name()], system=system, check=True, timeout=30)
|
||||||
print(f"✓ {_service_scope_label(system).capitalize()} service started")
|
print(f"✓ {_service_scope_label(system).capitalize()} service started")
|
||||||
|
|
@ -1990,6 +2000,7 @@ def systemd_stop(system: bool = False):
|
||||||
system = _select_systemd_scope(system)
|
system = _select_systemd_scope(system)
|
||||||
if system:
|
if system:
|
||||||
_require_root_for_system_service("stop")
|
_require_root_for_system_service("stop")
|
||||||
|
_require_service_installed("stop", system=system)
|
||||||
_run_systemctl(["stop", get_service_name()], system=system, check=True, timeout=90)
|
_run_systemctl(["stop", get_service_name()], system=system, check=True, timeout=90)
|
||||||
print(f"✓ {_service_scope_label(system).capitalize()} service stopped")
|
print(f"✓ {_service_scope_label(system).capitalize()} service stopped")
|
||||||
|
|
||||||
|
|
@ -2001,6 +2012,7 @@ def systemd_restart(system: bool = False):
|
||||||
_require_root_for_system_service("restart")
|
_require_root_for_system_service("restart")
|
||||||
else:
|
else:
|
||||||
_preflight_user_systemd()
|
_preflight_user_systemd()
|
||||||
|
_require_service_installed("restart", system=system)
|
||||||
refresh_systemd_unit_if_needed(system=system)
|
refresh_systemd_unit_if_needed(system=system)
|
||||||
from gateway.status import get_running_pid
|
from gateway.status import get_running_pid
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,27 @@ class TestSystemdServiceRefresh:
|
||||||
assert ["systemctl", "--user", "daemon-reload"] in calls
|
assert ["systemctl", "--user", "daemon-reload"] in calls
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequireServiceInstalled:
|
||||||
|
def test_exits_with_install_hint_when_unit_missing(self, tmp_path, monkeypatch, capsys):
|
||||||
|
unit_path = tmp_path / "hermes-gateway.service"
|
||||||
|
monkeypatch.setattr(gateway_cli, "get_systemd_unit_path", lambda system=False: unit_path)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
gateway_cli._require_service_installed("start")
|
||||||
|
|
||||||
|
assert exc_info.value.code == 1
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "not installed" in out
|
||||||
|
assert "hermes gateway install" in out
|
||||||
|
|
||||||
|
def test_passes_when_unit_exists(self, tmp_path, monkeypatch):
|
||||||
|
unit_path = tmp_path / "hermes-gateway.service"
|
||||||
|
unit_path.write_text("[Unit]\n", encoding="utf-8")
|
||||||
|
monkeypatch.setattr(gateway_cli, "get_systemd_unit_path", lambda system=False: unit_path)
|
||||||
|
|
||||||
|
gateway_cli._require_service_installed("start")
|
||||||
|
|
||||||
|
|
||||||
class TestGeneratedSystemdUnits:
|
class TestGeneratedSystemdUnits:
|
||||||
def test_user_unit_avoids_recursive_execstop_and_uses_extended_stop_timeout(self):
|
def test_user_unit_avoids_recursive_execstop_and_uses_extended_stop_timeout(self):
|
||||||
unit = gateway_cli.generate_systemd_unit(system=False)
|
unit = gateway_cli.generate_systemd_unit(system=False)
|
||||||
|
|
@ -521,6 +542,7 @@ class TestGatewaySystemServiceRouting:
|
||||||
calls = []
|
calls = []
|
||||||
|
|
||||||
monkeypatch.setattr(gateway_cli, "_select_systemd_scope", lambda system=False: False)
|
monkeypatch.setattr(gateway_cli, "_select_systemd_scope", lambda system=False: False)
|
||||||
|
monkeypatch.setattr(gateway_cli, "_require_service_installed", lambda action, system=False: None)
|
||||||
monkeypatch.setattr(gateway_cli, "refresh_systemd_unit_if_needed", lambda system=False: calls.append(("refresh", system)))
|
monkeypatch.setattr(gateway_cli, "refresh_systemd_unit_if_needed", lambda system=False: calls.append(("refresh", system)))
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"gateway.status.get_running_pid",
|
"gateway.status.get_running_pid",
|
||||||
|
|
@ -575,6 +597,7 @@ class TestGatewaySystemServiceRouting:
|
||||||
|
|
||||||
def test_systemd_restart_recovers_failed_planned_restart(self, monkeypatch, capsys):
|
def test_systemd_restart_recovers_failed_planned_restart(self, monkeypatch, capsys):
|
||||||
monkeypatch.setattr(gateway_cli, "_select_systemd_scope", lambda system=False: False)
|
monkeypatch.setattr(gateway_cli, "_select_systemd_scope", lambda system=False: False)
|
||||||
|
monkeypatch.setattr(gateway_cli, "_require_service_installed", lambda action, system=False: None)
|
||||||
monkeypatch.setattr(gateway_cli, "refresh_systemd_unit_if_needed", lambda system=False: None)
|
monkeypatch.setattr(gateway_cli, "refresh_systemd_unit_if_needed", lambda system=False: None)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"gateway.status.read_runtime_status",
|
"gateway.status.read_runtime_status",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue