diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 16eee2489cc9..45ce0a7a8c26 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -8222,8 +8222,11 @@ def _update_node_dependencies() -> None: from hermes_constants import get_default_hermes_root - hermes_root = get_default_hermes_root() - if not _npm_lockfile_changed(hermes_root): + # This cache describes PROJECT_ROOT/node_modules, which is shared by every + # Hermes profile using this checkout. Keep one per-checkout cache under the + # shared Hermes root rather than rerunning npm once per named profile. + shared_hermes_root = get_default_hermes_root() + if not _npm_lockfile_changed(shared_hermes_root): logger.info("npm lockfile unchanged, skipping npm install") return @@ -8271,7 +8274,7 @@ def _update_node_dependencies() -> None: env=nixos_env, ) if ws_result.returncode == 0: - _record_npm_lockfile_hash(hermes_root) + _record_npm_lockfile_hash(shared_hermes_root) print(" ✓ repo root + ui-tui, web workspaces (desktop skipped)") else: print(" ⚠ npm workspace install failed") diff --git a/tests/hermes_cli/test_cmd_update.py b/tests/hermes_cli/test_cmd_update.py index 3be32ab8cf04..b1458cbd3c2f 100644 --- a/tests/hermes_cli/test_cmd_update.py +++ b/tests/hermes_cli/test_cmd_update.py @@ -220,6 +220,40 @@ class TestCmdUpdateNpmLockfileCache: mock_run.assert_not_called() + def test_update_uses_one_shared_npm_cache_across_profiles( + self, tmp_path, monkeypatch + ): + """The npm cache describes checkout-global node_modules, not a profile.""" + from hermes_cli import main as hm + import hermes_constants + + checkout = tmp_path / "checkout" + checkout.mkdir() + (checkout / "package.json").write_text("{}") + shared_root = tmp_path / ".hermes" + named_profile = shared_root / "profiles" / "work" + named_profile.mkdir(parents=True) + + monkeypatch.setattr(hm, "PROJECT_ROOT", checkout) + monkeypatch.setattr(hermes_constants.Path, "home", lambda: tmp_path) + monkeypatch.setattr( + hermes_constants, "find_node_executable", lambda _name: "/usr/bin/npm" + ) + + cache_roots = [] + with patch.object( + hm, + "_npm_lockfile_changed", + side_effect=lambda root: cache_roots.append(root) or False, + ): + monkeypatch.setenv("HERMES_HOME", str(shared_root)) + hm._update_node_dependencies() + + monkeypatch.setenv("HERMES_HOME", str(named_profile)) + hm._update_node_dependencies() + + assert cache_roots == [shared_root, shared_root] + class TestCmdUpdatePip: """Regression tests for pip-install update flows."""