hugging-science/request-moderator
0
1"""Open a GitHub PR against the website repo for an approved submission.2 3The site owner still reviews and merges the PR by hand — this only removes4the manual data-entry step of splicing a new object into the right5src/data/*.js array.6"""7 8import base649import os10 11import requests12 13import formatters14from about import GITHUB_API, GITHUB_REPO15 16 17def _headers() -> dict:18 token = os.environ["GITHUB_TOKEN"]19 return {20 "Authorization": f"Bearer {token}",21 "Accept": "application/vnd.github+json",22 "X-GitHub-Api-Version": "2022-11-28",23 }24 25 26def _default_branch() -> str:27 resp = requests.get(f"{GITHUB_API}/repos/{GITHUB_REPO}", headers=_headers())28 resp.raise_for_status()29 return resp.json()["default_branch"]30 31 32def _get_file(path: str, ref: str) -> tuple[str, str]:33 """Return (content, sha) of a file at ref."""34 resp = requests.get(35 f"{GITHUB_API}/repos/{GITHUB_REPO}/contents/{path}",36 headers=_headers(),37 params={"ref": ref},38 )39 resp.raise_for_status()40 data = resp.json()41 content = base64.b64decode(data["content"]).decode("utf-8")42 return content, data["sha"]43 44 45def _create_branch(branch: str, from_sha: str) -> None:46 resp = requests.post(47 f"{GITHUB_API}/repos/{GITHUB_REPO}/git/refs",48 headers=_headers(),49 json={"ref": f"refs/heads/{branch}", "sha": from_sha},50 )51 resp.raise_for_status()52 53 54def _branch_sha(branch: str) -> str:55 resp = requests.get(56 f"{GITHUB_API}/repos/{GITHUB_REPO}/git/ref/heads/{branch}", headers=_headers()57 )58 resp.raise_for_status()59 return resp.json()["object"]["sha"]60 61 62def _update_file(path: str, branch: str, new_content: str, sha: str, message: str) -> None:63 resp = requests.put(64 f"{GITHUB_API}/repos/{GITHUB_REPO}/contents/{path}",65 headers=_headers(),66 json={67 "message": message,68 "content": base64.b64encode(new_content.encode("utf-8")).decode("ascii"),69 "sha": sha,70 "branch": branch,71 },72 )73 resp.raise_for_status()74 75 76def _open_pr(branch: str, base: str, title: str, body: str) -> str:77 resp = requests.post(78 f"{GITHUB_API}/repos/{GITHUB_REPO}/pulls",79 headers=_headers(),80 json={"title": title, "head": branch, "base": base, "body": body},81 )82 resp.raise_for_status()83 return resp.json()["html_url"]84 85 86def open_pr_for_submission(submission: dict, fields: dict) -> str:87 """Splice the approved entry into the right data file and open a PR.88 89 `submission` is the row from submissions.py; `fields` are the90 reviewer-filled values matching formatters.TARGETS[type_]'s schema.91 """92 type_ = submission["type"]93 file_path = formatters.target_file(type_)94 entry_block = formatters.render_entry(type_, fields)95 96 base = _default_branch()97 base_sha = _branch_sha(base)98 99 slug = fields.get("id", submission["id"][:8])100 branch = f"add/{type_}-{slug}"101 _create_branch(branch, base_sha)102 103 content, sha = _get_file(file_path, branch)104 new_content = formatters.insert_entry(content, type_, entry_block)105 _update_file(106 file_path, branch, new_content, sha,107 message=f"Add {type_}: {fields.get('name') or fields.get('title') or slug}",108 )109 110 title = f"Add {type_}: {fields.get('name') or fields.get('title') or slug}"111 body = (112 f"Auto-generated from a huggingscience.co submission via the review Space.\n\n"113 f"- **Submission id:** `{submission['id']}`\n"114 f"- **Submitted at:** {submission['submitted_at']}\n"115 f"- **Source:** {submission['source']}\n\n"116 f"**Original description:**\n{submission['description']}\n"117 )118 return _open_pr(branch, base, title, body)119 