fix(gateway): remap HERMES_HOME to target user in system service unit
When `sudo hermes gateway install --system --run-as-user <user>` generates the systemd unit, get_hermes_home() resolves to /root/.hermes because Path.home() returns root's home under sudo. The unit correctly sets HOME= and User= via _system_service_identity(), but HERMES_HOME was computed independently and pointed to root's config directory. Add _hermes_home_for_target_user() which remaps the current HERMES_HOME to the equivalent path under the target user's home. This handles: - Default ~/.hermes → target user's ~/.hermes - Profiles (e.g. ~/.hermes/profiles/coder) → preserves relative structure - Custom paths (e.g. /opt/hermes) → kept as-is Supersedes #3861 which only handled the default case and left profiles broken (also flagged by Copilot review). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -463,6 +463,32 @@ def _build_user_local_paths(home: Path, path_entries: list[str]) -> list[str]:
|
||||
return [p for p in candidates if p not in path_entries and Path(p).exists()]
|
||||
|
||||
|
||||
def _hermes_home_for_target_user(target_home_dir: str) -> str:
|
||||
"""Remap the current HERMES_HOME to the equivalent under a target user's home.
|
||||
|
||||
When installing a system service via sudo, get_hermes_home() resolves to
|
||||
root's home. This translates it to the target user's equivalent path:
|
||||
/root/.hermes → /home/alice/.hermes
|
||||
/root/.hermes/profiles/coder → /home/alice/.hermes/profiles/coder
|
||||
/opt/custom-hermes → /opt/custom-hermes (kept as-is)
|
||||
"""
|
||||
current_hermes = get_hermes_home().resolve()
|
||||
current_default = (Path.home() / ".hermes").resolve()
|
||||
target_default = Path(target_home_dir) / ".hermes"
|
||||
|
||||
# Default ~/.hermes → remap to target user's default
|
||||
if current_hermes == current_default:
|
||||
return str(target_default)
|
||||
|
||||
# Profile or subdir of ~/.hermes → preserve the relative structure
|
||||
try:
|
||||
relative = current_hermes.relative_to(current_default)
|
||||
return str(target_default / relative)
|
||||
except ValueError:
|
||||
# Completely custom path (not under ~/.hermes) — keep as-is
|
||||
return str(current_hermes)
|
||||
|
||||
|
||||
def generate_systemd_unit(system: bool = False, run_as_user: str | None = None) -> str:
|
||||
python_path = get_python_path()
|
||||
working_dir = str(PROJECT_ROOT)
|
||||
@@ -478,12 +504,11 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
|
||||
if resolved_node_dir not in path_entries:
|
||||
path_entries.append(resolved_node_dir)
|
||||
|
||||
hermes_home = str(get_hermes_home().resolve())
|
||||
|
||||
common_bin_paths = ["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"]
|
||||
|
||||
if system:
|
||||
username, group_name, home_dir = _system_service_identity(run_as_user)
|
||||
hermes_home = _hermes_home_for_target_user(home_dir)
|
||||
path_entries.extend(_build_user_local_paths(Path(home_dir), path_entries))
|
||||
path_entries.extend(common_bin_paths)
|
||||
sane_path = ":".join(path_entries)
|
||||
@@ -518,6 +543,7 @@ StandardError=journal
|
||||
WantedBy=multi-user.target
|
||||
"""
|
||||
|
||||
hermes_home = str(get_hermes_home().resolve())
|
||||
path_entries.extend(_build_user_local_paths(Path.home(), path_entries))
|
||||
path_entries.extend(common_bin_paths)
|
||||
sane_path = ":".join(path_entries)
|
||||
|
||||
Reference in New Issue
Block a user