layered-soul/skills/forgejo-operations/references/api-merge-pattern.md

57 lines
2 KiB
Markdown
Raw Permalink Normal View History

# Forgejo API — Create and Merge PRs via curl
Pattern for automated PR creation and merge when branch protection blocks
direct push.
## Prerequisites
- API token with `repository:write` scope
- Token stored as `FORGEJO_API_TOKEN` in `~/.hermes/.env` (mode 0600)
## Create and merge a PR
```bash
source ~/.hermes/.env 2>/dev/null
API="https://code.smilepowered.org/api/v1/repos/<owner>/<repo>"
# Create PR
PR=$(curl -s -X POST \
-H "Authorization: token $FORGEJO_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"...","head":"<branch>","base":"main","body":"..."}' \
"$API/pulls")
NUM=$(echo "$PR" | python3 -c "import sys,json; print(json.load(sys.stdin).get('number',''))")
# Merge
curl -s -X POST \
-H "Authorization: token $FORGEJO_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"Do":"merge","delete_branch_after_merge":true}' \
"$API/pulls/$NUM/merge" > /dev/null
# Verify
curl -s -H "Authorization: token $FORGEJO_API_TOKEN" "$API/pulls/$NUM" | \
python3 -c "import sys,json; d=json.load(sys.stdin); print(f'PR #{d[\"number\"]}: merged={d[\"merged\"]}')"
```
## Token scopes needed
| Scope | For |
| -------------------- | -------------------------------- |
| `repository:write` | Create PRs, merge PRs |
| `organization:write` | Adjust branch protection rules |
| `read:user` | Verify token identity (optional) |
## Pitfalls
- **Merge field capitalization:** Forgejo merge endpoint requires `"Do":"merge"`
(capital D). Lowercase `"do"` may return HTTP 422 `[Do]: Required`. Use
`"Do":"merge"` and verify with a follow-up GET on the PR — HTTP 200 + empty
body = merged.
- The merge endpoint returns empty body on success (not JSON). Check PR state
after merging by fetching `GET /pulls/{id}`.
- Forgejo API base is `https://<host>/api/v1`, not `/api`.
- Direct push to protected branches still blocked even with token — use PR
creation + merge workflow.
- Token must be the full hash, not truncated.