diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 93ca26e90e..e59d370847 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -8929,7 +8929,10 @@ def _cmd_update_pip(args): cmd = [sys.executable, "-m", "pip", "install", "--upgrade", "hermes-agent"] print(f"→ Running: {' '.join(cmd)}") - result = subprocess.run(cmd) + run_kwargs = {} + if sys.prefix != sys.base_prefix: + run_kwargs["env"] = {**os.environ, "VIRTUAL_ENV": sys.prefix} + result = subprocess.run(cmd, **run_kwargs) if result.returncode != 0: print("✗ Update failed") sys.exit(1) diff --git a/tests/hermes_cli/test_cmd_update.py b/tests/hermes_cli/test_cmd_update.py index 0cb8d033eb..ed9033ffce 100644 --- a/tests/hermes_cli/test_cmd_update.py +++ b/tests/hermes_cli/test_cmd_update.py @@ -39,6 +39,45 @@ def mock_args(): return SimpleNamespace() +class TestCmdUpdatePip: + """Regression tests for pip-install update flows.""" + + @patch("shutil.which", return_value="/usr/bin/uv") + @patch("subprocess.run") + def test_update_pip_exports_virtualenv_from_sys_prefix( + self, mock_run, _mock_which, mock_args, monkeypatch + ): + from hermes_cli import main as hm + + mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="") + monkeypatch.delenv("VIRTUAL_ENV", raising=False) + monkeypatch.setattr(hm.sys, "prefix", "/tmp/hermes-launcher-venv") + monkeypatch.setattr(hm.sys, "base_prefix", "/usr") + + hm._cmd_update_pip(mock_args) + + assert mock_run.call_count == 1 + assert mock_run.call_args.args[0] == ["/usr/bin/uv", "pip", "install", "--upgrade", "hermes-agent"] + assert mock_run.call_args.kwargs["env"]["VIRTUAL_ENV"] == "/tmp/hermes-launcher-venv" + + @patch("shutil.which", return_value="/usr/bin/uv") + @patch("subprocess.run") + def test_update_pip_does_not_export_virtualenv_for_system_python( + self, mock_run, _mock_which, mock_args, monkeypatch + ): + from hermes_cli import main as hm + + mock_run.return_value = subprocess.CompletedProcess([], 0, stdout="", stderr="") + monkeypatch.delenv("VIRTUAL_ENV", raising=False) + monkeypatch.setattr(hm.sys, "prefix", "/usr") + monkeypatch.setattr(hm.sys, "base_prefix", "/usr") + + hm._cmd_update_pip(mock_args) + + assert mock_run.call_count == 1 + assert "env" not in mock_run.call_args.kwargs + + class TestCmdUpdateBranchFallback: """cmd_update falls back to main when current branch has no remote counterpart."""