fix(cli): keep passive update checks noninteractive

This commit is contained in:
RobbertC5
2026-09-02 17:37:08 +02:00
committed by Teknium
parent 1cb3ab6173
commit 6e775907d7
2 changed files with 35 additions and 1 deletions

View File

@@ -241,11 +241,15 @@ def _is_full_sha(value: Optional[str]) -> bool:
def _upstream_main_sha() -> Optional[str]:
"""Tip SHA of upstream main via HTTPS ls-remote (no auth, no prompts)."""
from hermes_cli._subprocess_compat import noninteractive_git_env
try:
result = subprocess.run(
["git", "ls-remote", _UPSTREAM_REPO_URL, "refs/heads/main"],
capture_output=True, text=True, encoding="utf-8", errors="replace",
timeout=10,
stdin=subprocess.DEVNULL,
env=noninteractive_git_env(),
)
except Exception:
return None
@@ -277,6 +281,8 @@ def _check_via_rev(local_rev: str) -> Optional[int]:
def _check_via_local_git(repo_dir: Path) -> Optional[int]:
"""Count commits behind origin/main in a local checkout."""
from hermes_cli._subprocess_compat import noninteractive_git_env
origin_url = _git_stdout(["remote", "get-url", "origin"], cwd=repo_dir)
if _is_official_ssh_remote(origin_url):
head_rev = _git_stdout(["rev-parse", "HEAD"], cwd=repo_dir)
@@ -348,6 +354,8 @@ def _check_via_local_git(repo_dir: Path) -> Optional[int]:
fetch_args,
capture_output=True, timeout=10,
cwd=str(repo_dir),
stdin=subprocess.DEVNULL,
env=noninteractive_git_env(),
)
fetch_ok = fetch_proc.returncode == 0
except Exception:

View File

@@ -23,7 +23,10 @@ def test_check_for_updates_uses_cache(tmp_path, monkeypatch):
(repo_dir / ".git").mkdir()
cache_file = tmp_path / ".update_check"
cache_file.write_text(json.dumps({"ts": time.time(), "behind": 3, "ver": __version__}))
cache_file.write_text(
json.dumps({"ts": time.time(), "behind": 3, "ver": __version__}),
encoding="utf-8",
)
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
with patch("hermes_cli.banner.subprocess.run") as mock_run:
@@ -58,6 +61,21 @@ def test_prefetch_non_blocking():
assert banner._update_result == 5
def test_upstream_main_sha_disables_git_prompts(monkeypatch):
"""The passive HTTPS probe must never inherit the interactive terminal."""
from hermes_cli import banner
completed = MagicMock(returncode=1, stdout="", stderr="auth required")
run = MagicMock(return_value=completed)
monkeypatch.setattr(banner.subprocess, "run", run)
assert banner._upstream_main_sha() is None
kwargs = run.call_args.kwargs
assert kwargs["stdin"] is banner.subprocess.DEVNULL
assert kwargs["env"]["GIT_TERMINAL_PROMPT"] == "0"
assert kwargs["env"]["GCM_INTERACTIVE"] == "Never"
def test_check_via_local_git_fetch_failure_returns_none(tmp_path, monkeypatch):
"""When git fetch fails and the stale origin/main ref is not ahead,
_check_via_local_git must return None (#82166).
@@ -90,8 +108,12 @@ def test_check_via_local_git_fetch_failure_returns_none(tmp_path, monkeypatch):
stale_zero_proc.returncode = 0
stale_zero_proc.stdout = "0"
fetch_kwargs = None
def mock_run(args, **kwargs):
nonlocal fetch_kwargs
if args[:2] == ["git", "fetch"]:
fetch_kwargs = kwargs
return failed_proc
if args[:2] == ["git", "rev-list"]:
return stale_zero_proc
@@ -104,6 +126,10 @@ def test_check_via_local_git_fetch_failure_returns_none(tmp_path, monkeypatch):
assert result is None, (
"Fetch failure with stale 0-behind must return None, not 'up to date'"
)
assert fetch_kwargs is not None
assert fetch_kwargs["stdin"] is banner.subprocess.DEVNULL
assert fetch_kwargs["env"]["GIT_TERMINAL_PROMPT"] == "0"
assert fetch_kwargs["env"]["GCM_INTERACTIVE"] == "Never"
def test_check_via_local_git_fetch_failure_keeps_positive_stale_count(tmp_path, monkeypatch):