request-moderator / github_pr.py
Emma Scharfmann
add app
e2bb8e8
Raw
History Blame Contribute Delete
3.8 kB
"""Open a GitHub PR against the website repo for an approved submission.
The site owner still reviews and merges the PR by hand — this only removes
the manual data-entry step of splicing a new object into the right
src/data/*.js array.
"""
import base64
import os
import requests
import formatters
from about import GITHUB_API, GITHUB_REPO
def _headers() -> dict:
token = os.environ["GITHUB_TOKEN"]
return {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
def _default_branch() -> str:
resp = requests.get(f"{GITHUB_API}/repos/{GITHUB_REPO}", headers=_headers())
resp.raise_for_status()
return resp.json()["default_branch"]
def _get_file(path: str, ref: str) -> tuple[str, str]:
"""Return (content, sha) of a file at ref."""
resp = requests.get(
f"{GITHUB_API}/repos/{GITHUB_REPO}/contents/{path}",
headers=_headers(),
params={"ref": ref},
)
resp.raise_for_status()
data = resp.json()
content = base64.b64decode(data["content"]).decode("utf-8")
return content, data["sha"]
def _create_branch(branch: str, from_sha: str) -> None:
resp = requests.post(
f"{GITHUB_API}/repos/{GITHUB_REPO}/git/refs",
headers=_headers(),
json={"ref": f"refs/heads/{branch}", "sha": from_sha},
)
resp.raise_for_status()
def _branch_sha(branch: str) -> str:
resp = requests.get(
f"{GITHUB_API}/repos/{GITHUB_REPO}/git/ref/heads/{branch}", headers=_headers()
)
resp.raise_for_status()
return resp.json()["object"]["sha"]
def _update_file(path: str, branch: str, new_content: str, sha: str, message: str) -> None:
resp = requests.put(
f"{GITHUB_API}/repos/{GITHUB_REPO}/contents/{path}",
headers=_headers(),
json={
"message": message,
"content": base64.b64encode(new_content.encode("utf-8")).decode("ascii"),
"sha": sha,
"branch": branch,
},
)
resp.raise_for_status()
def _open_pr(branch: str, base: str, title: str, body: str) -> str:
resp = requests.post(
f"{GITHUB_API}/repos/{GITHUB_REPO}/pulls",
headers=_headers(),
json={"title": title, "head": branch, "base": base, "body": body},
)
resp.raise_for_status()
return resp.json()["html_url"]
def open_pr_for_submission(submission: dict, fields: dict) -> str:
"""Splice the approved entry into the right data file and open a PR.
`submission` is the row from submissions.py; `fields` are the
reviewer-filled values matching formatters.TARGETS[type_]'s schema.
"""
type_ = submission["type"]
file_path = formatters.target_file(type_)
entry_block = formatters.render_entry(type_, fields)
org_id = fields.get("orgId")
base = _default_branch()
base_sha = _branch_sha(base)
slug = fields.get("id", submission["id"][:8])
branch = f"add/{type_}-{slug}"
_create_branch(branch, base_sha)
content, sha = _get_file(file_path, branch)
new_content = formatters.insert_entry(content, type_, entry_block, org_id)
_update_file(
file_path, branch, new_content, sha,
message=f"Add {type_}: {fields.get('name') or fields.get('title') or slug}",
)
title = f"Add {type_}: {fields.get('name') or fields.get('title') or slug}"
body = (
f"Auto-generated from a huggingscience.co submission via the review Space.\n\n"
f"- **Submission id:** `{submission['id']}`\n"
f"- **Submitted at:** {submission['submitted_at']}\n"
f"- **Source:** {submission['source']}\n\n"
f"**Original description:**\n{submission['description']}\n"
)
return _open_pr(branch, base, title, body)