mr edit --title and a new issue edit: catch the CLI up to the PATCH routes (rickub#230) #7
- @ogirardot opened this issue · Sep 7, 2026edited
Summary
rickub/rickubMR #112 (feat(api): edit a merge request title (#230)) shipped the
server half of headless title/description correction. The CLI half never
landed, so as of today there is no way to correct a merge request title or edit an
issue from the terminal at all.Evidence
Server routes that exist today (registered in
rickub/rickubweb/api.go, around the
merge-request and issue route blocks):PATCH /api/v1/repos/{owner}/{repo}/merge-requests/{number} {title?, body?} PATCH /api/v1/repos/{owner}/{repo}/issues/{number} {title?, body?}Semantics (from
web/api_pulls.goapiPullUpdateandweb/api_issues.go
apiIssueUpdate):- Both fields are optional pointers: a field left out of the JSON stays as it was.
This is a PATCH, not a replace. - A request carrying neither field is
400 empty_update
("nothing to update: send title and/or body"), not a silent200over a no-op. - A blank title is a
400 title_required— it is the one field an issue or MR
cannot lack. A blank description is legitimate and accepted. - Authorization is the existing shared rule — the author, or anyone with write access
to the repo (canModifyPull/canModifyIssue), with a uniform404on refusal so
the route never leaks the existence of a resource the caller may not touch. - Response is the usual merge-request / issue view JSON, reloaded after the write.
The CLI could not ride along in the same MR: the
cli/tree was removed from
rickub/rickubin commitadf8ad3("chore: remove the cli/ tree — the CLI lives in
rickub/cli now"), so the client-side commits in that branch had nothing to rebase onto
and were dropped. The server half merged; the client half was never re-authored here.Current state in this repo, verified against
main:cmd/pr.go— theeditsubcommand (editCmd, roughly line 159) is declared as
"Replace a merge request's description" and registers exactly one flag:
editCmd.Flags().StringVarP(&prBody, "body", "b", "", …)(~line 167). There is no
--title.runPREdit(~line 276) callsclient.UpdateMergeRequestBody, i.e. the
older single-fieldPOST .../merge-requests/{n}/bodyroute.cmd/issue.go— there is noeditsubcommand at all. TheAddCommandlist
(~line 126) is list/view/create/close/reopen/comment/label/milestone/assign/
delete-comment/labels.internal/api/endpoints.go— hasUpdateMergeRequestBody(~line 210) and nothing
that issues either PATCH. There is noUpdateIssueBodyeither.
Impact
- A wrong merge request title is uncorrectable from the terminal. The server can
fix it;rickubcannot ask it to. Any headless flow (CI, scripts, an agent working
over the CLI, anyone on a box without a browser) has to fall back to
rickub api --method PATCH …by hand, or open the web UI. rickub issue editdoes not exist, so an issue's title and description are
both terminal-uneditable — a strictly worse position than merge requests, which at
least havepr edit -b.- This is the same class of gap as issue #94 (a body that could be written but never
revised without a browser): a create-only field is a permanent field for every
non-browser client.
What to add
-
internal/api/endpoints.go— two helpers next toUpdateMergeRequestBody:UpdateMergeRequest(ctx, owner, repo string, number int, in MergeRequestPatch) (*MergeRequest, error)UpdateIssue(ctx, owner, repo string, number int, in IssuePatch) (*Issue, error)
Both issue
PATCHto the routes above. The patch struct must use pointer fields
(Title *string,Body *string) withomitempty-equivalent behaviour, so a flag the
user did not pass is genuinely absent from the JSON rather than sent as""— sending
""for a title would triptitle_required, and sending""for a body would blank a
description the user never meant to touch. This is the load-bearing detail. -
cmd/pr.go—editCmdgains--title:--titleand--bodyare each optional, but at least one must be given; refuse
locally with a non-zero exit when neither is, rather than sending an empty patch and
letting the server answer400 empty_update.--bodykeeps going through the sharedrequireBody/body resolver so-b -still
reads stdin and a body that resolves blank is still refused locally (issue #94), and
so it inherits the fix for #339 (-b -storing a literal-).- Update the command's
Short/Long— they currently say "description" only. - Route it at the new
UpdateMergeRequestso one invocation can correct both fields
in a single call.
-
cmd/issue.go— a newedit <number>subcommand with--title/--body, the same
rules, callingUpdateIssue; register it in theAddCommandlist. -
Keep the success line proving the bytes landed (the
"Updated the description of #%d (%d bytes)"shape inrunPREdit), extended to report the new title when one was set.
A success line that does not echo stored state is what made issue #94 invisible. -
rickub/rickub'sFEATURES.mdCLI inventory line intentionally does not list
issue edittoday, and the API bullet for these routes says the CLI has not caught up.
Update both when this lands.
Acceptance criteria
rickub mr edit <n> --title "new title"renames the merge request and leaves its
description untouched;rickub mr edit <n> -b -still replaces only the description;
passing both in one invocation sets both in a single PATCH.rickub issue edit <n> --title … [--body …]exists and does the same for issues.rickub mr edit <n>/rickub issue edit <n>with no field flags exits non-zero with a
clear message and sends no request.--title ""is refused (locally or by the server'stitle_required) and never blanks a
title;--body ""/blank stdin is refused locally per issue #94.- Wire-level tests (the
internal/apihttpteststyle already used in
endpoints_issues_test.go) assert the method isPATCH, the path is the route above,
and the JSON body contains only the fields the user actually passed — a flag left
off must not appear in the payload at all. - A user who is neither the author nor a repo writer gets the server's uniform
404
surfaced as a clean CLI error, not a stack trace.
- Both fields are optional pointers: a field left out of the JSON stays as it was.
- @ogirardot · Sep 7, 2026edited
Closed via 1 — merged 8e1b39cbe92cdc54d5e7fe39b0515020ec03590f into main.
Sign in to comment.