86 lines
5.5 KiB
Markdown
86 lines
5.5 KiB
Markdown
---
|
|
name: gitea-api-pr-operations
|
|
description: "\"Open, list, merge, and diagnose issues/PRs and manage labels on the Dankclaw Gitea API (192.168.0.143:3000) with the token stored in ~/.config/secrets/gitea — incl. the token-only write auth (Basic reads but 422s on POST/PATCH), the required merge payload, where review comments actually live on 1.27.3, and how to recover the session id for the mandatory body footer.\""
|
|
---
|
|
|
|
# Gitea API operations (Dankclaw `dank/command-center`, `dank/serverAppConfigs`)
|
|
|
|
## Where
|
|
- Token: `~/.config/secrets/gitea` (`http://user:TOKEN@192.168.0.143` single line — git-credential URL form, NOT `username=`/`password=` lines).
|
|
Extract WITHOUT echoing:
|
|
`tok=$(sed -nE 's#^http://[^:/@]+:([^@]+)@192\.168\.0\.143.*#\1#p' ~/.config/secrets/gitea | head -1)`
|
|
- Build a 600-perm temp curl config and use `curl -K`:
|
|
`cfg=$(mktemp) && chmod 600 "$cfg" && printf 'header = "Authorization: token %s"\n' "$tok" > "$cfg"`
|
|
Delete `$cfg` after use. NEVER pass `-H "Authorization: token $tok"` (the literal
|
|
header lands in transcript + logs). NEVER echo the token.
|
|
- Base URL: `http://192.168.0.143:3000/api/v1/repos/dank/<repo>`.
|
|
|
|
## Auth: reads tolerate Basic, writes DO NOT (measured 2026-10-20)
|
|
GETs succeed with an HTTP Basic header built from the same credential, but every
|
|
POST/PATCH/DELETE with Basic returns `407/422 {"message":"token is required"}`.
|
|
Always use the `Authorization: token <tok>` header for everything — never debug
|
|
"permissions" when the header shape is the fault.
|
|
|
|
## Recipes (all verified live)
|
|
- **Open PR** (token only needed for private repos; this one reads publicly but
|
|
POST needs auth): `POST /pulls` JSON `{title, head, base:"main", body}`.
|
|
- **PATCH PR title/body**: `PATCH /pulls/<n>` with JSON body `{title}` and/or
|
|
`{body}`. Returns 201 with the full PR. Use `jq -n --arg` to build JSON; for
|
|
multi-line bodies write the text to a temp file and `--arg b "$(cat b.txt)"`.
|
|
(jq CLI: a bare `---` argument after the program is parsed as a flag → error;
|
|
fold footers into the --arg string instead.)
|
|
- **List open PRs**: `GET /pulls?state=open` → `jq -r '.[] | "#\(.number) \(.head.ref) \(.title)"'`.
|
|
- **Read PR**: `GET /pulls/<n>` (no token; public repo) → `jq -c '{state,merged,mergeable,head:.head.ref}'`.
|
|
`mergeable:false` is often Gitea's *not-yet-checked* state, not a conflict — prove a real
|
|
conflict locally with `git merge-tree --write-tree HEAD origin/main` before believing it.
|
|
- **MERGE**: `POST /pulls/<n>/merge` with `-H 'Content-Type: application/json'`
|
|
and body **`{"Do":"merge"}`** — this build REQUIRES the `Do` field:
|
|
`{}` or `{"DoNotMerge":false,...}` → 422; `{"Do":"merge"}` → 200 +
|
|
`{"state":"closed","merged":true,"merge_commit_sha":"..."}`. Verify with a plain
|
|
GET /pulls/<n> afterwards (merge endpoint's own stdout may not parse).
|
|
Merge is the `--no-ff` merge commit on main; then `git -C ~/.omp fetch && git
|
|
merge --ff-only origin/main` propagates the live deployment.
|
|
- **Issues**: same shape at `/issues` (`POST {title, body}`), `/issues/<n>/comments`
|
|
(`POST {body}`, `PATCH .../comments/<id>`) — both work with the temp-cfg auth.
|
|
- **Labels**: `GET /labels` (list incl. numeric `id`); `POST /labels`
|
|
`{"name":"<name>","color":"#rrggbb"}` to create; attach by ID with
|
|
`POST /issues/<n>/labels {"labels":[<id>,...]}` (names are rejected);
|
|
detach with `DELETE /issues/<n>/labels/<id>`. serverAppConfigs gained
|
|
needs-triage / needs-info / ready-for-agent / ready-for-human / wontfix
|
|
(2026-10-20) alongside the older `human-only`.
|
|
|
|
## Review comments: the API lies on 1.27.3 (learned 2026-10-18)
|
|
- `GET /pulls/<n>/comments` (inline review comments) → **404 `not found` on every
|
|
route** (token or not, `?pull_request_id=` or not) even when a review with
|
|
comments exists.
|
|
- `GET /pulls/<n>/reviews` works (shows `state`, `comments_count`) but bodies are empty.
|
|
- **The comments are readable from the PR HTML**:
|
|
`curl -sS http://192.168.0.143:3000/dank/command-center/pulls/<n> -o /tmp/pr.html`
|
|
then extract `<div class="content comment-content">` blocks
|
|
(python: `re.finditer(r'comment-content', src)` → widen ±800 chars, strip tags).
|
|
Duplicates appear (suggested-change UI shows text twice); dedupe mentally.
|
|
- Per-conversation resolve API also 404s — flag resolutions in an issue comment instead.
|
|
|
|
## Body-footer law (AGENTS.md)
|
|
Every issue/PR body ends with `---` line then `cwd: <wd> · session: <id>`. Keep
|
|
it inside the --arg string (not a `---` CLI arg).
|
|
|
|
`cwd` is THIS session's own working directory (note a build worktree separately if they
|
|
differ). When `/status` is unreachable (headless/subagent context), recover the session id
|
|
from the session store instead of inventing one:
|
|
`ls -t ~/.omp/agent/sessions/<cwd-slug>/ | head -2` → `<timestamp>_<uuid>` (the same uuid as
|
|
`<uuid>.jsonl`); the slug is the cwd with `/` replaced by `-`. Subagent files live under the
|
|
**parent** session's directory, so the newest entry there is the parent id.
|
|
|
|
## Gotchas
|
|
- Content-Type: POSTs with `-d @file.json` MUST carry
|
|
`-H 'Content-Type: application/json'` on the command line. `-H` entries put in
|
|
the curl config file are ignored for this (header ordering quirk).
|
|
- `xdg-open <pr-url>` lands the PR in Frank's Firefox the moment it is opened —
|
|
law, not courtesy.
|
|
- `jq: cannot index "not found" with "user"` = endpoint 404'd into an error
|
|
object; check `-w '%{http_code}'` before blaming jq.
|
|
- The token in `.git/config`'s remote URL is the same secret; the credential
|
|
file (`~/.config/secrets/gitea`) is the one git actually consults (global
|
|
credential.helper) and the one to reuse for API calls.
|