fix(gateway): enable linger on systemd user-service repair path (#12863)

The repair branch in `systemd_install()` exits as soon as it rewrites an
outdated unit and re-runs `systemctl enable`, bypassing
`_ensure_linger_enabled()`. On headless Linux the command reports
success, but the repaired user service still stops at logout.

Call `_ensure_linger_enabled()` before the early return when the install
is user-scoped, mirroring what the fresh-install path already does.

Adds two regression tests in `tests/hermes_cli/test_gateway_linger.py`:
- repair path (user scope) calls the linger helper
- repair path (system scope) does not call it
This commit is contained in:
Season
2026-04-20 19:37:17 +08:00
committed by Teknium
parent 5be0c4921c
commit 6d20c321d6
2 changed files with 25 additions and 0 deletions
+3
View File
@@ -3234,9 +3234,12 @@ def systemd_install(
else:
print(f"Service already installed at: {unit_path}")
print("Use --force to reinstall")
# Same post-install guarantee as a fresh install: a repaired user unit must survive logout too.
configured_user = _read_systemd_user_from_unit(unit_path) if system else None
if configured_user:
_ensure_system_service_linger(configured_user)
elif not system:
_ensure_linger_enabled()
return
unit_path.parent.mkdir(parents=True, exist_ok=True)
+22
View File
@@ -215,3 +215,25 @@ def test_existing_system_install_repairs_linger_for_configured_user(monkeypatch,
gateway.systemd_install(system=True, run_as_user="alice")
assert helper_calls == ["alice"]
@pytest.mark.parametrize("system", [False, True])
def test_systemd_install_repair_path_keeps_linger_guarantee(monkeypatch, tmp_path, system):
"""Repairing a stale unit used to return before the linger step, so an upgraded headless
user service died at logout (#12863). System scope never touches user linger."""
unit_path = tmp_path / "hermes-gateway.service"
unit_path.write_text("old unit\n", encoding="utf-8")
monkeypatch.setattr(gateway, "get_systemd_unit_path", lambda system=False: unit_path)
monkeypatch.setattr(gateway, "systemd_unit_is_current", lambda system=False: False)
monkeypatch.setattr(gateway, "has_legacy_hermes_units", lambda: False)
monkeypatch.setattr(gateway, "_require_root_for_system_service", lambda _action: None)
monkeypatch.setattr(gateway, "_sync_hermes_home_from_systemd_unit", lambda system=False: None)
monkeypatch.setattr(gateway, "_read_systemd_user_from_unit", lambda path: None)
monkeypatch.setattr(gateway, "refresh_systemd_unit_if_needed", lambda system=False: None)
monkeypatch.setattr(gateway, "_run_systemctl", lambda *args, **kwargs: None)
helper_calls = []
monkeypatch.setattr(gateway, "_ensure_linger_enabled", lambda: helper_calls.append(True))
gateway.systemd_install(force=False, system=system)
assert helper_calls == ([] if system else [True])