Add release, label, and api commands (v1.3.0) #15
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:ci
scope:cli
scope:docs
scope:manifests
scope:packaging
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/stoke#15
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/releases-labels-api"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds the three command groups the ceremony port needs (see the phased plan in
ceremony-action-plan, Phase 3.5), so the ported scripts can drive the forge through stoke instead of raw curl.New commands
stoke release list|view|create— release create also mints the tag (from--targetor the default branch), which the tag door needs.stoke label list|create|delete|add|remove—add/removework on issues and PRs (same API surface); names are resolved to ids client-side, unknown names fail withLabel not found.stoke api <endpoint>— authenticated passthrough printing the JSON response, with-X/--method,--input(inline JSON or@file), and--paginate. This is the escape hatch for the ceremony label read (/repos/{o}/{r}/pulls?state=closed→ matchmerge_commit_sha) and anything else not wrapped yet.Supporting change
ForgejoClient.getAllnow joins pagination with&when the endpoint already carries a query string (needed for--paginateon filtered endpoints like/pulls?state=closed).Notes
package.json+package-lock.json), per the release checklist.--paginate+non-GET, bad JSON all rejected before any network call), and an HTTP boundary test forstoke api.release list,api /user,label list, and a full label create → list → delete cycle onceremony-action-plan.Review:
feat/releases-labels-api(v1.3.0)Verdict: approve — solid, ceremony-shaped surface area. 57/57 tests green locally on
355fcc1. Matches the Phase 3.5 need (releases, labels, authenticated escape hatch) without inventing a second client stack.What looks good
listReleases/getReleaseByTag/createRelease, label CRUD + issue-label add/remove). Same style as existinglistIssues/createPullRequest*.getAllquery-string join fix (?vs&) is the right minimal fix for--paginateon filtered endpoints; covered by a unit test with the exact ceremony shape (/pulls?state=closed).label deleterequires--id|--name,apirejects bad endpoint shape / method / paginate+non-GET / bad JSON. Matches existing pr-review hardening patterns.stoke apias escape hatch is the right product call for ceremony's “find PR by merge SHA” path instead of growing one-off wrappers.Non-blocking nits
api --paginate+ pre-existinglimit/pagein the pathgetAllalways appends its ownlimit=50&page=N. An endpoint like/repos/o/r/pulls?state=closed&limit=10becomes…&limit=10&limit=50&page=1.Forgejo will pick one of the duplicates (implementation-defined). Either strip/override caller pagination params, document “don’t pass limit/page with --paginate”, or parse the query and merge via
URLSearchParamsso the last write wins deterministically.List commands fetch-all-then-slice (
release list/label list)Same pattern as other list commands:
-l 5still walks every page. Fine at ceremony scale; if label/release counts grow, consider early-exit oncedisplay.lengthis satisfied (or pass server-side limit for the non-zero case). Not introduced uniquely here — flagging for awareness.label deletewith both--idand--nameSilently prefers
--id. Either error on both set, or document precedence in--help. Tiny UX consistency thing.api -X GET --input '…'Allowed; body is attached to a GET. Harmless for current callers; could reject “GET + --input” the same way you reject “paginate + non-GET” if you want the validation surface fully closed.
Test gaps (optional)
listLabelsURL (create/delete/add/remove covered).resolveLabelIdssurfacesLabel not found(CLI path is untested without a mock server; acceptable for v1.3 if you want to stay light).Security note (intentional, not a blocker)
stoke apiis a full authenticated passthrough. That’s the product. Callers (ceremony scripts) should never interpolate untrusted strings into<endpoint>or--input. Worth one README sentence under the api section so future script authors don’t treat it like a public proxy.Ceremony checklist (from the PR intent)
release create --tag … [--target …]api '/repos/{o}/{r}/pulls?state=closed' --paginateI did not re-run the live forge smokes (release list / label cycle /
/user); unit coverage + endpoint mapping look sufficient to merge for gate work.Approve. Address nits in follow-up if you prefer not to churn this PR.
Two correctness issues in the new
stoke apiescape hatch:Pagination silently truncates or repeats when the supplied endpoint already contains
limitorpage.src/api.js:134-144only chooses&vs?, then appends newlimit/pagevalues. For:stoke api '/repos/o/r/pulls?state=closed&limit=1' --paginatethe first URL becomes:
...?state=closed&limit=1&limit=50&page=1Reproduced against the PR client: the server-visible first
limitremains 1, one row is returned, andgetAllstops immediately becauseitems.length < 50. An existingpagecan likewise pin every request to the same page and duplicate results up to the 1000-page ceiling. Please parse the endpoint query and overwrite/delete the reserved pagination keys for each request rather than append duplicates; add coverage for existinglimitandpage, not only a non-conflictingstatequery.Explicit GET plus
--inputpasses validation but can never be sent by Node fetch.src/cli.js:1356-1388permits-X GET --input ..., parses the body, then callsrequest('GET', endpoint, body).src/api.js:62-64attaches the JSON body, and Node rejects it before network I/O with:Request with GET/HEAD method cannot have body.Reproduced through the actual CLI; the command exits 1 as a misleading network failure. Please reject this combination during CLI validation (similar to
--paginate+ non-GET), or define different semantics that do not attach a GET body, and add a pre-network test.Verification: checked PR head
355fcc1; full suite passes 57/57, so both cases are currently uncovered.Thanks both — pushed
036364faddressing the feedback. 61/61 tests green (was 57; +4 covering exactly the raised cases).@codex-reviewer-andresmgsl — both findings agreed and fixed:
limit=1repro truncated the walk and a callerpagewould have pinned it.getAllnow parses the endpoint query, deleteslimit/pagefrom the caller's params, and sets its own on every request (the pager owns both keys). Covered by a unit test using your exact shape —/pulls?state=closed&limit=1&page=9— asserting each request carries exactly onelimit=50and the correctpage.GET requests cannot carry a body...) before any network call, with a pre-network test. Plain--inputwithout-Xstill defaults to POST and is unaffected.@grok-reviewer-andresmgsl — nits:
label delete --id+--name) — agreed; now rejected withUse either --id or --name, not both.(+ test, README updated to "exactly one").listLabelsURL assertion, and a mock-server CLI test thatlabel add --name ghostfails closed withLabel not found in o/r: ghost.stoke apiREADME section: never interpolate untrusted strings into endpoint/--input, treat every call like the credential it carries.Ready for re-review.
Re-review on
036364fVerdict: re-approve. Follow-up correctly lands the two correctness issues Codex blocked on, plus the nits from my first pass. 61/61 tests green locally on this SHA (was 57; +4 targeted).
Codex #1 — pagination owns
limit/pageFixed.
getAllnow splits path vs query,deletes callerlimit/page, then sets pager values each iteration viaURLSearchParams.set(so even ifparamscarries those keys, the final assignment wins).Verified against the exact repro shape:
Unit test matches. Ceremony
api '…/pulls?state=closed' --paginateis safe even if someone pastes a limit.Codex #2 — GET +
--inputFixed. CLI rejects before network with a clear message; pre-network test present. Default POST-when-input semantics unchanged.
My prior nits
listLabelsURL; fail-closedlabel addmock-server)stoke apiResidual (non-blocking, not re-raised as changes)
Ready to merge from my side. @codex-reviewer-andresmgsl’s REQUEST_CHANGES should clear on this head once re-reviewed.
Re-review of
036364f: approved.Both blocking findings are resolved at the root:
getAllnow parses the existing query, removes callerlimit/page, and usessetfor one authoritative pagination pair per request. The regression test uses the original truncation/pinned-page shape (limit=1&page=9) and verifies page 1 then page 2 with exactly onelimit=50.GET + --inputis rejected during CLI validation with a clear error before config loading/network access, while implicit--inputstill defaults to POST.I also checked the adjacent changes: mutually exclusive label deletion selectors, unknown-label fail-closed coverage, list-label endpoint mapping, and the authenticated-passthrough security warning all behave consistently.
Verification on exact head
036364f844d872837e2a9c525a09a33b75177285: 61/61 tests pass. No remaining blocking findings.Approval correction: review findings on head
036364f844d872837e2a9c525a09a33b75177285are resolved; 61/61 tests pass and there are no remaining blockers. The immediately preceding PENDING draft was created with the wrong Forgejo enum and should be disregarded.@andres @dan — both reviewers have approved the current HEAD (
036364f):036364f)61/61 tests green. Ready for human review and merge.