Files
ci-ai/scripts/gitea_api.py
Tarik Moussa daf89ff8b9 ci-ai: KI-Schicht für die Gitea-CI (PR-Review, Security-Erklärung, CI-Failure-Analyse)
Provider-agnostische LLM-Anbindung (Anthropic jetzt, Jetson/Ollama später per
Env-Var umschaltbar). Stdlib-only Skripte + Beispiel-Workflows. GitLab-Duo-Parität
für self-hosted Gitea + act_runner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 15:38:53 +02:00

71 lines
2.6 KiB
Python

"""Schlanke Gitea-API-Helfer (stdlib only).
- PR-Diff holen
- Kommentare IDEMPOTENT posten: ein versteckter HTML-Marker identifiziert den
Bot-Kommentar; existiert er schon, wird er editiert statt neu angelegt
(kein Zuspammen bei jedem Push).
Env-Vars:
GITEA_API Basis-URL der API, Default https://git.eulernest.eu/api/v1
GITEA_TOKEN Token fürs Kommentieren (Actions-Auto-Token ODER PAT).
Fallback: GITEA_BOT_TOKEN.
"""
import json
import os
import urllib.error
import urllib.request
_BASE = os.environ.get("GITEA_API", "https://git.eulernest.eu/api/v1").rstrip("/")
_TOKEN = os.environ.get("GITEA_TOKEN") or os.environ.get("GITEA_BOT_TOKEN", "")
def _request(method: str, path: str, *, data=None, raw: bool = False):
url = f"{_BASE}{path}"
headers = {}
if _TOKEN:
headers["Authorization"] = f"token {_TOKEN}"
body = None
if data is not None:
body = json.dumps(data).encode("utf-8")
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=body, headers=headers, method=method)
try:
with urllib.request.urlopen(req, timeout=60) as resp:
content = resp.read().decode("utf-8", "replace")
if raw:
return content
return json.loads(content) if content.strip() else None
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", "replace")
raise SystemExit(f"[gitea] {method} {path} -> HTTP {exc.code}: {detail[:400]}")
except urllib.error.URLError as exc:
raise SystemExit(f"[gitea] {method} {path} fehlgeschlagen: {exc.reason}")
def get_pr_diff(owner: str, repo: str, index: int) -> str:
"""Unified Diff eines PRs (Gitea unterstützt den .diff-Suffix)."""
return _request("GET", f"/repos/{owner}/{repo}/pulls/{index}.diff", raw=True)
def upsert_comment(owner: str, repo: str, index: int, marker: str, body: str) -> int:
"""Legt einen Kommentar an ODER editiert den vorhandenen mit gleichem Marker.
PR-Kommentare sind in Gitea Issue-Kommentare (gleicher Endpunkt).
"""
full = f"{body}\n\n{marker}"
existing = _request("GET", f"/repos/{owner}/{repo}/issues/{index}/comments") or []
for comment in existing:
if marker in (comment.get("body") or ""):
_request(
"PATCH",
f"/repos/{owner}/{repo}/issues/comments/{comment['id']}",
data={"body": full},
)
return comment["id"]
created = _request(
"POST",
f"/repos/{owner}/{repo}/issues/{index}/comments",
data={"body": full},
)
return created["id"]