141 lines
4.8 KiB
Python
141 lines
4.8 KiB
Python
"""hermes desktop forwards an explicit profile into the packaged Electron launch.
|
|
|
|
Electron boots from active-profile.json. ``hermes -p <name> desktop`` used to
|
|
set only the CLI HERMES_HOME, so the packaged app still started the stored
|
|
profile. A launch with no ``-p`` / ``--profile`` must not invent a flag.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from argparse import Namespace
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from hermes_cli import main as cli_main
|
|
from hermes_cli import main_desktop
|
|
|
|
|
|
def _ns(**kwargs):
|
|
defaults = dict(
|
|
skip_build=True,
|
|
build_only=False,
|
|
force_build=False,
|
|
source=False,
|
|
fake_boot=False,
|
|
ignore_existing=False,
|
|
hermes_root=None,
|
|
cwd=None,
|
|
setup_tcc_identity=False,
|
|
identity=None,
|
|
local=False,
|
|
)
|
|
defaults.update(kwargs)
|
|
return Namespace(**defaults)
|
|
|
|
|
|
def _named_profiles(argv: list[str]) -> set[str]:
|
|
names: set[str] = set()
|
|
for index, arg in enumerate(argv):
|
|
if arg in {"-p", "--profile"} and index + 1 < len(argv):
|
|
names.add(argv[index + 1].strip().casefold())
|
|
elif arg.startswith("--profile="):
|
|
names.add(arg.split("=", 1)[1].strip().casefold())
|
|
names.discard("default")
|
|
return names
|
|
|
|
|
|
def _packaged_exe(root: Path) -> Path:
|
|
desktop_dir = root / "apps" / "desktop"
|
|
if sys.platform == "darwin":
|
|
exe = desktop_dir / "release" / "mac-arm64" / "Hermes.app" / "Contents" / "MacOS" / "Hermes"
|
|
elif sys.platform == "win32":
|
|
exe = desktop_dir / "release" / "win-unpacked" / "Hermes.exe"
|
|
else:
|
|
exe = desktop_dir / "release" / "linux-unpacked" / "hermes"
|
|
exe.parent.mkdir(parents=True, exist_ok=True)
|
|
(exe.parent / "chrome-sandbox").write_text("", encoding="utf-8")
|
|
exe.parent.mkdir(parents=True, exist_ok=True)
|
|
exe.write_text("", encoding="utf-8")
|
|
return exe
|
|
|
|
|
|
def _live_profile(root: Path, name: str) -> None:
|
|
profile = root / "profiles" / name
|
|
profile.mkdir(parents=True, exist_ok=True)
|
|
(profile / "config.yaml").write_text("{}\n", encoding="utf-8")
|
|
|
|
|
|
def _launch_command(tmp_path, monkeypatch, argv: list[str], *, active_profile: str | None = None) -> list[str]:
|
|
hermes_root = tmp_path / ".hermes"
|
|
hermes_root.mkdir()
|
|
if active_profile and active_profile != "default":
|
|
_live_profile(hermes_root, active_profile)
|
|
(hermes_root / "active_profile").write_text(active_profile + "\n", encoding="utf-8")
|
|
for name in _named_profiles(argv):
|
|
_live_profile(hermes_root, name)
|
|
|
|
project = tmp_path / "hermes-agent"
|
|
(project / "apps" / "desktop").mkdir(parents=True)
|
|
(project / "apps" / "desktop" / "package.json").write_text("{}", encoding="utf-8")
|
|
exe = _packaged_exe(project)
|
|
|
|
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
monkeypatch.delenv("HERMES_HOME", raising=False)
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "xdg"))
|
|
monkeypatch.setenv("GNOME_KEYRING_CONTROL", "/run/user/1000/keyring")
|
|
for var in (
|
|
"HERMES_SUPERVISED_CHILD",
|
|
"HERMES_S6_SUPERVISED_CHILD",
|
|
"INVOCATION_ID",
|
|
"HERMES_GATEWAY_EXTERNAL_SUPERVISOR",
|
|
"KDE_SESSION_VERSION",
|
|
"KDE_FULL_SESSION",
|
|
):
|
|
monkeypatch.delenv(var, raising=False)
|
|
monkeypatch.setattr(sys, "argv", argv)
|
|
monkeypatch.setattr(cli_main, "PROJECT_ROOT", project)
|
|
monkeypatch.setattr(main_desktop, "_desktop_exe_integrity_error", lambda _path: None)
|
|
# Module global: restore it so a later cmd_gui test does not inherit a flag.
|
|
monkeypatch.setattr(cli_main, "_explicit_cli_profile", None)
|
|
|
|
cli_main._apply_profile_override()
|
|
|
|
calls: list[list[str]] = []
|
|
|
|
def _run(cmd, **_kwargs):
|
|
calls.append(list(cmd))
|
|
return subprocess.CompletedProcess(cmd, 0)
|
|
|
|
monkeypatch.setattr(subprocess, "run", _run)
|
|
with pytest.raises(SystemExit) as exc:
|
|
cli_main.cmd_gui(_ns())
|
|
assert exc.value.code == 0
|
|
launches = [cmd for cmd in calls if cmd and cmd[0] == str(exe)]
|
|
assert len(launches) == 1
|
|
return launches[0]
|
|
|
|
|
|
def test_dash_p_desktop_appends_profile_to_packaged_launch(tmp_path, monkeypatch):
|
|
launched = _launch_command(tmp_path, monkeypatch, ["hermes", "-p", "desktop", "desktop"])
|
|
|
|
assert launched[-2:] == ["--profile", "desktop"]
|
|
|
|
|
|
def test_profile_equals_spelling_is_forwarded(tmp_path, monkeypatch):
|
|
launched = _launch_command(tmp_path, monkeypatch, ["hermes", "desktop", "--profile=work"])
|
|
|
|
assert launched[-2:] == ["--profile", "work"]
|
|
|
|
|
|
def test_missing_flag_does_not_forward_sticky_profile(tmp_path, monkeypatch):
|
|
launched = _launch_command(
|
|
tmp_path, monkeypatch, ["hermes", "desktop"], active_profile="coder"
|
|
)
|
|
|
|
assert "--profile" not in launched
|
|
assert os.environ.get("HERMES_HOME", "").endswith(f"{os.sep}profiles{os.sep}coder")
|