48 lines
4.7 KiB
Markdown
48 lines
4.7 KiB
Markdown
---
|
||
name: gitea-pr-api-cycle
|
||
description: "Open/update/merge PRs and issues on the Dankclaw Gitea API (192.168.0.143:3000) with the token-safe sequence, the form-encoded merge field, the empty-token-returns-404 trap, and post-merge live-checkout fast-forward."
|
||
---
|
||
|
||
# Gitea PR/issue cycle — Dankclaw API
|
||
|
||
Friction-earned 2026-10-18 (PRs #15–#20, issue #18) and re-earned 2026-10-19 on `dank/dankvoice` PRs #4–#6: the merge payload claim below was wrong, and an empty token masqueraded as a missing repo.
|
||
|
||
## Where
|
||
- API base: `http://192.168.0.143:3000/api/v1/repos/dank/<repo>/{pulls,issues}` — repo varies (`command-center`, `dankvoice`, `serverAppConfigs`); numbering is per-repo, so a footer citing `#36` means nothing without the repo.
|
||
- Token: never inline, never in argv (`-H "Authorization: token …"` lands in `/proc/<pid>/cmdline`, world-readable). Extract into a 600-perm curl config and hand it to curl with `-K`:
|
||
```bash
|
||
tok=$(sed -nE 's#^https?://[^:/@]+:([^@]+)@192\.168\.0\.143.*#\1#p' ~/.config/secrets/gitea | head -1)
|
||
[ "${#tok}" -ge 40 ] || { echo "token extraction failed"; exit 1; }
|
||
cfg=$(mktemp); chmod 600 "$cfg"; printf 'header = "Authorization: token %s"\n' "$tok" > "$cfg"
|
||
curl -sS -K "$cfg" … ; rm -f "$cfg"
|
||
```
|
||
- **Empty token answers `404 {"message":"not found"}`, NOT 401** — Gitea hides the private repo. A 404 on a path you have used before means auth, not a typo'd repo. The trap that produces the empty token: `awk -F'/' … '{print $NF}'` on the store line (`scheme://user:token@host/`) — the host is in `$3`, the match never fires, you get a silent empty variable. Always assert the length before POSTing.
|
||
- Multi-line bodies: build JSON in python (`json.dumps({"title":…, "body":open(f).read(),…})`) or `jq -n --arg b "$(cat b.txt)"`. NEVER pass `---` as a bare jq argument (parsed as a flag). Escaped quotes inside `python3 -c '…'` break on `\"` — write the script to a file or use a heredoc.
|
||
- Always capture status: `-o r.json -w '%{http_code}'`; a bare `-o` hides failures.
|
||
|
||
## Create
|
||
`POST /pulls` with `{title, body, base:"main", head:<branch>}` — JSON is accepted here (unlike merge).
|
||
- **409 `pull request already exists for these targets [issue_id: N, …]`** — a PR for that head/base exists already. Common cause: the push message printed `/pulls/new/<branch>` and someone (Frank, or an `xdg-open` you ran) clicked Create. `GET /pulls?state=all`, find it, then `PATCH /pulls/N` with your title+body instead of re-posting.
|
||
- `GET /pulls?state=all` on this Gitea omits `head`/`base` per entry — read `/pulls/N` for those, or key off `number`/`user`.
|
||
- Issues: same shape, `POST /issues` `{title, body}`; `PATCH /issues/N` fixes a body retroactively (works for PRs too, since a PR is an issue).
|
||
|
||
## Merge (the 422 trap) — corrected
|
||
`POST /pulls/N/merge` needs **form-encoded** fields; the merge form field name is lowercase **`do`**:
|
||
|
||
```bash
|
||
curl -sS -X POST -K "$cfg" \
|
||
--data-urlencode 'do=merge' --data-urlencode 'merge_style=no-ff' \
|
||
http://192.168.0.143:3000/api/v1/repos/dank/<repo>/pulls/N/merge
|
||
```
|
||
|
||
JSON `{"Do":"merge"}` (what this skill used to claim) and header variants all return `422 {"message":"[Do]: Required"}`. `merge_style` ∈ `no-ff|merge|rebase|squash`. Success returns **200 with an empty body**; verify by landing a checkout rather than re-reading the PR. Merge only with Frank's explicit per-PR OK.
|
||
|
||
## Post-merge landing
|
||
- Command-center: the checkout IS the deployment — `git -C ~/.omp fetch -q origin && git -C ~/.omp merge --ff-only origin/main`.
|
||
- Package repos (`~/Projects/<pkg>`): same `--ff-only`, then reinstall (`omp plugin upgrade <name>`) and `cmp` the installed `dist/index.js` against the repo `dist/index.js` to prove the live artifact is the merged one.
|
||
- **A fast-forward that adds or removes a tracked `node_modules` changes it on disk.** Landing a PR that deleted the tracked symlink wiped the dev checkout's installed deps, and `bun run typecheck` then died with `tsc: command not found` (exit 127); `bun install` restored them. Check `ls -ld node_modules` after landing anything that touched it.
|
||
- Prune: `git worktree remove --force <path>` (force: untracked deps link), `git branch -D`, `git push origin --delete <branch>` (allowed: local Gitea).
|
||
|
||
## Attribution footer (AGENTS.md)
|
||
Every issue/PR body ends with `---`, then `cwd: <session cwd> · session: <session id>`. Read the real values, never invent an id: the session id is the suffix of the transcript dir name (`ls -t ~/.omp/agent/sessions/` → `…_01a155bb-…`), and `cwd` is that session's own working directory. `local://<name>.md` artifacts live under that same session dir in `local/` — resolve with `read local://<name>.md` (its header prints the absolute path) instead of guessing.
|