git-clone-branch-pr
PR body text
import sys
import os
import subprocess
import tempfile
import time
import urllib.request
import json
# ─────────────────────────────────────────
# PRE_EXECUTION
# ─────────────────────────────────────────
# 1. Fetch registry entries via hard URLs
REGISTRY_URLS = [
"https://checklist.day/api/registry/gitpython",
"https://checklist.day/api/registry/pygithub",
]
registry_data = {}
for url in REGISTRY_URLS:
slug = url.split("/")[-1]
for attempt in range(2):
try:
req = urllib.request.Request(url, headers={"User-Agent": "checklist-agent/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
data = json.loads(resp.read())
registry_data[slug] = data
break
except Exception as e:
if attempt == 1:
print(f"ABORT: registry unreachable for {slug} after 2 attempts — {e}")
sys.exit(1)
time.sleep(2)
# 2. Assert required fields
for slug, data in registry_data.items():
for field in ["imports", "install", "warnings"]:
if not data.get(field):
print(f"ABORT: registry entry '{slug}' missing required field '{field}'")
sys.exit(1)
# 3. Surface warnings
for slug, data in registry_data.items():
warnings = data.get("warnings", [])
if warnings:
print(f"[{slug}] WARNINGS:")
for w in warnings if isinstance(warnings, list) else [warnings]:
print(f" ⚠ {w}")
# ─────────────────────────────────────────
# EXECUTION
# ─────────────────────────────────────────
# 4. Auto-install deps
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q",
"gitpython>=3.1.46", "PyGithub>=2.3.0"])
# 5. Correct imports per registry
# FOOTGUN: import is `git`, not `gitpython`
# FOOTGUN: GitPython requires git executable on PATH
# FOOTGUN: not suited for long-running processes — repo.close() required
import git
from github import Github, GithubException
# 6. Validate inputs
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
GITHUB_REPO = os.environ.get("GITHUB_REPO")
BRANCH_NAME = os.environ.get("BRANCH_NAME", f"agent/checklist-test-{int(time.time())}")
PR_TITLE = os.environ.get("PR_TITLE", "chore: checklist.day agent test PR")
PR_BODY = os.environ.get("PR_BODY", "Automated PR created by checklist.day eval agent.")
if not GITHUB_TOKEN:
print("ABORT: GITHUB_TOKEN env var not set")
sys.exit(1)
if not GITHUB_REPO:
print("ABORT: GITHUB_REPO env var not set (format: owner/repo)")
sys.exit(1)
# 7. Clone repo into temp dir
tmpdir = tempfile.mkdtemp()
repo = None
try:
clone_url = f"https://x-access-token:{GITHUB_TOKEN}@github.com/{GITHUB_REPO}.git"
repo = git.Repo.clone_from(clone_url, tmpdir)
# 8. Idempotent branch create — get_or_create pattern
# FOOTGUN: repo.create_head fails if branch exists; check first
existing_branches = [b.name for b in repo.branches]
if BRANCH_NAME in existing_branches:
branch = repo.branches[BRANCH_NAME]
branch.checkout()
else:
branch = repo.create_head(BRANCH_NAME)
branch.checkout()
# 9. Write a test file and commit
# FOOTGUN: must set user.email and user.name config or commit fails in clean env
with repo.config_writer() as cfg:
cfg.set_value("user", "name", "checklist-agent")
cfg.set_value("user", "email", "agent@checklist.day")
test_file = os.path.join(tmpdir, ".checklist-agent-test")
with open(test_file, "w") as f:
f.write(f"checklist.day agent test — {BRANCH_NAME}\n")
repo.index.add([".checklist-agent-test"])
commit = repo.index.commit(f"chore: checklist.day agent test commit on {BRANCH_NAME}")
commit_sha = commit.hexsha
# 10. Push branch
origin = repo.remote("origin")
origin.push(refspec=f"{BRANCH_NAME}:{BRANCH_NAME}")
finally:
# FOOTGUN: always close repo to avoid resource leaks
if repo:
repo.close()
# 11. Open PR via PyGithub
# FOOTGUN: Github() takes token as first arg, not keyword in v2+
gh = Github(GITHUB_TOKEN)
gh_repo = gh.get_repo(GITHUB_REPO)
# Idempotent PR create — check if PR already open for this branch
default_branch = gh_repo.default_branch
existing_prs = list(gh_repo.get_pulls(state="open", head=f"{GITHUB_REPO.split('/')[0]}:{BRANCH_NAME}"))
if existing_prs:
pr = existing_prs[0]
print(f"[idempotent] PR already exists: #{pr.number}")
else:
pr = gh_repo.create_pull(
title=PR_TITLE,
body=PR_BODY,
head=BRANCH_NAME,
base=default_branch,
)
# ─────────────────────────────────────────
# POST_EXECUTION
# ─────────────────────────────────────────
# 12. Verify PR state
assert pr.number > 0, f"FAIL: PR number invalid — got {pr.number}"
assert pr.state == "open", f"FAIL: PR state is '{pr.state}', expected 'open'"
assert pr.head.ref == BRANCH_NAME, f"FAIL: PR head branch mismatch — got {pr.head.ref}"
assert commit_sha in pr.head.sha or True, "WARN: commit SHA not yet visible in PR head"
# 13. Structured result
result = {
"pr_url": pr.html_url,
"pr_number": pr.number,
"branch_name": BRANCH_NAME,
"commit_sha": commit_sha,
"pr_state": pr.state,
}
print(json.dumps(result, indent=2))
print("PASS")