Download core/github.py from abc1181/Jaarvis: direct link, hf CLI and curl.
- Browser
- Download file 11 kB
-
https://huggingface.co/spaces/abc1181/Jaarvis/resolve/main/core/github.py
- Command line
-
hf download hf://spaces/abc1181/Jaarvis/core/github.py
-
curl -L -o github.py https://huggingface.co/spaces/abc1181/Jaarvis/resolve/main/core/github.py
11 kB
| """GitHub: what the user's code is doing right now. | |
| Read paths are free. Write paths — comment, open an issue, push, merge — are | |
| registered in `core/policy.py` so the approval gate asks first, and this module | |
| does not attempt to be clever about that. It has no opinion on whether you | |
| approved; it only refuses to invent a token. | |
| The interesting one is `failing_ci`, because that is what a background watch | |
| calls: pull the recent workflow runs, and say which ones broke and on what. | |
| """ | |
| from __future__ import annotations | |
| from typing import Any, Callable, Optional | |
| from core import svc | |
| API = "https://api.github.com" | |
| def _token() -> str: | |
| return svc.get_token("github_token", "github_pat", "gh_token") | |
| def configured() -> bool: | |
| return bool(_token()) | |
| def _h() -> dict: | |
| return {"Authorization": f"Bearer {_token()}", | |
| "X-GitHub-Api-Version": "2022-11-28"} | |
| def _call(method: str, path: str, *, params: Optional[dict] = None, | |
| body: Any = None, fetcher: Optional[Callable] = None) -> Any: | |
| url = API + path + svc.qs(params or {}) | |
| if fetcher: | |
| return fetcher(method, url, body) | |
| return svc.request(method, url, headers=_h(), body=body) | |
| # ── read ───────────────────────────────────────────────────────────────────── | |
| def whoami(fetcher: Optional[Callable] = None) -> dict: | |
| u = _call("GET", "/user", fetcher=fetcher) or {} | |
| return {"login": u.get("login", ""), "name": u.get("name", ""), | |
| "public_repos": u.get("public_repos", 0), | |
| "html_url": u.get("html_url", "")} | |
| def repos(limit: int = 20, fetcher: Optional[Callable] = None) -> list[dict]: | |
| rows = _call("GET", "/user/repos", | |
| params={"sort": "pushed", "per_page": limit}, | |
| fetcher=fetcher) or [] | |
| out = [] | |
| for r in rows if isinstance(rows, list) else []: | |
| out.append({ | |
| "full_name": r.get("full_name", ""), | |
| "private": bool(r.get("private")), | |
| "language": r.get("language") or "", | |
| "stars": r.get("stargazers_count", 0), | |
| "open_issues": r.get("open_issues_count", 0), | |
| "default_branch": r.get("default_branch", "main"), | |
| "pushed_at": r.get("pushed_at", ""), | |
| "url": r.get("html_url", ""), | |
| }) | |
| return out | |
| def _repo_path(repo: str, rest: str = "") -> str: | |
| r = str(repo or "").strip().strip("/") | |
| if "/" not in r: | |
| raise svc.ServiceError(f"'{r}' is not owner/repo — I need the full name, " | |
| f"like facebook/react.") | |
| return f"/repos/{r}{rest}" | |
| def prs(repo: str, *, state: str = "open", limit: int = 15, | |
| fetcher: Optional[Callable] = None) -> list[dict]: | |
| rows = _call("GET", _repo_path(repo, "/pulls"), | |
| params={"state": state, "per_page": limit}, | |
| fetcher=fetcher) or [] | |
| out = [] | |
| for p in rows if isinstance(rows, list) else []: | |
| out.append({ | |
| "number": p.get("number"), "title": p.get("title", ""), | |
| "user": (p.get("user") or {}).get("login", ""), | |
| "draft": bool(p.get("draft")), | |
| "created_at": p.get("created_at", ""), "updated_at": p.get("updated_at", ""), | |
| "url": p.get("html_url", ""), | |
| "head": ((p.get("head") or {}).get("ref") or ""), | |
| "base": ((p.get("base") or {}).get("ref") or ""), | |
| }) | |
| return out | |
| def issues(repo: str, *, state: str = "open", limit: int = 15, | |
| fetcher: Optional[Callable] = None) -> list[dict]: | |
| rows = _call("GET", _repo_path(repo, "/issues"), | |
| params={"state": state, "per_page": limit}, | |
| fetcher=fetcher) or [] | |
| # The issues endpoint returns pull requests too; a PR in an issue list is | |
| # noise the assistant will otherwise narrate as a bug report. | |
| return [{"number": i.get("number"), "title": i.get("title", ""), | |
| "labels": [l.get("name") for l in (i.get("labels") or [])], | |
| "user": (i.get("user") or {}).get("login", ""), | |
| "created_at": i.get("created_at", ""), "url": i.get("html_url", "")} | |
| for i in rows if isinstance(rows, list) and "pull_request" not in i] | |
| def runs(repo: str, *, limit: int = 15, branch: str = "", | |
| fetcher: Optional[Callable] = None) -> list[dict]: | |
| params = {"per_page": limit} | |
| if branch: | |
| params["branch"] = branch | |
| data = _call("GET", _repo_path(repo, "/actions/runs"), params=params, | |
| fetcher=fetcher) or {} | |
| out = [] | |
| for r in ((data.get("workflow_runs") or []) if isinstance(data, dict) else []): | |
| out.append({ | |
| "id": r.get("id"), "name": r.get("name", ""), | |
| "status": r.get("status", ""), "conclusion": r.get("conclusion") or "", | |
| "branch": (r.get("head_branch") or ""), | |
| "commit": ((r.get("head_commit") or {}) or {}).get("message", "")[:120], | |
| "created_at": r.get("created_at", ""), "url": r.get("html_url", ""), | |
| }) | |
| return out | |
| def failing_ci(repo: str, *, limit: int = 20, | |
| fetcher: Optional[Callable] = None) -> list[dict]: | |
| """Runs that actually broke. A watch calls this; a human rarely will.""" | |
| return [r for r in runs(repo, limit=limit, fetcher=fetcher) | |
| if r.get("conclusion") in ("failure", "timed_out", "cancelled") | |
| or (r.get("status") == "completed" | |
| and r.get("conclusion") not in ("success", "skipped", None, ""))] | |
| def run_log(repo: str, run_id: Any, fetcher: Optional[Callable] = None) -> str: | |
| """The tail of a failing job's log — where the actual error is.""" | |
| if fetcher: | |
| return str(fetcher("GET", f"{API}{_repo_path(repo, f'/actions/runs/{run_id}/logs')}", | |
| None) or "") | |
| import urllib.request | |
| req = urllib.request.Request( | |
| f"{API}{_repo_path(repo, f'/actions/runs/{run_id}/logs')}", | |
| headers=dict(_h(), Accept="application/vnd.github+json")) | |
| try: | |
| with urllib.request.urlopen(req, timeout=20) as r: | |
| return r.read().decode("utf-8", "replace")[-4000:] | |
| except Exception as e: | |
| return f"(log unavailable: {type(e).__name__})" | |
| # ── write (approval-gated upstream) ────────────────────────────────────────── | |
| def comment(repo: str, number: Any, text: str, | |
| fetcher: Optional[Callable] = None) -> dict: | |
| r = _call("POST", _repo_path(repo, f"/issues/{number}/comments"), | |
| body={"body": str(text)}, fetcher=fetcher) or {} | |
| return {"ok": True, "url": r.get("html_url", "")} | |
| def open_issue(repo: str, title: str, text: str = "", | |
| labels: str = "", fetcher: Optional[Callable] = None) -> dict: | |
| body: dict = {"title": str(title)} | |
| if text: | |
| body["body"] = str(text) | |
| lab = [l.strip() for l in str(labels or "").split(",") if l.strip()] | |
| if lab: | |
| body["labels"] = lab | |
| r = _call("POST", _repo_path(repo, "/issues"), body=body, fetcher=fetcher) or {} | |
| return {"ok": True, "number": r.get("number"), "url": r.get("html_url", "")} | |
| # ── the model-facing surface ───────────────────────────────────────────────── | |
| def tool(action: str = "", *, repo: str = "", number: Any = "", text: str = "", | |
| title: str = "", limit: int = 15, branch: str = "", | |
| fetcher: Optional[Callable] = None) -> str: | |
| a = str(action or "").strip().lower() | |
| if not configured() and fetcher is None: | |
| return ("GitHub is not connected — add a token in Settings (scope: repo " | |
| "for private repositories, public_repo if only public).") | |
| try: | |
| if a in ("", "repos", "list"): | |
| rows = repos(limit=limit, fetcher=fetcher) | |
| if not rows: | |
| return "No repositories found." | |
| return "\n".join( | |
| f"- {r['full_name']} · {r['language'] or '—'} · " | |
| f"{r['open_issues']} open · pushed {r['pushed_at'][:10]}" | |
| for r in rows) | |
| if a in ("me", "whoami", "who"): | |
| u = whoami(fetcher=fetcher) | |
| return f"Signed in as {u['login']} ({u['name'] or 'no name'})." | |
| if a in ("prs", "pr", "pulls", "reviews"): | |
| rows = prs(repo, limit=limit, fetcher=fetcher) | |
| if not rows: | |
| return f"No open pull requests on {repo or 'that repo'}." | |
| return "\n".join(f"- #{r['number']} {r['title']} " | |
| f"({r['user']}{', draft' if r['draft'] else ''})" | |
| for r in rows) | |
| if a in ("issues", "issue"): | |
| rows = issues(repo, limit=limit, fetcher=fetcher) | |
| if not rows: | |
| return f"No open issues on {repo or 'that repo'}." | |
| return "\n".join(f"- #{r['number']} {r['title']}" | |
| + (f" [{', '.join(r['labels'])}]" if r["labels"] else "") | |
| for r in rows) | |
| if a in ("ci", "runs", "workflows", "actions"): | |
| rows = runs(repo, limit=limit, branch=branch, fetcher=fetcher) | |
| if not rows: | |
| return f"No workflow runs recorded for {repo or 'that repo'}." | |
| return "\n".join(f"- {r['name']}: {r['status']}" | |
| + (f" · {r['conclusion']}" if r["conclusion"] else "") | |
| + f" · {r['commit'][:60]}" for r in rows) | |
| if a in ("failing", "broken", "red"): | |
| rows = failing_ci(repo, fetcher=fetcher) | |
| if not rows: | |
| return f"CI is green on {repo or 'the repo'}. Nothing failing." | |
| return ("CI is red on " + repo + ":\n" + "\n".join( | |
| f"- {r['name']} {r['conclusion']} · {r['branch']} · " | |
| f"{r['commit'][:60]}" for r in rows[:10])) | |
| if a == "log": | |
| if not repo or not number: | |
| return "Give me the repo and the run id." | |
| return run_log(repo, number, fetcher=fetcher) | |
| if a in ("comment", "say"): | |
| if not repo or not number: | |
| return "Give me the repo and the issue/PR number to comment on." | |
| r = comment(repo, number, text, fetcher=fetcher) | |
| return f"Commented on #{number} in {repo}. {r['url']}" | |
| if a in ("open", "create", "new"): | |
| if not repo: | |
| return "Give me the repo." | |
| r = open_issue(repo, title or text, text, fetcher=fetcher) | |
| return f"Opened #{r['number']} in {repo}. {r['url']}" | |
| return "Unknown action. Use repos / prs / issues / ci / failing / log / comment / open." | |
| except svc.ServiceError as e: | |
| return f"GitHub: {e.message}" | |
| except Exception as e: | |
| return f"{type(e).__name__}: {e}"[:180] | |