596cf853e8
`labels.py` handed `_gitea.api` the issue store as a place to put the request file, and on a checkout without a store that quietly created `tmp/issues/.payload/`. Bootstrapping a repository's labels touches no issue at all, so the one rule the store has — nothing materializes it as a side effect of a write — was broken by an operation that has no business knowing the store exists. Where a request body goes was never the caller's decision to make. It is now the transport's: `tmp/payload/`, resolved from `_gitea.py`'s own location the way both domains resolve theirs, so every caller — sync and wiki alike — writes to one directory whatever it was invoked from, and `out_root` is gone from `api`, `add_dependency` and all six call sites. The directory is created by the first write of a run and not before: a `--dry-run` leaves nothing behind. `tmp/` is already gitignored. The name carries the distinction the old path lost. A store holds the only copy of something; this holds debris kept for a retry or a post-mortem, and deleting it costs nothing. A dotdir sitting among an issue's files claimed otherwise, and `ls tmp/issues` started lying about what existed. tests/test_payload_root.py runs the real `labels.py` in a throwaway repo against a fake `tea` on PATH: no store appears, the payloads land in tmp/payload/, a dry run writes nothing, and a run from a subdirectory still resolves to the repo root. Two source checks keep the callers from drifting apart again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
121 lines
5.2 KiB
Markdown
121 lines
5.2 KiB
Markdown
---
|
|
name: wiki
|
|
description: Move wiki pages between a local space and Gitea — fetch a page and everything under it as a local cache, publish a page tree with an update message, list what the wiki holds. Load when the user asks to read/fetch a wiki page, cache a wiki subtree for a discussion, or publish artifacts to the wiki. Organizing artifacts into a page tree (titles, ordering, the index) is /tea:page and needs no network.
|
|
---
|
|
|
|
# /tea:wiki — the bridge between a local space and a Gitea wiki
|
|
|
|
One job: translate between `tmp/wiki/<space>/` and Gitea's wiki JSON, and carry
|
|
the result over the wire. Everything about **what a page tree is** — titles,
|
|
ordering, paths, the index — belongs to `/tea:page` and is imported from there,
|
|
never redefined here.
|
|
|
|
Transport is `tea api` through `skills/sync/scripts/_gitea.py`: the same login
|
|
pin, the same pagination, the same payload files in the same `tmp/payload/`.
|
|
There is no second transport.
|
|
|
|
## The wiki is flat, and that is the whole design
|
|
|
|
Gitea's wiki is a list of pages, not a tree. Nesting exists only inside a
|
|
title, as `/`, and Gitea escapes that title into a filename by rules that are
|
|
its own:
|
|
|
|
| title | `sub_url` |
|
|
|---|---|
|
|
| `Abstract Issue` | `Abstract-Issue` |
|
|
| `zz-probe/child` | `zz-probe%2Fchild.-` |
|
|
| `Simple Chains/Parked/Chain decisions — DC` | `Simple-Chains%2FParked%2FChain-decisions-%E2%80%94-DC` |
|
|
|
|
**`sub_url` is the identity and is never constructed.** It is read back from
|
|
the API and stored in the manifest. Building one by hand that is almost right
|
|
does not fail loudly — it creates a second page and abandons the first.
|
|
|
|
**Never commit a subdirectory into the wiki's git repository.** A real
|
|
`folder/page.md` is invisible to the API and to the web UI. It is a ghost file.
|
|
Do not clone the wiki repo to work in; use these scripts.
|
|
|
|
## Scripts
|
|
|
|
In `<skill-base-dir>/scripts/`.
|
|
|
|
| Script | What it does |
|
|
|---|---|
|
|
| `wiki_ls.py [--prefix T]` | what the wiki actually holds — titles, `sub_url`, last commit. One call, no bodies |
|
|
| `wiki_pull.py [--prefix T] [--space S]` | fetch a page and everything under it into a local space |
|
|
| `wiki_push.py -m MSG [--prefix T] [PATH…]` | publish; create what is new, update what changed, skip what is not |
|
|
| `wikimap.py` | md ↔ wiki JSON, pure — not a command |
|
|
|
|
## Fetching a subtree as a cache
|
|
|
|
"A page and its children" is a prefix test on the title, run against one
|
|
listing call, followed by one GET per page. There is no tree endpoint and no
|
|
bulk-body endpoint.
|
|
|
|
```bash
|
|
python3 scripts/wiki_ls.py --prefix "Simple Chains" # what is there
|
|
python3 scripts/wiki_pull.py --prefix "Simple Chains" # cache it locally
|
|
```
|
|
|
|
A pull **overwrites** the local body — a fetch, not a merge. `synced` tells you
|
|
how old your copy is; re-pull when it matters. Nothing tracks drift.
|
|
|
|
The space defaults to the repo's own `owner/repo`, so a pull with no flags
|
|
caches this repo's whole wiki into `tmp/wiki/<owner>/<repo>/`.
|
|
|
|
## Publishing
|
|
|
|
```bash
|
|
python3 scripts/wiki_push.py -m "Import the simple-chains discussion" --dry-run
|
|
python3 scripts/wiki_push.py -m "Import the simple-chains discussion"
|
|
```
|
|
|
|
`-m` is required and is the wiki commit message — the only record of why a page
|
|
changed, and it shows up in `wiki/revisions/<sub_url>`. One operation, one
|
|
message.
|
|
|
|
Change detection is a hash: a page whose file matches `pushed` is skipped.
|
|
A page with no `sub_url` is created; one with a `sub_url` is edited in place,
|
|
using the title **from the manifest** — sending a different title to the edit
|
|
endpoint is a rename and leaves nothing at the old address.
|
|
|
|
Selection, narrowest first: positional `PATH`-or-`TITLE` arguments (matched
|
|
exactly), then `--prefix`, then the whole space.
|
|
|
|
**Pushing is additive.** A page deleted locally is not deleted in the wiki.
|
|
Removing a published page is an explicit act — the web UI, or
|
|
`tea api --login "$GITEA_LOGIN" -X DELETE repos/{owner}/{repo}/wiki/page/<sub_url>`.
|
|
|
|
## Order of operations for a fresh tree
|
|
|
|
The index links published pages by `sub_url`, which does not exist until the
|
|
first push. So:
|
|
|
|
```bash
|
|
python3 ../page/scripts/page_import.py --from DIR --prefix "Simple Chains"
|
|
python3 scripts/wiki_push.py -m "Import the simple-chains discussion"
|
|
python3 ../page/scripts/page_index.py --prefix "Simple Chains" # now with real links
|
|
python3 scripts/wiki_push.py -m "Index"
|
|
```
|
|
|
|
## Linking an issue to a page
|
|
|
|
An issue's `wiki:` field holds page **titles**, not URLs — a title is a name for
|
|
a document and stays in the domain; the URL is bookkeeping. `wiki_ls.py
|
|
--titles` prints them one per line, which is what to paste.
|
|
|
|
## Endpoints, for when a script is not enough
|
|
|
|
Reach for `/tea:use` and `tea api` directly only for what has no script — a
|
|
delete, or a page's history.
|
|
|
|
| | |
|
|
|---|---|
|
|
| list | `GET repos/{owner}/{repo}/wiki/pages` |
|
|
| read | `GET repos/{owner}/{repo}/wiki/page/{sub_url}` |
|
|
| create | `POST repos/{owner}/{repo}/wiki/new` — `{title, content_base64, message}` |
|
|
| edit | `PATCH repos/{owner}/{repo}/wiki/page/{sub_url}` — same body |
|
|
| delete | `DELETE repos/{owner}/{repo}/wiki/page/{sub_url}` |
|
|
| history | `GET repos/{owner}/{repo}/wiki/revisions/{sub_url}` |
|
|
|
|
The `tea` CLI has no wiki subcommand. `tea api` is the only route.
|