跳到正文
cccskills
免费在 GitHub 公开

github-pr-workflow

Take a change from branch to merged pull request, CI included.

中文简介正在准备中。当前显示原文说明。

集成工具GitHub
查看安装方法

包含的文件(5)

  • SKILL.md10.2 KB
  • references/ci-troubleshooting.md4.8 KB
  • references/conventional-commits.md2.3 KB
  • templates/pr-body-bugfix.md511 B
  • templates/pr-body-feature.md580 B

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

GitHub pull request workflow

Branch, commit, open the pull request, watch CI, fix what it says, merge, clean up. Every step is given twice: with gh, and with git plus curl for the machines that do not have it, which is most servers.

Run the shell blocks through shell_exec. Read github-auth first if anything returns 401 or 404.

Two things that need consent

Pushing and merging are outward actions. A push puts the user's name on a public commit; a merge changes the default branch other people build on. Do them because the user asked for them, not because the workflow reached that step. When in doubt, stop and show what you are about to push.

Never force-push a branch that has left the machine, and never rewrite history on a shared branch, on your own initiative.

Setup, once per session

if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
  AUTH=gh
else
  AUTH=curl   # ${GITHUB_TOKEN} comes from the LaRuche vault
fi

OWNER_REPO=$(git remote get-url origin | sed -E 's|.*github\.com[:/]||; s|\.git$||')
OWNER=${OWNER_REPO%%/*}
REPO=${OWNER_REPO##*/}
echo "$AUTH on $OWNER/$REPO"

If OWNER_REPO comes out empty or still looks like a URL, the remote is not GitHub, or there is no origin. Stop there: everything below will fail in a way that blames authentication.

1. Branch

git fetch origin
git switch main && git pull --ff-only origin main
git switch -c feat/jwt-authentication

--ff-only refuses to create a merge commit behind your back. If it fails, the local main has commits that are not on the remote, and that is worth understanding before branching off it.

Prefix by intent: feat/, fix/, refactor/, docs/, ci/, chore/.

2. Commit

Edit with file_write and file_edit, then stage deliberately:

git status
git add src/auth.py src/models/user.py tests/test_auth.py
git commit -m "feat: add JWT-based authentication

Sessions were stored server-side, which blocked horizontal scaling.
Tokens move that state to the client.

Closes #42"

Name the files. git add . sweeps in build output, editor droppings and, once in a while, a .env. Read git status before staging, every time.

The subject line follows Conventional Commits, type(scope): summary, under about 72 characters, imperative. The body says WHY: the diff already says what.

3. Open the pull request

git push -u origin HEAD

With gh:

gh pr create --title "feat: add JWT-based authentication" --body "$(cat <<'BODY'
## What this changes
Session state moves from the server to a signed token.

## Why
Server-side sessions pinned every user to one process, which blocked scaling out.

## How to verify
- `pytest tests/test_auth.py -q`
- log in, restart the server, confirm the session survives

Closes #42
BODY
)"

--draft opens it unreviewable, --reviewer a,b, --label enhancement, --base develop when the target is not the default branch.

With curl:

BRANCH=$(git branch --show-current)
curl -s -X POST \
  -H "Authorization: Bearer ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/$OWNER/$REPO/pulls" \
  -d "$(python -c "
import json, sys
print(json.dumps({
  'title': 'feat: add JWT-based authentication',
  'body': open('/tmp/pr-body.md').read(),
  'head': sys.argv[1],
  'base': 'main',
}))" "$BRANCH")"

Build the JSON with a tool, not with string interpolation. A body containing a quote, a backtick or a newline produces malformed JSON, and the API answers with a validation error that says nothing about quoting.

Keep the number from the response: it is PR_NUMBER everywhere below.

4. Watch CI

gh pr checks           # once
gh pr checks --watch   # until everything settles

With curl, and this is where the trap is:

SHA=$(git rev-parse HEAD)
curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" \
  "https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/check-runs" \
  | python -c "
import sys, json
runs = json.load(sys.stdin).get('check_runs', [])
if not runs:
    print('no check runs')
