Files
mopac-pmo/archive/REPORT-20260829-1600-gitea-cli.md

99 lines
5.0 KiB
Markdown

# REPORT — mopac-gitea-go (mgit): Gitea Go client + CLI delivered
Task: TASK-20260829-1600 — Gitea Go client (Redmine #508)
Delivered: 2026-08-29 16:00 turn
Repo: https://git.knownelement.com/ukrrs/mopac-gitea-go (created via the
Gitea API with the tea token; public, AGPLv3)
Clone: ~/projects/meta/MOPAC/gitea-go (origin main, 9 commits, pushed)
Status: **v0 complete, green, pushed. Ready for harness PR-flow cutover.**
No Redmine writes in this turn (PMO owns the ticket, per directive).
## What landed
Stdlib-only Go module `git.knownelement.com/ukrrs/mopac-gitea-go`:
library package `gitea` (Client + sentinel errors — importable by the
harness for the turn PR flow, no shell parsing), CLI `mgit`
(`internal/cli` + `cmd/mgit`), stateful fake Gitea
(`internal/fakegitea`), 0600-disciplined config loader
(`internal/config`). ALL dev work ran inside the digest-pinned
`golang:1.26-bookworm` builder via `./dev.sh`/`Makefile` (host never
runs a toolchain). TDD red/green at every layer; atomic conventional
commits pushed to origin main.
| Commit | Content |
|---|---|
| `1e47f5b` | Seed: AGPLv3 LICENSE (first commit) |
| `eb49c62` | Dev harness (digest-pinned Docker builder) |
| `43bc581` | Config: GITEA_* env + 0600 env-file loading |
| `7c11987` | gitea client core, fake server, repo endpoints |
| `c251cd6` | Branch list + commit-status endpoints |
| `5b35e8c` | Pull-request create/list/get/merge endpoints |
| `9b08589` | mgit CLI (flags accepted after positionals) |
| `5feac3c` | End-to-end smoke vs containerized fake |
| `55dbc9a` | README to docs standard |
## Command surface
| Command | Effect | Key flags |
|---|---|---|
| `mgit repo create -n NAME` | create repo (own account or org) | `--owner ORG`, `--desc`, `--private`, `--auto-init`, `--default-branch` |
| `mgit repo list` | list repos | `--owner OWNER` |
| `mgit repo show OWNER/REPO` | one repo | |
| `mgit branch list OWNER/REPO` | branches + tip shas | |
| `mgit status create OWNER/REPO REF` | report commit status | `--state success\|pending\|error\|failure`, `--context`, `--description`, `--target-url` |
| `mgit status list OWNER/REPO REF` | combined + latest per check | |
| `mgit pr create OWNER/REPO` | open PR (head may be `owner:branch`) | `--title`, `--body FILE\|-`, `--base`, `--head` |
| `mgit pr list OWNER/REPO` | list PRs | `--state open\|closed\|all` |
| `mgit pr show OWNER/REPO NUMBER` | one PR with body | |
| `mgit pr merge OWNER/REPO NUMBER` | merge open PR | `--do merge\|rebase\|rebase-merge\|squash\|fast-forward` |
Auth is token-scoped via env `GITEA_URL`/`GITEA_KEY` (or a 0600
`--config` env file; `MGIT_CONFIG`/`~/.config/mgit/env` also honored).
`-o json` on every command. Exit codes 0/1/2; API errors are one
parseable stderr line (`mgit: gitea: not found: http 404`). Flags parse
before OR after positionals.
## Test results
- Unit suite: **71 passing tests** (39 functions + 32 table-driven
subtests), 0 failures/skips, across three packages (library
round-trips against the fake, error mapping 401/403/404/409/422/5xx
-> sentinels with "http NNN", unreachable, malformed, redaction,
config 0600 discipline, CLI surface incl. usage tables and stdin
body).
- The fake Gitea implements real semantics the tests pin:
latest-status-per-context folding, worst-of combined status,
short-sha (>= 7 char) resolution, merge-style validation, 409 on
duplicate open PRs and re-merges, branch-advance on merge.
- Token redaction: the fake DELIBERATELY echoes the presented token in
every error body; library test + CLI test + the smoke redaction pass
prove the token appears nowhere else (it lives only in the
`Authorization: token` header).
## Smoke (end-to-end, `./dev.sh smoke`)
Builds mgit + fakegitea in the digest-pinned builder, boots the fake in
a container on 127.0.0.1:8602, drives the real binary from the host
through a 0600 env file: repo create (private, auto-init) -> list ->
list -o json (parsed) -> show; branch list; status pending -> success
flip -> combined "success (1 checks)"; PR create (body from file) ->
list -> show -> merge squash -> closed list still shows it, open list
empty; failure paths: missing repo (exit 2, one-line `http 404`
stderr), wrong token (`http 401`, no token material), loose env file
(exit 1, "insecure mode"); final redaction scan over every captured
out/err. Result: **smoke: OK**. Only the exact container ID the script
spawns is ever removed.
## Notes for the harness
- PR flow for turns = `branch list` -> `status create` on head sha ->
`pr create` -> `pr merge`, one static binary, JSON everywhere.
- One bootstrapping fix during the turn: the smoke fake initially used
per-boot `go run` (cold compile inside the container raced the
readiness window); it now builds `bin/fakegitea` alongside the CLI in
the same builder step and boots the compiled binary.
- A stale `go run ./smoke/fakeserver` debug container (harness repo's,
not this repo's) was removed by exact ID match during cleanup; no
broad process or container operations were used at any point.