--- name: github-pr-workflow description: Full pull request lifecycle — create branches, commit changes, open PRs, monitor CI status, auto-fix failures, and merge. Works with gh CLI or falls back to git + GitHub REST API via curl. version: 1.1.0 author: Hermes Agent license: MIT metadata: hermes: tags: [GitHub, Pull-Requests, CI/CD, Git, Automation, Merge] related_skills: [github-auth, github-code-review] --- # GitHub Pull Request Workflow Complete guide for managing the PR lifecycle. Each section shows the `gh` way first, then the `git` + `curl` fallback for machines without `gh`. ## Prerequisites - Authenticated with GitHub (see `github-auth` skill) - Inside a git repository with a GitHub remote ### Quick Auth Detection ```bash # Determine which method to use throughout this workflow if command -v gh &>/dev/null && gh auth status &>/dev/null; then AUTH="gh" else AUTH="git" # Ensure we have a token for API calls if [ -z "$GITHUB_TOKEN" ]; then if [ -f ~/.hermes/.env ] && grep -q "^GITHUB_TOKEN=" ~/.hermes/.env; then GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\n\r') elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|') fi fi fi echo "Using: $AUTH" ``` ### Extracting Owner/Repo from the Git Remote Many `curl` commands need `owner/repo`. Extract it from the git remote: ```bash # Works for both HTTPS and SSH remote URLs REMOTE_URL=$(git remote get-url origin) OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||') OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1) REPO=$(echo "$OWNER_REPO" | cut -d/ -f2) echo "Owner: $OWNER, Repo: $REPO" ``` --- ## 1. Branch Creation This part is pure `git` — identical either way: ```bash # Make sure you're up to date git fetch origin git checkout main && git pull origin main # Create and switch to a new branch git checkout -b feat/add-user-authentication ``` Branch naming conventions: - `feat/description` — new features - `fix/description` — bug fixes - `refactor/description` — code restructuring - `docs/description` — documentation - `ci/description` — CI/CD changes ## 2. Making Commits Use the agent's file tools (`write_file`, `patch`) to make changes, then commit: ```bash # Stage specific files git add src/auth.py src/models/user.py tests/test_auth.py # Commit with a conventional commit message git commit -m "feat: add JWT-based user authentication - Add login/register endpoints - Add User model with password hashing - Add auth middleware for protected routes - Add unit tests for auth flow" ``` Commit message format (Conventional Commits): ``` type(scope): short description Longer explanation if needed. Wrap at 72 characters. ``` Types: `feat`, `fix`, `refactor`, `docs`, `test`, `ci`, `chore`, `perf` ## 3. Pushing and Creating a PR ### Push the Branch (same either way) ```bash git push -u origin HEAD ``` ### Create the PR **With gh:** ```bash gh pr create \ --title "feat: add JWT-based user authentication" \ --body "## Summary - Adds login and register API endpoints - JWT token generation and validation ## Test Plan - [ ] Unit tests pass Closes #42" ``` Options: `--draft`, `--reviewer user1,user2`, `--label "enhancement"`, `--base develop` **With git + curl:** ```bash BRANCH=$(git branch --show-current) curl -s -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/$OWNER/$REPO/pulls \ -d "{ \"title\": \"feat: add JWT-based user authentication\", \"body\": \"## Summary\nAdds login and register API endpoints.\n\nCloses #42\", \"head\": \"$BRANCH\", \"base\": \"main\" }" ``` The response JSON includes the PR `number` — save it for later commands. To create as a draft, add `"draft": true` to the JSON body. ## 4. Monitoring CI Status ### Check CI Status **With gh:** ```bash # One-shot check gh pr checks # Watch until all checks finish (polls every 10s) gh pr checks --watch ``` **With git + curl:** ```bash # Get the latest commit SHA on the current branch SHA=$(git rev-parse HEAD) # Query the combined status curl -s \ -H "Authorization: token $GITHUB_TOKEN" \ https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status \ | python3 -c " import sys, json data = json.load(sys.stdin) print(f\"Overall: {data['state']}\") for s in data.get('statuses', []): print(f\" {s['context']}: {s['state']} - {s.get('description', '')}\")" # Also check GitHub Actions check runs (separate endpoint) curl -s \ -H "Authorization: token $GITHUB_TOKEN" \ https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/check-runs \ | python3 -c " import sys, json data = json.load(sys.stdin) for cr in data.get('check_runs', []): print(f\" {cr['name']}: {cr['status']} / {cr['conclusion'] or 'pending'}\")" ``` ### Poll Until Complete (git + curl) ```bash # Simple polling loop — check every 30 seconds, up to 10 minutes SHA=$(git rev-parse HEAD) for i in $(seq 1 20); do STATUS=$(curl -s \ -H "Authorization: token $GITHUB_TOKEN" \ https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status \ | python3 -c "import sys,json; print(json.load(sys.stdin)['state'])") echo "Check $i: $STATUS" if [ "$STATUS" = "success" ] || [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ]; then break fi sleep 30 done ``` ## 5. Auto-Fixing CI Failures When CI fails, diagnose and fix. This loop works with either auth method. ### Step 1: Get Failure Details **With gh:** ```bash # List recent workflow runs on this branch gh run list --branch $(git branch --show-current) --limit 5 # View failed logs gh run view --log-failed ``` **With git + curl:** ```bash BRANCH=$(git branch --show-current) # List workflow runs on this branch curl -s \ -H "Authorization: token $GITHUB_TOKEN" \ "https://api.github.com/repos/$OWNER/$REPO/actions/runs?branch=$BRANCH&per_page=5" \ | python3 -c " import sys, json runs = json.load(sys.stdin)['workflow_runs'] for r in runs: print(f\"Run {r['id']}: {r['name']} - {r['conclusion'] or r['status']}\")" # Get failed job logs (download as zip, extract, read) RUN_ID= curl -s -L \ -H "Authorization: token $GITHUB_TOKEN" \ https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/logs \ -o /tmp/ci-logs.zip cd /tmp && unzip -o ci-logs.zip -d ci-logs && cat ci-logs/*.txt ``` ### Step 2: Fix and Push After identifying the issue, use file tools (`patch`, `write_file`) to fix it: ```bash git add git commit -m "fix: resolve CI failure in " git push ``` ### Step 3: Verify Re-check CI status using the commands from Section 4 above. ### Auto-Fix Loop Pattern When asked to auto-fix CI, follow this loop: 1. Check CI status → identify failures 2. Read failure logs → understand the error 3. Use `read_file` + `patch`/`write_file` → fix the code 4. `git add . && git commit -m "fix: ..." && git push` 5. Wait for CI → re-check status 6. Repeat if still failing (up to 3 attempts, then ask the user) ## 6. Merging **With gh:** ```bash # Squash merge + delete branch (cleanest for feature branches) gh pr merge --squash --delete-branch # Enable auto-merge (merges when all checks pass) gh pr merge --auto --squash --delete-branch ``` **With git + curl:** ```bash PR_NUMBER= # Merge the PR via API (squash) curl -s -X PUT \ -H "Authorization: token $GITHUB_TOKEN" \ https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/merge \ -d "{ \"merge_method\": \"squash\", \"commit_title\": \"feat: add user authentication (#$PR_NUMBER)\" }" # Delete the remote branch after merge BRANCH=$(git branch --show-current) git push origin --delete $BRANCH # Switch back to main locally git checkout main && git pull origin main git branch -d $BRANCH ``` Merge methods: `"merge"` (merge commit), `"squash"`, `"rebase"` ### Enable Auto-Merge (curl) ```bash # Auto-merge requires the repo to have it enabled in settings. # This uses the GraphQL API since REST doesn't support auto-merge. PR_NODE_ID=$(curl -s \ -H "Authorization: token $GITHUB_TOKEN" \ https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \ | python3 -c "import sys,json; print(json.load(sys.stdin)['node_id'])") curl -s -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ https://api.github.com/graphql \ -d "{\"query\": \"mutation { enablePullRequestAutoMerge(input: {pullRequestId: \\\"$PR_NODE_ID\\\", mergeMethod: SQUASH}) { clientMutationId } }\"}" ``` ## 7. Complete Workflow Example ```bash # 1. Start from clean main git checkout main && git pull origin main # 2. Branch git checkout -b fix/login-redirect-bug # 3. (Agent makes code changes with file tools) # 4. Commit git add src/auth/login.py tests/test_login.py git commit -m "fix: correct redirect URL after login Preserves the ?next= parameter instead of always redirecting to /dashboard." # 5. Push git push -u origin HEAD # 6. Create PR (picks gh or curl based on what's available) # ... (see Section 3) # 7. Monitor CI (see Section 4) # 8. Merge when green (see Section 6) ``` ## Useful PR Commands Reference | Action | gh | git + curl | |--------|-----|-----------| | List my PRs | `gh pr list --author @me` | `curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$OWNER/$REPO/pulls?state=open"` | | View PR diff | `gh pr diff` | `git diff main...HEAD` (local) or `curl -H "Accept: application/vnd.github.diff" ...` | | Add comment | `gh pr comment N --body "..."` | `curl -X POST .../issues/N/comments -d '{"body":"..."}'` | | Request review | `gh pr edit N --add-reviewer user` | `curl -X POST .../pulls/N/requested_reviewers -d '{"reviewers":["user"]}'` | | Close PR | `gh pr close N` | `curl -X PATCH .../pulls/N -d '{"state":"closed"}'` | | Check out someone's PR | `gh pr checkout N` | `git fetch origin pull/N/head:pr-N && git checkout pr-N` |