rickub/clipublic Fork
⑂ main
Commits
⬇ Clone ▾
git clone https://git.rickub.com/rickub/cli.git
git clone ssh://git@rickub.com/rickub/cli.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

mr edit --title and a new issue edit: catch the CLI up to the PATCH routes (rickub#230) #7

Closed@ogirardot opened this issue Sep 7, 2026 · 1 comment
  • @ogirardot opened this issue · Sep 7, 2026

    Summary

    rickub/rickub MR #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/rickub web/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.go apiPullUpdate and web/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 silent 200 over 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 uniform 404 on 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/rickub in commit adf8ad3 ("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 — the edit subcommand (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) calls client.UpdateMergeRequestBody, i.e. the
      older single-field POST .../merge-requests/{n}/body route.
    • cmd/issue.go — there is no edit subcommand at all. The AddCommand list
      (~line 126) is list/view/create/close/reopen/comment/label/milestone/assign/
      delete-comment/labels.
    • internal/api/endpoints.go — has UpdateMergeRequestBody (~line 210) and nothing
      that issues either PATCH. There is no UpdateIssueBody either.

    Impact

    • A wrong merge request title is uncorrectable from the terminal. The server can
      fix it; rickub cannot 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 edit does not exist, so an issue's title and description are
      both terminal-uneditable — a strictly worse position than merge requests, which at
      least have pr 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

    1. internal/api/endpoints.go — two helpers next to UpdateMergeRequestBody:

      • UpdateMergeRequest(ctx, owner, repo string, number int, in MergeRequestPatch) (*MergeRequest, error)
      • UpdateIssue(ctx, owner, repo string, number int, in IssuePatch) (*Issue, error)

      Both issue PATCH to the routes above. The patch struct must use pointer fields
      (Title *string, Body *string) with omitempty-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 trip title_required, and sending "" for a body would blank a
      description the user never meant to touch. This is the load-bearing detail.

    2. cmd/pr.go — editCmd gains --title:

      • --title and --body are 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 answer 400 empty_update.
      • --body keeps going through the shared requireBody/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 UpdateMergeRequest so one invocation can correct both fields
        in a single call.
    3. cmd/issue.go — a new edit <number> subcommand with --title / --body, the same
      rules, calling UpdateIssue; register it in the AddCommand list.

    4. Keep the success line proving the bytes landed (the "Updated the description of #%d (%d bytes)" shape in runPREdit), 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.

    5. rickub/rickub's FEATURES.md CLI inventory line intentionally does not list
      issue edit today, 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's title_required) and never blanks a
      title; --body ""/blank stdin is refused locally per issue #94.
    • Wire-level tests (the internal/api httptest style already used in
      endpoints_issues_test.go) assert the method is PATCH, 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.
  • @ogirardot · Sep 7, 2026

    Closed via 1 — merged 8e1b39cbe92cdc54d5e7fe39b0515020ec03590f into main.

Sign in to comment.