mirror of
https://github.com/home-assistant/core.git
synced 2026-10-07 14:58:11 -04:00
Co-authored-by: Markus Tuominen <3738613+Markus98@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
87 lines
2.8 KiB
Python
87 lines
2.8 KiB
Python
"""Read pull request data and repository files from the GitHub API."""
|
|
|
|
from collections.abc import Iterator
|
|
import os
|
|
from typing import Any
|
|
|
|
import requests
|
|
|
|
from .models import PullRequest
|
|
|
|
_TIMEOUT = 30
|
|
_JSON = "application/vnd.github+json"
|
|
_DIFF = "application/vnd.github.v3.diff"
|
|
|
|
|
|
def _session(token: str, accept: str = _JSON) -> requests.Session:
|
|
"""Return a session authenticated for the GitHub API."""
|
|
session = requests.Session()
|
|
session.headers.update(
|
|
{
|
|
"Authorization": f"Bearer {token}",
|
|
"Accept": accept,
|
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
}
|
|
)
|
|
return session
|
|
|
|
|
|
def _rest_url(*parts: str) -> str:
|
|
"""Return the REST API URL of a path below the API root."""
|
|
root = os.environ.get("GITHUB_API_URL", "https://api.github.com").rstrip("/")
|
|
return "/".join([root, *parts])
|
|
|
|
|
|
def _paginate(session: requests.Session, url: str) -> Iterator[dict[str, Any]]:
|
|
"""Yield every item of a paginated list endpoint."""
|
|
params: dict[str, Any] | None = {"per_page": 100}
|
|
while url:
|
|
response = session.get(url, params=params, timeout=_TIMEOUT)
|
|
response.raise_for_status()
|
|
yield from response.json()
|
|
url = response.links.get("next", {}).get("url", "")
|
|
params = None
|
|
|
|
|
|
def fetch_pull_request(repo: str, number: int, token: str) -> PullRequest:
|
|
"""Return the pull request metadata and the names of its changed files."""
|
|
session = _session(token)
|
|
url = _rest_url("repos", repo, "pulls", str(number))
|
|
response = session.get(url, timeout=_TIMEOUT)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
return PullRequest(
|
|
number=data["number"],
|
|
title=data["title"],
|
|
body=data["body"] or "",
|
|
head_sha=data["head"]["sha"],
|
|
base_ref=data["base"]["ref"],
|
|
additions=data["additions"],
|
|
deletions=data["deletions"],
|
|
changed_files=data["changed_files"],
|
|
file_statuses={
|
|
file["filename"]: file["status"]
|
|
for file in _paginate(session, f"{url}/files")
|
|
},
|
|
)
|
|
|
|
|
|
def fetch_diff(repo: str, number: int, token: str) -> str:
|
|
"""Return the unified diff of the pull request."""
|
|
response = _session(token, accept=_DIFF).get(
|
|
_rest_url("repos", repo, "pulls", str(number)), timeout=_TIMEOUT
|
|
)
|
|
response.raise_for_status()
|
|
return response.text
|
|
|
|
|
|
def graphql(query: str, token: str) -> dict[str, Any]:
|
|
"""Run a GraphQL query and return its `data` payload."""
|
|
url = os.environ.get("GITHUB_GRAPHQL_URL", "https://api.github.com/graphql")
|
|
response = _session(token).post(url, json={"query": query}, timeout=_TIMEOUT)
|
|
response.raise_for_status()
|
|
payload = response.json()
|
|
if errors := payload.get("errors"):
|
|
raise RuntimeError(f"GraphQL query failed: {errors}")
|
|
return payload["data"]
|