Files
hermes-agent/tests/pm/test_long_path_exits.py
alt-glitch 953a598860 fix(pm): give text that leaves PM the ordinary spelling of store paths
Store now spells its root \\?\C:\... on Windows so PM's own file calls
under it pass MAX_PATH. That spelling must not escape: the same folder
spelled two ways hashes and compares differently, and children such as
cmd.exe, gpg or uv refuse or record the prefix (uv would write it into
pyvenv.cfg).

native() now converts store-derived paths where their text leaves PM:
- process argv and cwd: uv via PythonEnvironment._run (its one exit),
  binary version probes, npm self-install, the PortableGit extractor,
  gpg provenance checks, the plugin-eviction interpreter probe;
- environment values: compose_env (PATH and package env diffs), the
  activation PATH dirs, HERMES_RUNTIME_DIR;
- hashed identities: the PM runtime inputs and isolated-environment
  identity, so existing records still match and nothing rebuilds;
- API results callers execute or compare: installed_package,
  uv_launcher, verified_tools entries;
- user-facing verification messages.

Facts records already store {STORE}-templated env, so no absolute path
reaches them. Tests pin that identities are spelling-independent and that
records, composed env and uv invocations from a verbatim root carry no
prefix (both fail on e8c97320ac).
2026-10-07 06:40:39 +05:30

55 lines
2.4 KiB
Python

"""A store root spelled in Windows' extended-length form (``\\\\?\\C:\\...``) stays inside PM.
The spelling exists only so PM's own file calls beneath the root pass MAX_PATH. Text that
leaves PM must carry the ordinary spelling: the same folder spelled two ways hashes and
compares differently, and child tools (cmd.exe, gpg, installers) may refuse the prefix.
The roots below are plain text, so both invariants hold on every OS.
"""
import subprocess
from pathlib import Path
from pm import environment as environment_module
from pm.environment import PythonEnvironment
from pm.lock import Facts
from pm.package import compose_env
from pm.runtime import _inputs
from pm.store import Store
VERBATIM = "\\\\?\\"
ROOT = Path(VERBATIM + r"C:\Program Files\WindowsApps\Hermes\agent-payload\tools")
def test_identity_does_not_depend_on_the_root_spelling(tmp_path):
for name in ("pyproject.toml", "uv.lock"):
(tmp_path / name).write_text(name, encoding="utf-8")
plain = Path(r"C:\Program Files\WindowsApps\Hermes\agent-payload\tools\python-3.12\python.exe")
assert _inputs(tmp_path, Path(VERBATIM + str(plain))) == _inputs(tmp_path, plain)
def test_no_verbatim_spelling_leaves_through_records_environment_or_uv(tmp_path, monkeypatch):
store = Store(ROOT)
facts = Facts(tmp_path / "facts.json")
facts.record("git", "1", "git-1", {"PATH": [str(store.entry("git-1") / "cmd")]}, store.root)
facts.record("chromium", "1", "chromium-1",
{"AGENT_BROWSER_EXECUTABLE_PATH": str(store.entry("chromium-1") / "chrome.exe")}, store.root)
record = (tmp_path / "facts.json").read_text(encoding="utf-8")
env = compose_env([facts.env_for("git", store.root), facts.env_for("chromium", store.root)], base={})
calls = []
def run(command, **kwargs):
calls.append((command, kwargs))
return subprocess.CompletedProcess(command, 0, "", "")
monkeypatch.setattr(environment_module.subprocess, "run", run)
PythonEnvironment(uv=store.entry("uv-1") / "uv.exe", python=store.entry("python-1") / "python.exe",
destination=tmp_path / "venv", cache=tmp_path / "cache",
env={}).sync(store.entry("source"))
(command, kwargs), = calls
leaving = [record, *env.values(), *command, kwargs["cwd"], *kwargs["env"].values()]
assert [text for text in leaving if VERBATIM in text] == []
assert env["AGENT_BROWSER_EXECUTABLE_PATH"].endswith("chrome.exe")