Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contributors/emails/xinyu@starfie1d.top
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Starfie1d1272
22 changes: 20 additions & 2 deletions hermes_cli/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -2360,12 +2360,30 @@ def resolve_profile_env(profile_name: str) -> str:

Called early in the CLI entry point, before any hermes modules
are imported, to set the HERMES_HOME environment variable.

When HERMES_HOME is already set, the configured spelling IS the
launch root (it may be a junction/symlink alias of the platform
default). Keep that spelling so profile re-home does not destroy
the launcher's lexical provenance -- the subprocess sanitizer needs
it to match Hermes-owned PYTHONPATH entries written in the same
spelling (#82581 junction follow-up). Physically the paths are
identical (junction-transparent); only the spelling is preserved.
"""
canon = normalize_profile_name(profile_name)
validate_profile_name(canon)
profile_dir = get_profile_dir(canon)
env_home = os.environ.get("HERMES_HOME", "").strip()
if env_home:
env_path = Path(env_home)
# A profile-shaped env value means the root is the grandparent
# (mirrors get_default_hermes_root()).
root = env_path.parent.parent if env_path.parent.name == "profiles" else env_path
else:
root = _get_default_hermes_home()
if canon == "default":
return str(root)
profile_dir = root / "profiles" / canon

if canon != "default" and not profile_dir.is_dir():
if not profile_dir.is_dir():
raise FileNotFoundError(
f"Profile '{canon}' does not exist. "
f"Create it with: hermes profile create {canon}"
Expand Down
49 changes: 49 additions & 0 deletions tests/hermes_cli/test_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,3 +938,52 @@ def test_allowlist_normalizes_deduplicates_and_keeps_default(self, profile_env):



assert set(serve) == {"default", "worker"}
assert serve["worker"] == get_profile_dir("worker")


# ---------------------------------------------------------------------------
# resolve_profile_env spelling preservation (#82581 junction follow-up)
# ---------------------------------------------------------------------------


class TestResolveProfileEnvSpelling:
"""resolve_profile_env() keeps the configured HERMES_HOME spelling as

the launch root (junction installs) while preserving the pre-existing
profile-path handling and existence/validation semantics.
"""

def test_resolution_matrix_preserves_configured_spelling(self, monkeypatch, tmp_path):
"""Resolution matrix over the four pre-existing invariants: root env
-> <root>/profiles/<name>; profile-shaped env -> <root>/profiles/<name>
with no nesting; profile-shaped env + default -> <root>; custom roots
never fall back to the platform default.
"""
root = tmp_path / "configured-root"
(root / "profiles" / "beta").mkdir(parents=True)
(root / "profiles" / "coder").mkdir(parents=True)
custom = tmp_path / "custom-hermes"
(custom / "profiles" / "beta").mkdir(parents=True)
cases = [
(root, "coder", root / "profiles" / "coder"),
(root / "profiles" / "alpha", "beta", root / "profiles" / "beta"),
(root / "profiles" / "alpha", "default", root),
(custom, "beta", custom / "profiles" / "beta"),
]
for env_home, profile, expected in cases:
monkeypatch.setenv("HERMES_HOME", str(env_home))
assert Path(resolve_profile_env(profile)) == expected

def test_missing_named_profile_still_raises(self, monkeypatch, tmp_path):
root = tmp_path / "configured-root"
monkeypatch.setenv("HERMES_HOME", str(root))
with pytest.raises(FileNotFoundError):
resolve_profile_env("nope")

def test_unset_env_falls_back_to_default_root(self, monkeypatch):
# No HERMES_HOME: the platform default root applies (existing contract).
monkeypatch.delenv("HERMES_HOME", raising=False)
assert Path(resolve_profile_env("default")) == _get_default_hermes_home()


Loading
Loading