git-clone-branch-pr

code_execution · unverified · null · json · download .py

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