mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(gateway): quote systemd service paths to handle spaces
This commit is contained in:
parent
ff9752410a
commit
d75278e26f
2 changed files with 39 additions and 4 deletions
|
|
@ -1176,6 +1176,14 @@ def _hermes_home_for_target_user(target_home_dir: str) -> str:
|
||||||
return str(current_hermes)
|
return str(current_hermes)
|
||||||
|
|
||||||
|
|
||||||
|
def _systemd_quote_arg(value: str) -> str:
|
||||||
|
"""Quote a systemd command/path argument only when needed."""
|
||||||
|
text = str(value)
|
||||||
|
if text and not any(ch.isspace() for ch in text) and '"' not in text and "\\" not in text:
|
||||||
|
return text
|
||||||
|
return '"' + text.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||||
|
|
||||||
|
|
||||||
def generate_systemd_unit(system: bool = False, run_as_user: str | None = None) -> str:
|
def generate_systemd_unit(system: bool = False, run_as_user: str | None = None) -> str:
|
||||||
python_path = get_python_path()
|
python_path = get_python_path()
|
||||||
working_dir = str(PROJECT_ROOT)
|
working_dir = str(PROJECT_ROOT)
|
||||||
|
|
@ -1210,6 +1218,11 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
|
||||||
path_entries.extend(_build_user_local_paths(Path(home_dir), path_entries))
|
path_entries.extend(_build_user_local_paths(Path(home_dir), path_entries))
|
||||||
path_entries.extend(common_bin_paths)
|
path_entries.extend(common_bin_paths)
|
||||||
sane_path = ":".join(path_entries)
|
sane_path = ":".join(path_entries)
|
||||||
|
profile_suffix = f" {profile_arg}" if profile_arg else ""
|
||||||
|
exec_start = (
|
||||||
|
f"{_systemd_quote_arg(python_path)} -m hermes_cli.main"
|
||||||
|
f"{profile_suffix} gateway run --replace"
|
||||||
|
)
|
||||||
return f"""[Unit]
|
return f"""[Unit]
|
||||||
Description={SERVICE_DESCRIPTION}
|
Description={SERVICE_DESCRIPTION}
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
|
|
@ -1221,8 +1234,8 @@ StartLimitBurst=5
|
||||||
Type=simple
|
Type=simple
|
||||||
User={username}
|
User={username}
|
||||||
Group={group_name}
|
Group={group_name}
|
||||||
ExecStart={python_path} -m hermes_cli.main{f" {profile_arg}" if profile_arg else ""} gateway run --replace
|
ExecStart={exec_start}
|
||||||
WorkingDirectory={working_dir}
|
WorkingDirectory={_systemd_quote_arg(working_dir)}
|
||||||
Environment="HOME={home_dir}"
|
Environment="HOME={home_dir}"
|
||||||
Environment="USER={username}"
|
Environment="USER={username}"
|
||||||
Environment="LOGNAME={username}"
|
Environment="LOGNAME={username}"
|
||||||
|
|
@ -1248,6 +1261,11 @@ WantedBy=multi-user.target
|
||||||
path_entries.extend(_build_user_local_paths(Path.home(), path_entries))
|
path_entries.extend(_build_user_local_paths(Path.home(), path_entries))
|
||||||
path_entries.extend(common_bin_paths)
|
path_entries.extend(common_bin_paths)
|
||||||
sane_path = ":".join(path_entries)
|
sane_path = ":".join(path_entries)
|
||||||
|
profile_suffix = f" {profile_arg}" if profile_arg else ""
|
||||||
|
exec_start = (
|
||||||
|
f"{_systemd_quote_arg(python_path)} -m hermes_cli.main"
|
||||||
|
f"{profile_suffix} gateway run --replace"
|
||||||
|
)
|
||||||
return f"""[Unit]
|
return f"""[Unit]
|
||||||
Description={SERVICE_DESCRIPTION}
|
Description={SERVICE_DESCRIPTION}
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
@ -1256,8 +1274,8 @@ StartLimitBurst=5
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart={python_path} -m hermes_cli.main{f" {profile_arg}" if profile_arg else ""} gateway run --replace
|
ExecStart={exec_start}
|
||||||
WorkingDirectory={working_dir}
|
WorkingDirectory={_systemd_quote_arg(working_dir)}
|
||||||
Environment="PATH={sane_path}"
|
Environment="PATH={sane_path}"
|
||||||
Environment="VIRTUAL_ENV={venv_dir}"
|
Environment="VIRTUAL_ENV={venv_dir}"
|
||||||
Environment="HERMES_HOME={hermes_home}"
|
Environment="HERMES_HOME={hermes_home}"
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,23 @@ class TestGeneratedSystemdUnits:
|
||||||
|
|
||||||
assert "/home/test/.nvm/versions/node/v24.14.0/bin" in unit
|
assert "/home/test/.nvm/versions/node/v24.14.0/bin" in unit
|
||||||
|
|
||||||
|
def test_user_unit_quotes_execstart_and_workdir_paths_with_spaces(self, tmp_path, monkeypatch):
|
||||||
|
project_root = tmp_path / "Hermes Agent"
|
||||||
|
venv_dir = project_root / "venv"
|
||||||
|
python_path = venv_dir / "bin" / "python"
|
||||||
|
python_path.parent.mkdir(parents=True)
|
||||||
|
python_path.write_text("", encoding="utf-8")
|
||||||
|
|
||||||
|
monkeypatch.setattr(gateway_cli, "PROJECT_ROOT", project_root)
|
||||||
|
monkeypatch.setattr(gateway_cli, "_detect_venv_dir", lambda: venv_dir)
|
||||||
|
monkeypatch.setattr(gateway_cli, "get_python_path", lambda: str(python_path))
|
||||||
|
monkeypatch.setattr(gateway_cli.shutil, "which", lambda cmd: None)
|
||||||
|
|
||||||
|
unit = gateway_cli.generate_systemd_unit(system=False)
|
||||||
|
|
||||||
|
assert f'ExecStart="{python_path}" -m hermes_cli.main gateway run --replace' in unit
|
||||||
|
assert f'WorkingDirectory="{project_root}"' in unit
|
||||||
|
|
||||||
def test_system_unit_avoids_recursive_execstop_and_uses_extended_stop_timeout(self):
|
def test_system_unit_avoids_recursive_execstop_and_uses_extended_stop_timeout(self):
|
||||||
unit = gateway_cli.generate_systemd_unit(system=True)
|
unit = gateway_cli.generate_systemd_unit(system=True)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue