Buckets:
| name: github-pr-workflow | |
| description: "GitHub PR lifecycle: branch, commit, open, CI, merge." | |
| version: 1.1.0 | |
| author: Hermes Agent | |
| license: MIT | |
| platforms: [linux, macos, windows] | |
| 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 <RUN_ID> --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=<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 <fixed_files> | |
| git commit -m "fix: resolve CI failure in <check_name>" | |
| 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=<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` | | |
Xet Storage Details
- Size:
- 9.99 kB
- Xet hash:
- 51d4123a658455634588173cb2ca4d68a1100b644d2b3427f8793e4e30bb9a71
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.