for run in runs:
    print(f\"{run['name']}: {run['status']} / {run['conclusion'] or 'pending'}\")"

Use check-runs, not status. They are two different APIs. The legacy /commits/$SHA/status endpoint only reports commit statuses posted by external CI. A repository whose CI is GitHub Actions returns "state": "pending" there with an empty statuses array, forever. Polling it waits for something that will never arrive, and the timeout gets read as "CI is slow" rather than "wrong endpoint".

Poll on the conclusion, with a ceiling:

SHA=$(git rev-parse HEAD)
for i in $(seq 1 20); do
  PENDING=$(curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" \
    "https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/check-runs" \
    | python -c "
import sys, json
runs = json.load(sys.stdin).get('check_runs', [])
print(sum(1 for r in runs if r['status'] != 'completed'))")
  echo "poll $i: $PENDING still running"
  [ "$PENDING" = "0" ] && break
  sleep 30
done

Ten minutes maximum. If it is still running after that, say so rather than looping: some suites take an hour, and a silent agent looks identical to a stuck one.

5. Fix what CI says

gh run list --branch "$(git branch --show-current)" --limit 5
gh run view <RUN_ID> --log-failed

With curl, the logs come back as a redirect to a zip, so -L is not optional:

curl -sL -H "Authorization: Bearer ${GITHUB_TOKEN}" \
  "https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/logs" \
  -o /tmp/ci-logs.zip
unzip -o /tmp/ci-logs.zip -d /tmp/ci-logs && grep -ri "error\|failed" /tmp/ci-logs | head -40

Then: read the real error, reproduce it LOCALLY, fix it, run the local check, push once.

Reproduce locally before pushing a fix. Pushing a guess and waiting for CI is a fifteen-minute compile of a hypothesis you could have tested in ten seconds. If the failure genuinely only happens in CI, that difference is the bug: environment, version, ordering.

Stop after three attempts. Three failed fixes means the diagnosis is wrong, not that the fourth will land. Say what you tried, paste the failing output, and hand it back.

6. Merge

gh pr merge --squash --delete-branch          # now
gh pr merge --auto --squash --delete-branch   # when checks pass

With curl:

curl -s -X PUT -H "Authorization: Bearer ${GITHUB_TOKEN}" \
  "https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/merge" \
  -d '{"merge_method": "squash"}'

merge_method is merge, squash or rebase. Match the repository's habit: read git log --oneline -20 on the default branch. A merge commit in a history of clean squashes is noise somebody has to explain.

Auto-merge has no REST endpoint; it is GraphQL only:

NODE_ID=$(curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" \
  "https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER" \
  | python -c "import sys, json; print(json.load(sys.stdin)['node_id'])")

python - "$NODE_ID" <<'PY' > /tmp/automerge.json
import json, sys
print(json.dumps({
    "query": "mutation($id: ID!) {"
             " enablePullRequestAutoMerge(input: {pullRequestId: $id, mergeMethod: SQUASH})"
             " { clientMutationId } }",
    "variables": {"id": sys.argv[1]},
}))
PY

curl -s -X POST -H "Authorization: Bearer ${GITHUB_TOKEN}" \n  https://api.github.com/graphql --data @/tmp/automerge.json

Then clean up:

git switch main && git pull --ff-only origin main
git branch -d "$BRANCH"

-d refuses to delete a branch that is not merged. Never reach for -D to make that message go away: it is telling you the work is not where you think it is.

Quick reference

ActionghREST
My open PRsgh pr list --author @meGET /repos/O/R/pulls?state=open
Diffgh pr diffgit diff main...HEAD
Commentgh pr comment N --body "..."POST /repos/O/R/issues/N/comments
Request reviewgh pr edit N --add-reviewer uPOST /repos/O/R/pulls/N/requested_reviewers
Closegh pr close NPATCH /repos/O/R/pulls/N {"state":"closed"}
Check out a PRgh pr checkout Ngit fetch origin pull/N/head:pr-N

Note main...HEAD, three dots: that is the diff against the merge base, which is what the pull request shows. Two dots compares the tips and includes everything that landed on main since you branched.

Traps

  • gh pr create before pushing fails with a message about the branch not existing on the remote. Push first.
  • A PR body built by string interpolation breaks on the first quote or backtick. Serialise it with a tool.
  • Branch protection rejects the merge, not the token. Read the message: required reviews and required checks are the usual causes, and neither is fixed by a new token.
  • Squashing loses the co-authors in a multi-author branch unless the trailers are carried into the squash message.
  • A push to a protected default branch is rejected outright. That is the protection working. Open a PR.
  • --delete-branch deletes it locally too, so run it from a different branch or expect to be moved.

Failure modes

gh works and git push asks for a password. gh auth setup-git was never run. See github-auth.

422 Validation Failed on PR creation. Almost always one of: a PR already exists for this head branch, head and base are identical, or the body was malformed JSON. Read the errors array in the response; it names the field.

CI never finishes. You are polling /status on an Actions repository. Use /check-runs. See section 4.

A check is red but the logs look clean. The failing step is a different job. List the runs and open the one whose conclusion is failure, not the most recent one.

Merge returns 405 Method Not Allowed. The PR is not mergeable: conflicts, a required check pending, or a required review missing. gh pr view --json mergeable,mergeStateStatus says which.

The branch will not delete after merging. It was merged by squash, so git cannot see its commits in main. Confirm the PR is merged on GitHub, then delete it with -D, which is the one case where that is correct.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Drive an installed LaRuche App through its declared actions, never its files.

中文简介正在准备中。当前显示原文说明。

infinition/LaRuche332026年9月22日 更新

arxiv

免费

Find academic papers on arXiv, with citation counts and BibTeX.

中文简介正在准备中。当前显示原文说明。

infinition/LaRuche332026年9月22日 更新

ascii-art

免费

Render text or an image as ASCII art for terminal-friendly output.

中文简介正在准备中。当前显示原文说明。

infinition/LaRuche332026年9月22日 更新

Track RSS/Atom feeds and blogs via blogwatcher-cli.

中文简介正在准备中。当前显示原文说明。

infinition/LaRuche332026年9月22日 更新

Drive a real web browser: navigate, read, find, click, fill, screenshot

中文简介正在准备中。当前显示原文说明。

infinition/LaRuche332026年9月22日 更新

Measure a codebase: lines of code, language mix, and symbol lookups.

中文简介正在准备中。当前显示原文说明。

infinition/LaRuche332026年9月22日 更新

查看 infinition 的全部技能

报告此技能的问题