fix(nix): let the install-method stamp name a home-manager install

detect_install_method reads the stamp against an allowlist. The
allowlist held "nixos" but not "home-manager", and a stamp that names
home-manager gave "unknown". The managed path (step 3) returned the
correct name, so the gap was invisible: it appeared only for an install
that carries a stamp.

An install with the value "unknown" gets "hermes update" as its update
guidance. That command is the one command a managed install refuses, so
the user gets a dead end.

The test for this was also environment-dependent. It called the real
get_project_root(), and it passed here only because this worktree
carries no stamp. A checkout from the curl installer carries a "git"
stamp, and the assertion then failed for the contributor and not for
us. The test now detects against a temporary install tree.

The new test stamps each managed system and asserts the value that
comes back. With the allowlist reverted, the home-manager case fails
with "assert 'unknown' == 'home-manager'". The nixos case passes,
because that name was already in the allowlist.
This commit is contained in:
ethernet
2026-08-18 20:23:07 -04:00
parent 00c3872882
commit 1f234a1033
2 changed files with 33 additions and 4 deletions
+6 -2
View File
@@ -425,7 +425,8 @@ def _install_method_project_root(project_root: Optional[Path] = None) -> Path:
def detect_install_method(project_root: Optional[Path] = None) -> str:
"""Detect how Hermes was installed: 'apt', 'docker', 'nix', 'nixos', 'git', or 'unknown'.
"""Detect how Hermes was installed: 'apt', 'docker', 'nix', 'nixos',
'home-manager', 'git', or 'unknown'.
Resolution order:
1. Code-scoped stamp ``<install tree>/.install_method`` (next to the
@@ -473,7 +474,10 @@ def detect_install_method(project_root: Optional[Path] = None) -> str:
# generic Debian/Ubuntu APT signal. If another APT-managed distribution is
# added, give it a distinct install method or make update-command selection
# platform-aware instead of silently reusing Termux's `pkg` command.
supported_methods = {"apt", "docker", "nix", "nixos", "git", "unknown"}
# "home-manager" is here because step 3 can return it. A stamp must name
# every method that this function returns. Without it, the stamp of a
# home-manager install gives "unknown".
supported_methods = {"apt", "docker", "nix", "nixos", "home-manager", "git", "unknown"}
# 1. Code-scoped stamp — authoritative, immune to shared $HERMES_HOME.
try:
@@ -79,20 +79,45 @@ def test_env_var_wins_over_the_marker(hermes_home, monkeypatch):
@pytest.mark.parametrize("managed_value", ["nixos", "home-manager"])
def test_managed_install_names_its_system_and_offers_an_update(
hermes_home, monkeypatch, managed_value
hermes_home, monkeypatch, tmp_path, managed_value
):
"""The message names the system, so the user knows what owns the install."""
monkeypatch.setenv("HERMES_MANAGED", managed_value)
# This test uses an install tree of its own. The real checkout can carry
# a stamp from the install shape of the contributor. A stamp answers
# first, and detection never reaches the managed state under test.
install_tree = tmp_path / "install"
install_tree.mkdir()
assert managed_value in config_mod.format_managed_message("set model")
assert "set model" in config_mod.format_managed_message("set model")
assert config_mod.get_managed_update_command()
assert config_mod.detect_install_method(config_mod.get_project_root()) == managed_value
assert config_mod.detect_install_method(install_tree) == managed_value
# `hermes update` cannot run on a managed install, so the advice must not
# name it.
assert config_mod.recommended_update_command() != "hermes update"
@pytest.mark.parametrize("managed_value", ["nixos", "home-manager"])
def test_a_stamp_can_name_every_managed_system(
hermes_home, monkeypatch, tmp_path, managed_value
):
"""A stamp must give back every value that detection can return.
Detection reads the stamp against an allowlist. A managed system that is
absent from that allowlist gives "unknown". The update guidance then
names a command that the managed guard refuses.
"""
monkeypatch.delenv("HERMES_MANAGED", raising=False)
install_tree = tmp_path / "install"
install_tree.mkdir()
config_mod.stamp_install_method(managed_value, project_root=install_tree)
assert config_mod.detect_install_method(install_tree) == managed_value
def test_unmanaged_install_offers_no_update_command(hermes_home, monkeypatch):
monkeypatch.delenv("HERMES_MANAGED", raising=False)
assert config_mod.get_managed_update_command() is None