"""Gitea transport adapter. This module owns HTTP mechanics only. Review policy, parsing, and publishing decisions stay in the review layer so they can be tested without a network. """ from __future__ import annotations import json import urllib.error import urllib.request def request( method: str, url: str, token: str, body: dict | None = None, accept: str = "application/json", ) -> tuple[int, bytes]: headers = {"Authorization": f"token {token}", "Accept": accept} data = None if body is not None: data = json.dumps(body).encode() headers["Content-Type"] = "application/json" req = urllib.request.Request(url, data=data, headers=headers, method=method) try: with urllib.request.urlopen(req, timeout=180) as response: return response.status, response.read() except urllib.error.HTTPError as exc: return exc.code, exc.read() except urllib.error.URLError as exc: raise RuntimeError(f"network error: {exc.reason}") from exc class GiteaClient: """Small adapter for repository-scoped Gitea calls.""" def __init__(self, api: str, token: str): self.api = api.rstrip("/") self.token = token def get(self, path: str, accept: str = "application/json") -> tuple[int, bytes]: return request("GET", f"{self.api}/api/v1/repos/{path}", self.token, accept=accept) def post(self, path: str, body: dict) -> tuple[int, bytes]: return request("POST", f"{self.api}/api/v1/repos/{path}", self.token, body)