mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
feat(dashboard): return recent commits from /api/hermes/update/check
Add a best-effort `commits` list (sha/summary/author/at) to the update-check response for git/pip installs that are behind upstream, so the desktop's remote update overlay can show what's changed before applying. Additive and non-breaking: existing consumers (legacy dashboard, tests using subset assertions) ignore the new field. Leaves the shared check_for_updates() int contract untouched — commits come from a separate best-effort git call.
This commit is contained in:
parent
fd1e7c2bc3
commit
9e360681f8
2 changed files with 84 additions and 0 deletions
|
|
@ -1405,6 +1405,49 @@ async def update_hermes():
|
|||
}
|
||||
|
||||
|
||||
def _recent_upstream_commits(n: int = 20) -> List[Dict[str, Any]]:
|
||||
"""Commits the local checkout is behind its upstream by, newest first.
|
||||
|
||||
Best-effort: returns [] if not a git checkout, no upstream is configured,
|
||||
or git is unavailable. Used only to enrich the update-check response —
|
||||
never raises into the request path.
|
||||
"""
|
||||
try:
|
||||
out = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(PROJECT_ROOT),
|
||||
"log",
|
||||
"--format=%H%x1f%s%x1f%an%x1f%ct",
|
||||
"HEAD..@{upstream}",
|
||||
f"-n{int(n)}",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
)
|
||||
if out.returncode != 0:
|
||||
return []
|
||||
rows: List[Dict[str, Any]] = []
|
||||
for line in out.stdout.splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
parts = (line.split("\x1f") + ["", "", "", "0"])[:4]
|
||||
sha, summary, author, at = parts
|
||||
rows.append(
|
||||
{
|
||||
"sha": sha[:7],
|
||||
"summary": summary,
|
||||
"author": author,
|
||||
"at": int(at or 0),
|
||||
}
|
||||
)
|
||||
return rows
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
@app.get("/api/hermes/update/check")
|
||||
async def check_hermes_update(force: bool = False):
|
||||
"""Report whether a Hermes update is available, without applying it.
|
||||
|
|
@ -1425,6 +1468,11 @@ async def check_hermes_update(force: bool = False):
|
|||
user must update out-of-band
|
||||
update_command: the recommended command for this install method
|
||||
message: human-readable guidance for non-applyable methods
|
||||
commits: for git/pip installs that are behind, a list of the commits
|
||||
the local checkout is behind upstream by — each
|
||||
{sha, summary, author, at}. Absent/empty otherwise. The
|
||||
desktop's remote update overlay renders this as "what's
|
||||
changed". Additive: existing consumers ignore it.
|
||||
"""
|
||||
install_method = detect_install_method(PROJECT_ROOT)
|
||||
update_command = recommended_update_command_for_method(install_method)
|
||||
|
|
@ -1467,6 +1515,11 @@ async def check_hermes_update(force: bool = False):
|
|||
payload["message"] = "You're on the latest version."
|
||||
else:
|
||||
payload["update_available"] = True
|
||||
# Enrich with the actual commits we're behind by, so the desktop's
|
||||
# remote update overlay can show "what's changed". git/pip only;
|
||||
# best-effort (empty list on any failure).
|
||||
if install_method in ("git", "pip"):
|
||||
payload["commits"] = await asyncio.to_thread(_recent_upstream_commits)
|
||||
|
||||
return payload
|
||||
|
||||
|
|
|
|||
|
|
@ -701,6 +701,37 @@ class TestUpdateCheckEndpoint:
|
|||
assert body["update_available"] is False
|
||||
assert body["message"]
|
||||
|
||||
def test_git_behind_includes_commits(self, monkeypatch):
|
||||
import hermes_cli.web_server as ws
|
||||
import hermes_cli.banner as banner
|
||||
|
||||
monkeypatch.setattr(ws, "detect_install_method", lambda *a, **k: "git")
|
||||
monkeypatch.setattr(banner, "check_for_updates", lambda: 3)
|
||||
monkeypatch.setattr(
|
||||
ws,
|
||||
"_recent_upstream_commits",
|
||||
lambda n=20: [
|
||||
{"sha": "abc1234", "summary": "feat: x", "author": "a", "at": 1},
|
||||
],
|
||||
)
|
||||
|
||||
body = self.client.get("/api/hermes/update/check").json()
|
||||
# The desktop overlay renders this as the "what's changed" list.
|
||||
assert isinstance(body["commits"], list)
|
||||
assert body["commits"][0]["sha"] == "abc1234"
|
||||
assert body["commits"][0]["summary"] == "feat: x"
|
||||
|
||||
def test_up_to_date_omits_commits(self, monkeypatch):
|
||||
import hermes_cli.web_server as ws
|
||||
import hermes_cli.banner as banner
|
||||
|
||||
monkeypatch.setattr(ws, "detect_install_method", lambda *a, **k: "git")
|
||||
monkeypatch.setattr(banner, "check_for_updates", lambda: 0)
|
||||
|
||||
body = self.client.get("/api/hermes/update/check").json()
|
||||
# No commits list when there's nothing to show (additive, non-breaking).
|
||||
assert body.get("commits", []) == []
|
||||
|
||||
|
||||
class TestDebugShareEndpoint:
|
||||
"""POST /api/ops/debug-share returns the paste URLs synchronously so the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue