fix: write issue dependencies to Gitea on push
The local `depends:` graph never reached the tracker. push.py sent dependent
issues in topological order but created no native links, so `native_deps` in
_gitea.py was a reader with no writer and the slugs in `## Depends on` stayed
dead prose for anyone reading the issue in Gitea.
Once an issue has its number, every `depends:` entry that also has one now
becomes a real link: POST /repos/{owner}/{repo}/issues/{index}/dependencies
with the blocker's IssueMeta. Topological order means the blocker is already
numbered, so no second pass is needed. Existing links are read back first, so
a repeat push is a no-op and never 409s; a link that fails anyway warns rather
than aborting a run that has already created issues. `--dry-run` prints the
links it would make and touches nothing.
The `## Depends on` prose is still passed through verbatim — the edge the
tracker acts on is the native link, not the text, which is exactly why the
text can be left alone. Removing a link that disappeared from `depends:` is
out of scope and now says so in push.py's docstring.
Establishes tests/: stdlib unittest, the transport stubbed at _gitea.api, no
network. Run with `python3 -m unittest discover -s tests`.
The POST body shape was confirmed against the instance's own swagger.v1.json
(Gitea 1.26.1), not assumed from upstream docs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+38
-6
@@ -131,10 +131,40 @@ at most one `severity/*`, English title with no type prefix, `## Summary` /
|
||||
`## Spec` / `## Acceptance criteria` present). `--force` posts anyway — say why
|
||||
when you use it.
|
||||
|
||||
Issues go up in topological order, dependencies first. A dependency that is
|
||||
still local-only is reported, not silently dropped: the body's `## Depends on`
|
||||
prose is sent verbatim either way, but the `#N` cross-link will be missing
|
||||
until that issue is pushed too.
|
||||
### Dependencies
|
||||
|
||||
Issues go up in topological order, dependencies first, and **the graph goes up
|
||||
with them**. Once an issue has its number, every `depends:` entry that also has
|
||||
one becomes a native Gitea link, so the tracker shows the blocking panel and
|
||||
refuses to close a blocked issue before its blocker.
|
||||
|
||||
The two directions are symmetric, and they use the same endpoint:
|
||||
|
||||
| | direction | endpoint |
|
||||
|---|---|---|
|
||||
| `push.py` | `depends:` → native links | `POST …/issues/{n}/dependencies` |
|
||||
| `pull.py --deps` | native links → `depends:` | `GET …/issues/{n}/dependencies` |
|
||||
|
||||
The POST body is Gitea's `IssueMeta` — `{"index", "owner", "repo"}` naming the
|
||||
**blocker**, posted to the **blocked** issue's endpoint ("make the issue in the
|
||||
url depend on the issue in the form"). `owner`/`repo` travel with it, so a
|
||||
dependency in another repo links correctly.
|
||||
|
||||
- Topological order means the blocker already has its number — no second pass.
|
||||
- A link the tracker already has is skipped: push GETs the existing ones first,
|
||||
so a repeat push is a no-op and a 409 never happens. Should a link fail
|
||||
anyway, it is a warning, not a dead run — the issues are already created.
|
||||
- `--update` carries links that appeared in `depends:` after the first push.
|
||||
- `--dry-run` prints every link it would make (`#?` for a number this run has
|
||||
not handed out yet) and makes no request at all.
|
||||
- **Removing a link is out of scope.** Push only adds. A dependency deleted
|
||||
from `depends:` leaves its Gitea link standing; drop it in the web UI or with
|
||||
`tea api -X DELETE …/issues/N/dependencies`.
|
||||
|
||||
A dependency that is still local-only is reported, not silently dropped: it has
|
||||
no number, so it gets no link. The body's `## Depends on` prose is sent verbatim
|
||||
either way — nothing is lost, but the tracker shows no edge until that issue is
|
||||
pushed too.
|
||||
|
||||
Missing labels are created with the canonical color and, for `type/*` and
|
||||
`severity/*`, `exclusive: true` — `tea labels create` cannot set that field
|
||||
@@ -178,14 +208,16 @@ never check out, create, or write anything.
|
||||
| `labels` | `labels[]` | names both ways; ids only on write |
|
||||
| `assignees` | `assignees[]` | logins |
|
||||
| `milestone` | `milestone.title` | resolved to an id on write |
|
||||
| `depends` | — | slugs; seeded from `#N` on pull |
|
||||
| `depends` | native links | slugs here, `IssueMeta` there; push writes them, `pull --deps` reads them |
|
||||
| — | `ref` | lands in `branch:`; sent only when non-empty |
|
||||
| — | `number`, `html_url` | lands in `gitea:` / `url:` |
|
||||
|
||||
`depends:` is always slugs. The body's `## Depends on` section is human prose
|
||||
and is passed through **unchanged** in both directions: a pull seeds `depends:`
|
||||
from the `#N` it finds there, a push never rewrites what the author wrote. A
|
||||
translator that edits prose churns the body on every round trip.
|
||||
translator that edits prose churns the body on every round trip. The edge the
|
||||
tracker acts on is the native link, not the text — which is exactly why the
|
||||
text can be left alone.
|
||||
|
||||
Comments are **pull-only** in the store: `<id>.comments.md` is written by
|
||||
`pull.py` and `comment.py`, and editing it by hand changes nothing in Gitea.
|
||||
|
||||
Reference in New Issue
Block a user