5.1 KiB
spec, blocked-by
| spec | blocked-by |
|---|---|
| gitea-axi | 0004-issue-create-and-comment |
What to build
The label command group: label list, label create, label edit, label delete.
label list takes --limit (default 500) and outputs a count line plus labels: [ { name } ].
label create requires --name and --color (hex without #; the # is prepended before the API call) with optional --description; it is idempotent via a case-insensitive existence check, reporting create: already_exists instead of failing.
label edit <name> and label delete <name> resolve the positional name via the standard case-insensitive label lookup with VALIDATION_ERROR when not found.
label delete is deliberately not idempotent — a nonexistent label is refused rather than reported as success (see ADR 0010).
Acceptance criteria
label listrenders the count line andlabels:block; empty repos get the explicit empty statelabel create --name --colorcreates the label, prepending#to the color, and outputscreated: ok+label: <name>- Creating an existing label (case-insensitive) outputs
create: already_exists+ the existing name, exit 0 label edit <name>applies--name/--color/--descriptionand outputsedit: ok+ the resulting namelabel edit/label deleteon an unknown name yieldVALIDATION_ERROR(exit 2)label delete <name>outputsdelete: ok+label: <name>- Fixture-server tests cover create, idempotent re-create, edit, delete, and the unknown-name refusals
- End-to-end tests exercise the live-Gitea semantics the fixture server cannot attest to: the
#-prefixed color round-trip on create, idempotency against the live listing, and name→id resolution behind edit/delete (plus the not-idempotent delete refusal)
Implementation Notes
Built the label group in src/commands/label.ts, wired into src/cli.ts (dispatcher plus the two most-common entries in the top-level help), following the sibling issue/pr command patterns.
Deviations and decisions:
label editrequires at least one change. The spec/gh-axi reference leaves all edit flags optional, but sending an emptyPATCHis a pointless call, so an edit with none of--name/--color/--descriptionis refused withVALIDATION_ERROR— mirroringissue edit's "requires at least one change" guard for consistency within gitea-axi.- Resulting name for
edit/create/deletecomes from the API response (edited.name,label.name), not the input, so the reported name is the server's canonical echo (correct casing, and the unchanged original when--namewas omitted). createvscreatedoutput keys. The success key iscreated: okand the idempotent-hit key iscreate: already_exists— two different top-level keys. This is spec-mandated (spec lines 342–343) rather than thealready: trueshape the dependency no-ops use; kept verbatim to match the fixed contract.- Flat
renderObjectoutput shape. AddedrenderObject(item, help)tosrc/render.tsbecause the label create/edit/delete outputs are flat top-level fields (created: ok/label: <name>), which the siblingrenderDetail(nests under anoun:block) cannot produce. This is the spec's output shape, not a new convention chosen freely. - Shared lookup + positional helpers (cleanups from review).
Extracted
findLabel/resolveLabeland a sharedlabelNotFoundmessage intosrc/lookup.ts, reused by the existingresolveLabelIds; and extractedparseSinglePositionalinsrc/flags.ts, now shared byparsePositionalNumberand the label name positionals, removing the duplicated count-check/error scaffolding. label listuses a single-page fetch with--limit(default 500), readingX-Total-Countfor the count line, rather than the exhaustive paginationresolveLabel/resolveLabelIdsuse; the spec asks only for--limit, and a repo with >500 labels is signalled by thecount: N of T totalline.
Follow-ups added after review
- End-to-end tier extended.
The task originally scoped its tests to the fixture-server tier only, matching the precedent of the preceding PR-command tasks (0011–0014), none of which added e2e cases.
On reflection the label commands sit squarely inside the e2e tier's charter — "behavior the fixture server cannot attest to" — because a fixture server never enforces that Gitea's
CreateLabelOption.colorrequires the leading#, nor that edit/delete really key on the numeric label id. Added atest/e2e/mutations.test.tslabel-lifecycle case (create → idempotent re-create → edit → delete, verified against live state via a newfetchLabelsprovisioner helper) plus the not-idempotent delete refusal. These run only in CI (gated onGITEA_AXI_E2E_URL). - Unit-tier coverage backfill.
The initial fixture tests covered only the happy paths and unknown-name refusals, leaving the help output, validation errors, and API-error propagation untested — enough to drop the repo below its global branch-coverage gate.
Added confirming fixture tests for those behaviors, bringing
src/commands/label.tsto ~96% line / ~86% branch and the suite back over its thresholds.