mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-30 01:41:43 +00:00
fix(gateway): handle Linux setups without systemctl
This commit is contained in:
parent
4eecaf06e4
commit
e89b9d9732
4 changed files with 78 additions and 12 deletions
|
|
@ -355,6 +355,8 @@ def _wsl_systemd_operational() -> bool:
|
|||
def supports_systemd_services() -> bool:
|
||||
if not is_linux() or is_termux():
|
||||
return False
|
||||
if shutil.which("systemctl") is None:
|
||||
return False
|
||||
if is_wsl():
|
||||
return _wsl_systemd_operational()
|
||||
return True
|
||||
|
|
@ -2135,7 +2137,7 @@ def _is_service_running() -> bool:
|
|||
)
|
||||
if result.stdout.strip() == "active":
|
||||
return True
|
||||
except subprocess.TimeoutExpired:
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
|
||||
if system_unit_exists:
|
||||
|
|
@ -2146,7 +2148,7 @@ def _is_service_running() -> bool:
|
|||
)
|
||||
if result.stdout.strip() == "active":
|
||||
return True
|
||||
except subprocess.TimeoutExpired:
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -2232,6 +2232,7 @@ def setup_gateway(config: dict):
|
|||
from hermes_cli.gateway import (
|
||||
_is_service_installed,
|
||||
_is_service_running,
|
||||
supports_systemd_services,
|
||||
has_conflicting_systemd_units,
|
||||
install_linux_gateway_from_setup,
|
||||
print_systemd_scope_conflict_warning,
|
||||
|
|
@ -2244,16 +2245,18 @@ def setup_gateway(config: dict):
|
|||
|
||||
service_installed = _is_service_installed()
|
||||
service_running = _is_service_running()
|
||||
supports_systemd = supports_systemd_services()
|
||||
supports_service_manager = supports_systemd or _is_macos
|
||||
|
||||
print()
|
||||
if _is_linux and has_conflicting_systemd_units():
|
||||
if supports_systemd and has_conflicting_systemd_units():
|
||||
print_systemd_scope_conflict_warning()
|
||||
print()
|
||||
|
||||
if service_running:
|
||||
if prompt_yes_no(" Restart the gateway to pick up changes?", True):
|
||||
try:
|
||||
if _is_linux:
|
||||
if supports_systemd:
|
||||
systemd_restart()
|
||||
elif _is_macos:
|
||||
launchd_restart()
|
||||
|
|
@ -2262,14 +2265,14 @@ def setup_gateway(config: dict):
|
|||
elif service_installed:
|
||||
if prompt_yes_no(" Start the gateway service?", True):
|
||||
try:
|
||||
if _is_linux:
|
||||
if supports_systemd:
|
||||
systemd_start()
|
||||
elif _is_macos:
|
||||
launchd_start()
|
||||
except Exception as e:
|
||||
print_error(f" Start failed: {e}")
|
||||
elif _is_linux or _is_macos:
|
||||
svc_name = "systemd" if _is_linux else "launchd"
|
||||
elif supports_service_manager:
|
||||
svc_name = "systemd" if supports_systemd else "launchd"
|
||||
if prompt_yes_no(
|
||||
f" Install the gateway as a {svc_name} service? (runs in background, starts on boot)",
|
||||
True,
|
||||
|
|
@ -2277,7 +2280,7 @@ def setup_gateway(config: dict):
|
|||
try:
|
||||
installed_scope = None
|
||||
did_install = False
|
||||
if _is_linux:
|
||||
if supports_systemd:
|
||||
installed_scope, did_install = install_linux_gateway_from_setup(force=False)
|
||||
else:
|
||||
launchd_install(force=False)
|
||||
|
|
@ -2285,7 +2288,7 @@ def setup_gateway(config: dict):
|
|||
print()
|
||||
if did_install and prompt_yes_no(" Start the service now?", True):
|
||||
try:
|
||||
if _is_linux:
|
||||
if supports_systemd:
|
||||
systemd_start(system=installed_scope == "system")
|
||||
elif _is_macos:
|
||||
launchd_start()
|
||||
|
|
@ -2296,7 +2299,7 @@ def setup_gateway(config: dict):
|
|||
print_info(" You can try manually: hermes gateway install")
|
||||
else:
|
||||
print_info(" You can install later: hermes gateway install")
|
||||
if _is_linux:
|
||||
if supports_systemd:
|
||||
print_info(" Or as a boot-time service: sudo hermes gateway install --system")
|
||||
print_info(" Or run in foreground: hermes gateway")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -394,6 +394,13 @@ class TestLaunchdServiceRecovery:
|
|||
|
||||
|
||||
class TestGatewayServiceDetection:
|
||||
def test_supports_systemd_services_requires_systemctl_binary(self, monkeypatch):
|
||||
monkeypatch.setattr(gateway_cli, "is_linux", lambda: True)
|
||||
monkeypatch.setattr(gateway_cli, "is_termux", lambda: False)
|
||||
monkeypatch.setattr(gateway_cli.shutil, "which", lambda name: None)
|
||||
|
||||
assert gateway_cli.supports_systemd_services() is False
|
||||
|
||||
def test_is_service_running_checks_system_scope_when_user_scope_is_inactive(self, monkeypatch):
|
||||
user_unit = SimpleNamespace(exists=lambda: True)
|
||||
system_unit = SimpleNamespace(exists=lambda: True)
|
||||
|
|
@ -418,6 +425,23 @@ class TestGatewayServiceDetection:
|
|||
|
||||
assert gateway_cli._is_service_running() is True
|
||||
|
||||
def test_is_service_running_returns_false_when_systemctl_missing(self, monkeypatch):
|
||||
unit = SimpleNamespace(exists=lambda: True)
|
||||
|
||||
monkeypatch.setattr(gateway_cli, "supports_systemd_services", lambda: True)
|
||||
monkeypatch.setattr(
|
||||
gateway_cli,
|
||||
"get_systemd_unit_path",
|
||||
lambda system=False: unit,
|
||||
)
|
||||
|
||||
def fake_run(*args, **kwargs):
|
||||
raise FileNotFoundError("systemctl")
|
||||
|
||||
monkeypatch.setattr(gateway_cli.subprocess, "run", fake_run)
|
||||
|
||||
assert gateway_cli._is_service_running() is False
|
||||
|
||||
|
||||
class TestGatewaySystemServiceRouting:
|
||||
def test_systemd_restart_self_requests_graceful_restart_without_reload_or_restart(self, monkeypatch, capsys):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
"""Tests for setup_model_provider — verifies the delegation to
|
||||
select_provider_and_model() and config dict sync."""
|
||||
"""Tests for setup.py configuration flows."""
|
||||
import json
|
||||
import sys
|
||||
import types
|
||||
|
|
@ -8,6 +7,7 @@ import pytest
|
|||
|
||||
from hermes_cli.auth import get_active_provider
|
||||
from hermes_cli.config import load_config, save_config
|
||||
from hermes_cli import setup as setup_mod
|
||||
from hermes_cli.setup import setup_model_provider
|
||||
|
||||
|
||||
|
|
@ -144,6 +144,43 @@ def test_setup_custom_providers_synced(tmp_path, monkeypatch):
|
|||
assert reloaded.get("custom_providers") == [{"name": "Local", "base_url": "http://localhost:8080/v1"}]
|
||||
|
||||
|
||||
def test_setup_gateway_skips_service_install_when_systemctl_missing(monkeypatch, capsys):
|
||||
env = {
|
||||
"TELEGRAM_BOT_TOKEN": "",
|
||||
"TELEGRAM_HOME_CHANNEL": "",
|
||||
"DISCORD_BOT_TOKEN": "",
|
||||
"DISCORD_HOME_CHANNEL": "",
|
||||
"SLACK_BOT_TOKEN": "",
|
||||
"SLACK_HOME_CHANNEL": "",
|
||||
"MATRIX_HOMESERVER": "https://matrix.example.com",
|
||||
"MATRIX_USER_ID": "@alice:example.com",
|
||||
"MATRIX_PASSWORD": "",
|
||||
"MATRIX_ACCESS_TOKEN": "token",
|
||||
"BLUEBUBBLES_SERVER_URL": "",
|
||||
"BLUEBUBBLES_HOME_CHANNEL": "",
|
||||
"WHATSAPP_ENABLED": "",
|
||||
"WEBHOOK_ENABLED": "",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(setup_mod, "get_env_value", lambda key: env.get(key, ""))
|
||||
monkeypatch.setattr(setup_mod, "prompt_yes_no", lambda *args, **kwargs: False)
|
||||
monkeypatch.setattr("platform.system", lambda: "Linux")
|
||||
|
||||
import hermes_cli.gateway as gateway_mod
|
||||
|
||||
monkeypatch.setattr(gateway_mod, "supports_systemd_services", lambda: False)
|
||||
monkeypatch.setattr(gateway_mod, "is_macos", lambda: False)
|
||||
monkeypatch.setattr(gateway_mod, "_is_service_installed", lambda: False)
|
||||
monkeypatch.setattr(gateway_mod, "_is_service_running", lambda: False)
|
||||
|
||||
setup_mod.setup_gateway({})
|
||||
|
||||
out = capsys.readouterr().out
|
||||
assert "Messaging platforms configured!" in out
|
||||
assert "Start the gateway to bring your bots online:" in out
|
||||
assert "hermes gateway" in out
|
||||
|
||||
|
||||
def test_setup_syncs_custom_provider_removal_from_disk(tmp_path, monkeypatch):
|
||||
"""Removing the last custom provider in model setup should persist."""
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue