Skip to content

Commit 103f51a

Browse files
jjjojojteknium1
authored andcommitted
fix(doctor): check gh auth status when GITHUB_TOKEN absent
hermes doctor showed 'No GITHUB_TOKEN (60 req/hr)' warning even when users had authenticated via gh auth login. Now falls back to gh auth status --json authenticated when GITHUB_TOKEN and GH_TOKEN are both unset. Fixes #16115
1 parent 8ab9f61 commit 103f51a

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

hermes_cli/doctor.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,9 +1264,23 @@ def run_doctor(args):
12641264
check_warn("Skills Hub directory not initialized", "(run: hermes skills list)")
12651265

12661266
from hermes_cli.config import get_env_value
1267+
1268+
def _gh_authenticated() -> bool:
1269+
"""Check if gh CLI is authenticated via token file or device flow."""
1270+
try:
1271+
result = subprocess.run(
1272+
["gh", "auth", "status", "--json", "authenticated"],
1273+
capture_output=True, timeout=10,
1274+
)
1275+
return result.returncode == 0
1276+
except (FileNotFoundError, subprocess.TimeoutExpired):
1277+
return False
1278+
12671279
github_token = get_env_value("GITHUB_TOKEN") or get_env_value("GH_TOKEN")
12681280
if github_token:
12691281
check_ok("GitHub token configured (authenticated API access)")
1282+
elif _gh_authenticated():
1283+
check_ok("GitHub authenticated via gh CLI", "(full API access — no GITHUB_TOKEN needed)")
12701284
else:
12711285
check_warn("No GITHUB_TOKEN", f"(60 req/hr rate limit — set in {_DHH}/.env for better rates)")
12721286

tests/hermes_cli/test_doctor.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,3 +663,79 @@ def fake_get(url, headers=None, timeout=None):
663663
)
664664
assert not any(url == "https://opencode.ai/zen/go/v1/models" for url, _, _ in calls)
665665
assert not any("opencode" in url.lower() and "models" in url.lower() for url, _, _ in calls)
666+
667+
668+
class TestGitHubTokenCheck:
669+
"""Tests for GitHub token / gh auth detection in doctor."""
670+
671+
def test_no_token_and_not_gh_authenticated_shows_warn(self, monkeypatch, tmp_path):
672+
home = tmp_path / ".hermes"
673+
home.mkdir(parents=True, exist_ok=True)
674+
monkeypatch.setenv("HERMES_HOME", str(home))
675+
monkeypatch.setenv("PATH", "/nonexistent") # gh not found
676+
677+
from hermes_cli.doctor import run_doctor, _DHH
678+
import io, contextlib
679+
680+
buf = io.StringIO()
681+
with contextlib.redirect_stdout(buf):
682+
run_doctor(Namespace(fix=False))
683+
out = buf.getvalue()
684+
685+
assert "No GITHUB_TOKEN" in out
686+
assert "60 req/hr" in out
687+
688+
def test_token_env_present_shows_ok(self, monkeypatch, tmp_path):
689+
home = tmp_path / ".hermes"
690+
home.mkdir(parents=True, exist_ok=True)
691+
monkeypatch.setenv("HERMES_HOME", str(home))
692+
monkeypatch.setenv("GITHUB_TOKEN", "ghp_test123")
693+
monkeypatch.setenv("PATH", "/nonexistent") # gh not found
694+
695+
from hermes_cli.doctor import run_doctor
696+
import io, contextlib
697+
698+
buf = io.StringIO()
699+
with contextlib.redirect_stdout(buf):
700+
run_doctor(Namespace(fix=False))
701+
out = buf.getvalue()
702+
703+
assert "GitHub token configured" in out
704+
705+
def test_gh_authenticated_without_env_token_shows_ok(self, monkeypatch, tmp_path):
706+
home = tmp_path / ".hermes"
707+
home.mkdir(parents=True, exist_ok=True)
708+
monkeypatch.setenv("HERMES_HOME", str(home))
709+
# No GITHUB_TOKEN or GH_TOKEN
710+
monkeypatch.delenv("GITHUB_TOKEN", raising=False)
711+
monkeypatch.delenv("GH_TOKEN", raising=False)
712+
713+
# Mock gh to return success
714+
import shutil
715+
real_which = shutil.which
716+
def mock_which(cmd):
717+
return "/usr/local/bin/gh" if cmd == "gh" else real_which(cmd)
718+
monkeypatch.setattr(shutil, "which", mock_which)
719+
720+
call_log = []
721+
def mock_run(cmd, **kwargs):
722+
call_log.append(cmd)
723+
if cmd[:2] == ["gh", "auth"]:
724+
result = types.SimpleNamespace(returncode=0, stdout="", stderr="")
725+
else:
726+
result = types.SimpleNamespace(returncode=1, stdout="", stderr="")
727+
return result
728+
729+
import subprocess
730+
monkeypatch.setattr(subprocess, "run", mock_run)
731+
732+
from hermes_cli.doctor import run_doctor
733+
import io, contextlib
734+
735+
buf = io.StringIO()
736+
with contextlib.redirect_stdout(buf):
737+
run_doctor(Namespace(fix=False))
738+
out = buf.getvalue()
739+
740+
assert "gh auth" in str(call_log) or any(c[0] == "gh" for c in call_log), f"gh not called: {call_log}"
741+
assert "GitHub authenticated via gh CLI" in out or "token configured" in out

0 commit comments

Comments
 (0)