ワンクリックで
github-pr-workflow
GitHub PR lifecycle: branch, commit, open, CI, merge.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
GitHub PR lifecycle: branch, commit, open, CI, merge.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Migrate Hermes Agent to a new server while keeping both instances running in parallel. Covers backup, SSH troubleshooting, skill/memory sync, and GitHub remote setup.
Backup, restore, and migrate Hermes Agent data across machines. Covers the local backup scripts, cron scheduling, retention policies, git remote management, shallow-clone migration, and cross-machine parallel deployment.
Modify PDF appearance without changing content/structure: change font colors, remove highlights, adjust styling. Preserves all text, layout, fonts, and embedded resources.
Cloudflare Workers + D1 e-commerce site for Shengtuo Tractor. Deploy, DB ops, SEO, product sync.
Deploy and manage Cloudflare Workers with D1, R2, KV, and Workers KV store
Install and select animated petdex mascots for Hermes.
| name | github-pr-workflow |
| description | GitHub PR lifecycle: branch, commit, open, CI, merge. |
| 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"]}} |
Complete guide for managing the PR lifecycle. Each section shows the gh way first, then the git + curl fallback for machines without gh.
github-auth skill)# 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"
Many curl commands need owner/repo. Extract it from the git remote:
# 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"
This part is pure git — identical either way:
# 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 featuresfix/description — bug fixesrefactor/description — code restructuringdocs/description — documentationci/description — CI/CD changesUse the agent's file tools (write_file, patch) to make changes, then commit:
# 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
git push -u origin HEAD
With gh:
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:
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.
With gh:
# One-shot check
gh pr checks
# Watch until all checks finish (polls every 10s)
gh pr checks --watch
With git + curl:
# 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'}\")"
# 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
When CI fails, diagnose and fix. This loop works with either auth method.
With gh:
# 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:
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
After identifying the issue, use file tools (patch, write_file) to fix it:
git add <fixed_files>
git commit -m "fix: resolve CI failure in <check_name>"
git push
Re-check CI status using the commands from Section 4 above.
When asked to auto-fix CI, follow this loop:
read_file + patch/write_file → fix the codegit add . && git commit -m "fix: ..." && git pushgh pr merge Command — What Works and What Doesn't# ✓ This works: squash + delete branch (non-interactive on most gh versions)
gh pr merge N --squash --delete-branch
# ✗ This flag DOES NOT EXIST — don't use it:
gh pr merge N --no-editor # unknown flag — will error
# ✓ For fully non-interactive programmatic merge, use the REST API directly:
gh api --method PUT repos/$OWNER/$REPO/pulls/$PR_NUMBER/merge \
-f merge_method=squash -f delete_branch=true
# Returns: {"merged": true, "message": "Pull Request successfully merged"}
Key insight: When scripting batch merges (e.g., merging all dependabot CI bumps), prefer the REST API over
gh pr mergeCLI. The API call is deterministic, returns JSON, and has no flag compatibility issues across gh versions.
# 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
# Merge when PR is already approved from a reviewer who is also admin
gh pr merge --admin --merge
Note: When the PR already has approval and you want to merge immediately without prompts, use
--admin --merge. Faster than--squash --delete-branchwhich may prompt depending on gh version.
With git + curl:
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"
# 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 } }\"}"
# 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)
| 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 |
git push to Protected BranchesEven with admin privileges and gh pr merge --admin --merge access, git push origin main will always fail on a protected branch (e.g., main in clowlove/hermes-house). The rejection happens at the git protocol level before any auth check.
Correct pattern:
# WRONG — will be rejected
git checkout main
git merge feature/xxx
git push origin main # ✗ rejected
# RIGHT — go through PR
git checkout -b feature/xxx
git add . && git commit -m "..."
git push -u origin HEAD
gh pr create --title "feat: ..." --body "..."
gh pr review N -a # if you need to approve your own PR
gh pr merge N --admin --merge
GitHub Apps cannot be created via API. The createApp GraphQL mutation does not exist. You must use the web UI at https://github.com/settings/apps/new. After creation you get:
iss claimThe webhook URL can be a placeholder during creation and updated later in App settings.
When a workflow step fails on "Create Pull Request" and logs are unavailable or uninformative:
Common root cause: The workflow script made no actual content changes. git diff --staged --quiet succeeds (no diff), so git commit is skipped, and the branch has no new commits. When github-script tries to create a PR for an empty branch, it silently fails.
Diagnosis via API:
# Get run → job → step details
curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/actions/runs?per_page=5" | \
python3 -c "import sys,json; d=json.load(sys.stdin); [print(f\"{r['id']} | {r['conclusion'] or r['status']} | {r['name']}\") for r in d['workflow_runs'][:3]]"
# Get jobs for a specific run
curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/jobs" | \
python3 -c "import sys,json; [print(f\" {j['name']}: {j['conclusion']}\") for j in json.load(sys.stdin)['jobs']]"
# Get step-level details to find the exact failing step
curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/jobs" | \
python3 -c "import sys,json; jobs=json.load(sys.stdin)['jobs']; [print(f\" Step {s['number']}: {s['name']} - {s['conclusion']}\") for j in jobs for s in j['steps'] if s['conclusion'] != 'success']"
Prevention pattern — check for empty commits before pushing:
git add -A
if git diff --staged --quiet; then
echo "No changes to commit"
exit 1 # Don't proceed to PR step — branch would be empty
fi
git commit -m "Auto-update journal"
git push -u origin "$BRANCH"
⚠️ Critical: exit 1 vs exit 0 depends on workflow type:
workflow_dispatch, push): exit 1 is correct — if a human triggers the workflow and nothing changed, that's a failure worth surfacing.schedule cron): use exit 0 — a "no changes to commit" result is normal and expected behavior (nothing new happened today). Using exit 1 here will cause the CI step to fail even when there's no actual problem.# Example: automated weekly job (use exit 0 for no-change)
- name: Create Branch and Commit
run: |
BRANCH="auto/journal-update-$(date +%Y%m%d-%H%M%S)"
git checkout -b "$BRANCH"
git add -A
if git diff --staged --quiet; then
echo "No changes to commit"
exit 0 # ✓ Correct for scheduled workflows
fi
git commit -m "Auto-update journal"
git push -u origin "$BRANCH"
# Example: interactive fix job (exit 1 is fine here)
if git diff --staged --quiet; then
echo "No changes to commit"
exit 1 # ✓ Correct for on-demand workflows
fi
Failure symptom of wrong exit code in scheduled workflows: the "Create Branch and Commit" step fails even though the prior "Generate Weekly Summary" step succeeded. Check step timing — if "Generate Weekly Summary" completed in <1s, it likely produced no file changes. The fix is exit 0, not exit 1.
Fix for workflows that only update timestamps: If a workflow only modifies timestamps (e.g., *最后更新:2026-05-12 16:40 UTC*), the file content may be identical after regex substitution if the regex doesn't match. The actual file must change — fetch real data (from GitHub API, external sources) and add new content before committing.
gh CLI Over github-script for PR Operationsactions/github-script is unreliable for PR creation and merge — it can fail without clear error messages. Prefer gh pr create + gh pr merge in a run step:
When the git remote URL embeds a token (e.g., https://github.com/sponsors@github.com/owner/repo.git), git push may fail with:
fatal: could not read Password for 'https://github.com/sponsors@github.com': No such device or address
Workarounds (pick one):
# Option 1: Remove token from remote URL — let gh handle auth
git remote set-url origin https://github.com/owner/repo.git
git push -u origin HEAD
# Option 2: Skip git push entirely — use gh pr create directly (preferred)
git checkout -b evolution/2026-05-18
git add . && git commit -m "docs: update evolution log"
gh pr create --repo owner/repo --base main --head evolution/2026-05-18 \
--title "docs: update evolution log" --body "..."
Rule: Prefer gh pr create over git push + separate PR creation when the remote requires token auth. gh manages its own credential flow reliably; embedding tokens in URLs is fragile.
Also ensure explicit job permissions:
```yaml
jobs:
update-journal:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
git push to Protected Branches