"""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)