github-pr-workflow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGitHub Pull Request Workflow
GitHub Pull Request 工作流
Complete guide for managing the PR lifecycle. Each section shows the way first, then the + fallback for machines without .
ghgitcurlgh管理PR生命周期的完整指南。每个章节先展示使用的方式,再展示针对未安装的机器,使用 + 的替代方案。
ghghgitcurlPrerequisites
前置条件
- Authenticated with GitHub (see skill)
github-auth - Inside a git repository with a GitHub remote
- 已通过GitHub认证(参考技能)
github-auth - 处于包含GitHub远程仓库的git仓库目录中
Quick Auth Detection
快速认证检测
bash
undefinedbash
undefinedDetermine which method to use throughout this workflow
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
GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]:([^@])@.*|\1|')
fi
fi
echo "Using: $AUTH"
undefinedif 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
GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]:([^@])@.*|\1|')
fi
fi
echo "Using: $AUTH"
undefinedExtracting Owner/Repo from the Git Remote
从Git远程仓库提取所有者/仓库信息
Many commands need . Extract it from the git remote:
curlowner/repobash
undefined许多命令需要信息。从git远程仓库中提取:
curlowner/repobash
undefinedWorks for both HTTPS and SSH remote URLs
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"
---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
1. 分支创建
This part is pure — identical either way:
gitbash
undefined这部分完全使用——两种方式操作一致:
gitbash
undefinedMake sure you're up to date
Make sure you're up to date
git fetch origin
git checkout main && git pull origin main
git fetch origin
git checkout main && git pull origin main
Create and switch to a new branch
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 changesgit checkout -b feat/add-user-authentication
分支命名规范:
- `feat/description` — 新功能
- `fix/description` — Bug修复
- `refactor/description` — 代码重构
- `docs/description` — 文档更新
- `ci/description` — CI/CD变更2. Making Commits
2. 提交变更
Use the agent's file tools (, ) to make changes, then commit:
write_filepatchbash
undefined使用Agent的文件工具(、)修改代码,然后提交:
write_filepatchbash
undefinedStage specific files
Stage specific files
git add src/auth.py src/models/user.py tests/test_auth.py
git add src/auth.py src/models/user.py tests/test_auth.py
Commit with a conventional commit message
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`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"
提交消息格式(Conventional Commits):type(scope): short description
Longer explanation if needed. Wrap at 72 characters.
类型:`feat`, `fix`, `refactor`, `docs`, `test`, `ci`, `chore`, `perf`3. Pushing and Creating a PR
3. 推送分支并创建PR
Push the Branch (same either way)
推送分支(两种方式一致)
bash
git push -u origin HEADbash
git push -u origin HEADCreate the PR
创建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使用gh:
bash
gh pr create \
--title "feat: add JWT-based user authentication" \
--body "## Summary
- Adds login and register API endpoints
- JWT token generation and validationTest Plan
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 — save it for later commands.
numberTo create as a draft, add to the JSON body.
"draft": true- Unit tests pass
Closes #42"
可选参数:`--draft`, `--reviewer user1,user2`, `--label "enhancement"`, `--base develop`
**使用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\"
}"返回的JSON包含PR的——保存该值用于后续命令。
number若要创建草稿PR,在JSON体中添加。
"draft": true4. Monitoring CI Status
4. 监控CI状态
Check CI Status
检查CI状态
With gh:
bash
undefined使用gh:
bash
undefinedOne-shot check
One-shot check
gh pr checks
gh pr checks
Watch until all checks finish (polls every 10s)
Watch until all checks finish (polls every 10s)
gh pr checks --watch
**With git + curl:**
```bashgh pr checks --watch
**使用git + curl:**
```bashGet the latest commit SHA on the current branch
Get the latest commit SHA on the current branch
SHA=$(git rev-parse HEAD)
SHA=$(git rev-parse HEAD)
Query the combined status
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', '')}")"
-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', '')}")"
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', '')}")"
-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)
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'}")"
-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'}")"
undefinedcurl -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'}")"
-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'}")"
undefinedPoll Until Complete (git + curl)
轮询直至完成(git + curl)
bash
undefinedbash
undefinedSimple polling loop — check every 30 seconds, up to 10 minutes
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
-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
undefinedSHA=$(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
-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
undefined5. Auto-Fixing CI Failures
5. 自动修复CI失败
When CI fails, diagnose and fix. This loop works with either auth method.
当CI失败时,诊断并修复问题。以下流程适用于两种认证方式。
Step 1: Get Failure Details
步骤1:获取失败详情
With gh:
bash
undefined使用gh:
bash
undefinedList recent workflow runs on this branch
List recent workflow runs on this branch
gh run list --branch $(git branch --show-current) --limit 5
gh run list --branch $(git branch --show-current) --limit 5
View failed logs
View failed logs
gh run view <RUN_ID> --log-failed
**With git + curl:**
```bash
BRANCH=$(git branch --show-current)gh run view <RUN_ID> --log-failed
**使用git + curl:**
```bash
BRANCH=$(git branch --show-current)List workflow runs on this branch
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']}")"
-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']}")"
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']}")"
-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)
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
-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
undefinedRUN_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
-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
undefinedStep 2: Fix and Push
步骤2:修复并推送
After identifying the issue, use file tools (, ) to fix it:
patchwrite_filebash
git add <fixed_files>
git commit -m "fix: resolve CI failure in <check_name>"
git push定位问题后,使用文件工具(、)修复代码:
patchwrite_filebash
git add <fixed_files>
git commit -m "fix: resolve CI failure in <check_name>"
git pushStep 3: Verify
步骤3:验证
Re-check CI status using the commands from Section 4 above.
使用第4节中的命令重新检查CI状态。
Auto-Fix Loop Pattern
自动修复循环模式
When asked to auto-fix CI, follow this loop:
- Check CI status → identify failures
- Read failure logs → understand the error
- Use +
read_file/patch→ fix the codewrite_file git add . && git commit -m "fix: ..." && git push- Wait for CI → re-check status
- Repeat if still failing (up to 3 attempts, then ask the user)
当需要自动修复CI时,遵循以下循环:
- 检查CI状态 → 定位失败项
- 查看失败日志 → 理解错误原因
- 使用+
read_file/patch→ 修复代码write_file git add . && git commit -m "fix: ..." && git push- 等待CI执行 → 重新检查状态
- 若仍失败则重复(最多3次,之后询问用户)
6. Merging
6. 合并PR
With gh:
bash
undefined使用gh:
bash
undefinedSquash merge + delete branch (cleanest for feature branches)
Squash merge + delete branch (cleanest for feature branches)
gh pr merge --squash --delete-branch
gh pr merge --squash --delete-branch
Enable auto-merge (merges when all checks pass)
Enable auto-merge (merges when all checks pass)
gh pr merge --auto --squash --delete-branch
**With git + curl:**
```bash
PR_NUMBER=<number>gh pr merge --auto --squash --delete-branch
**使用git + curl:**
```bash
PR_NUMBER=<number>Merge the PR via API (squash)
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)" }"
-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)" }"
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)" }"
-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
Delete the remote branch after merge
BRANCH=$(git branch --show-current)
git push origin --delete $BRANCH
BRANCH=$(git branch --show-current)
git push origin --delete $BRANCH
Switch back to main locally
Switch back to main locally
git checkout main && git pull origin main
git branch -d $BRANCH
Merge methods: `"merge"` (merge commit), `"squash"`, `"rebase"`git checkout main && git pull origin main
git branch -d $BRANCH
合并方式:`"merge"`(合并提交)、`"squash"`(压缩合并)、`"rebase"`(变基合并)Enable Auto-Merge (curl)
启用自动合并(curl)
bash
undefinedbash
undefinedAuto-merge requires the repo to have it enabled in settings.
Auto-merge requires the repo to have it enabled in settings.
This uses the GraphQL API since REST doesn't support auto-merge.
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'])")
-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 } }"}"
-H "Authorization: token $GITHUB_TOKEN"
https://api.github.com/graphql
-d "{"query": "mutation { enablePullRequestAutoMerge(input: {pullRequestId: \"$PR_NODE_ID\", mergeMethod: SQUASH}) { clientMutationId } }"}"
undefinedPR_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'])")
-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 } }"}"
-H "Authorization: token $GITHUB_TOKEN"
https://api.github.com/graphql
-d "{"query": "mutation { enablePullRequestAutoMerge(input: {pullRequestId: \"$PR_NODE_ID\", mergeMethod: SQUASH}) { clientMutationId } }"}"
undefined7. Complete Workflow Example
7. 完整工作流示例
bash
undefinedbash
undefined1. Start from clean main
1. Start from clean main
git checkout main && git pull origin main
git checkout main && git pull origin main
2. Branch
2. Branch
git checkout -b fix/login-redirect-bug
git checkout -b fix/login-redirect-bug
3. (Agent makes code changes with file tools)
3. (Agent makes code changes with file tools)
4. Commit
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."
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
5. Push
git push -u origin HEAD
git push -u origin HEAD
6. Create PR (picks gh or curl based on what's available)
6. Create PR (picks gh or curl based on what's available)
... (see Section 3)
... (see Section 3)
7. Monitor CI (see Section 4)
7. Monitor CI (see Section 4)
8. Merge when green (see Section 6)
8. Merge when green (see Section 6)
undefinedundefinedUseful PR Commands Reference
实用PR命令参考
| Action | gh | git + curl |
|---|---|---|
| List my PRs | | |
| View PR diff | | |
| Add comment | | |
| Request review | | |
| Close PR | | |
| Check out someone's PR | | |
| 操作 | gh | git + curl |
|---|---|---|
| 列出我的PR | | |
| 查看PR差异 | | |
| 添加评论 | | |
| 请求评审 | | |
| 关闭PR | | |
| 检出他人的PR | | |