From e7d3f1f3bab68794fcb7b05039970429d40b4bc1 Mon Sep 17 00:00:00 2001 From: teknium1 Date: Mon, 16 Mar 2026 04:35:34 -0700 Subject: [PATCH] fix(update): kill gateway via PID file before restart cmd_update only ran 'systemctl --user restart hermes-gateway', which left manually-started gateway processes alive, causing duplicates. Now uses get_running_pid() from gateway/status.py (scoped to HERMES_HOME) to find and SIGTERM this installation's gateway before restarting. Safe with multiple Hermes installations since each HERMES_HOME has its own PID file. If no systemd service exists, informs the user to restart manually. Based on PR #1131 by teknium1. Dropped the cli.py Rich from_ansi changes (already on main). --- hermes_cli/main.py | 68 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/hermes_cli/main.py b/hermes_cli/main.py index b835efb0..0a16c32d 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -2301,26 +2301,58 @@ def cmd_update(args): print() print("✓ Update complete!") - # Auto-restart gateway if it's running as a systemd service + # Auto-restart gateway if it's running. + # Uses the PID file (scoped to HERMES_HOME) to find this + # installation's gateway — safe with multiple installations. try: - check = subprocess.run( - ["systemctl", "--user", "is-active", "hermes-gateway"], - capture_output=True, text=True, timeout=5, - ) - if check.stdout.strip() == "active": - print() - print("→ Gateway service is running — restarting to pick up changes...") - restart = subprocess.run( - ["systemctl", "--user", "restart", "hermes-gateway"], - capture_output=True, text=True, timeout=15, + from gateway.status import get_running_pid, remove_pid_file + import signal as _signal + + existing_pid = get_running_pid() + has_systemd_service = False + + try: + check = subprocess.run( + ["systemctl", "--user", "is-active", "hermes-gateway"], + capture_output=True, text=True, timeout=5, ) - if restart.returncode == 0: - print("✓ Gateway restarted.") - else: - print(f"⚠ Gateway restart failed: {restart.stderr.strip()}") - print(" Try manually: hermes gateway restart") - except (FileNotFoundError, subprocess.TimeoutExpired): - pass # No systemd (macOS, WSL1, etc.) — skip silently + has_systemd_service = check.stdout.strip() == "active" + except (FileNotFoundError, subprocess.TimeoutExpired): + pass + + if existing_pid or has_systemd_service: + print() + + # Kill the PID-file-tracked process (may be manual or systemd) + if existing_pid: + try: + os.kill(existing_pid, _signal.SIGTERM) + print(f"→ Stopped gateway process (PID {existing_pid})") + except ProcessLookupError: + pass # Already gone + except PermissionError: + print(f"⚠ Permission denied killing gateway PID {existing_pid}") + remove_pid_file() + + # Restart the systemd service (starts a fresh process) + if has_systemd_service: + import time as _time + _time.sleep(1) # Brief pause for port/socket release + print("→ Restarting gateway service...") + restart = subprocess.run( + ["systemctl", "--user", "restart", "hermes-gateway"], + capture_output=True, text=True, timeout=15, + ) + if restart.returncode == 0: + print("✓ Gateway restarted.") + else: + print(f"⚠ Gateway restart failed: {restart.stderr.strip()}") + print(" Try manually: hermes gateway restart") + elif existing_pid: + print(" ℹ️ Gateway was running manually (not as a service).") + print(" Restart it with: hermes gateway run") + except Exception as e: + logger.debug("Gateway restart during update failed: %s", e) print() print("Tip: You can now select a provider and model:")