Files
hermes-agent/tests/hermes_cli/test_runtime_paths.py
ethernet 2efa4ff94f refactor(desktop): prepare dependencies before saving build caches
Dependency acquisition during packaging left native wheels and packager
inputs outside the pre-build cache save. Compose PM and existing providers
into a preparation phase, then require builds to consume admitted inputs.

Share native preparation with PM Bundle. Keep path-bound environments and
signing outputs separate from reusable caches. Use read-only cache tokens
for commit builds and preserve the one-command local build path.

Verify pinned tools through PM, probe PTYs under the prepared Electron,
and supply dmgbuild through a build-only PM package. Resolve bundled tool
stores from their payload manifest so relocation preserves discovery.

Validation: focused Python and JS tests, checkJs, Ruff, Windows checks,
anti-slop, cache relocation, and network-denied Linux AppImage builds.
Relocated runtime smoke passed with NixOS host libraries supplied.
Native Windows/macOS signing and live GitHub cache behavior remain untested.
2026-09-13 14:28:31 -04:00

67 lines
2.8 KiB
Python

"""Payload stores follow the payload, not the launching shell's home."""
import json
import pytest
from hermes_cli.runtime_paths import store_root
from hermes_constants import get_default_hermes_root
def test_store_resolution_follows_relocated_payload(tmp_path, monkeypatch):
monkeypatch.delenv("HERMES_RUNTIME_DIR", raising=False)
payload = tmp_path / "agent-payload"
repo = payload / "hermes-agent"
repo.mkdir(parents=True)
stamp = repo / "install-stamp.json"
stamp.write_text(json.dumps({"payload": "bundled", "runtime": {
"repoDir": "hermes-agent", "toolsDir": "tools",
}}))
manifest = payload / "manifest.json"
manifest.write_text(json.dumps({"schema": 1, "repo": "hermes-agent",
"venv": "venv", "store": "tools",
"runtime": {"toolsDir": "tools"}}))
tools = payload / "tools"
tools.mkdir()
facts = {"schema": 1, "packages": {"node": {"entry": "node-test"}}}
(tools / "facts.json").write_text(json.dumps(facts))
(tools / "node-test").mkdir()
for destination in (payload, tmp_path / "relocated payload"):
if destination != payload:
payload.rename(destination)
repo = destination / "hermes-agent"
resolved = store_root(repo)
assert resolved == destination / "tools"
installed = json.loads((resolved / "facts.json").read_text())
assert (resolved / installed["packages"]["node"]["entry"]).is_dir()
override = tmp_path / "stage-tools"
monkeypatch.setenv("HERMES_RUNTIME_DIR", str(override))
assert store_root(repo) == override
monkeypatch.delenv("HERMES_RUNTIME_DIR")
(repo / "install-stamp.json").write_text(json.dumps({"runtimeDir": str(override)}))
(destination / "manifest.json").write_text(json.dumps({"repo": "other-repo"}))
assert store_root(repo) == override
(repo / "install-stamp.json").unlink()
assert store_root(repo) == get_default_hermes_root() / "tools"
@pytest.mark.parametrize("escape", ["relative", "absolute", "symlink"])
def test_payload_store_cannot_escape_payload(tmp_path, monkeypatch, escape):
monkeypatch.delenv("HERMES_RUNTIME_DIR", raising=False)
payload = tmp_path / "agent-payload"
repo = payload / "hermes-agent"
repo.mkdir(parents=True)
outside = tmp_path / "outside"
outside.mkdir()
values = {"relative": "../outside", "absolute": str(outside), "symlink": "tools"}
if escape == "symlink":
(payload / "tools").symlink_to(outside, target_is_directory=True)
(payload / "manifest.json").write_text(json.dumps({
"repo": "hermes-agent", "store": values[escape],
}))
with pytest.raises(RuntimeError, match="payload store escapes its root"):
store_root(repo)
monkeypatch.setenv("HERMES_RUNTIME_DIR", str(outside))
assert store_root(repo) == outside