Skip to content

feat: butter changes list / show, and resolve a build's commit to its change - #11

Merged
ryanlitalien merged 1 commit into
mainfrom
feat/1961-changes-read-path
Sep 25, 2026
Merged

ryanlitalien merged 1 commit into
mainfrom
feat/1961-changes-read-path

Conversation

@ryanlitalien

Copy link
Copy Markdown
Member

Adds the missing read path for changes (commits and changelists across git, Perforce, and Lore), so "did my commit land in ButterStack?" and "which change did this build come from?" can be answered from the terminal instead of the web UI.

What

  • butter changes list --project <id>: same row shape as builds list. Filters: --source lore|git|perforce, --since <date>, --identifier <id>, --orphaned, --limit <n>, --after <cursor>. Prints the --after value for the next page. --json returns the whole {changes, pagination} envelope (not just the array like builds list), because without it a script has no next_cursor.
  • butter changes show <change_id|commit> --project <id> [--json]: an all-digit argument is the change id that list prints; anything else (full git SHA, a Perforce build's p4-<n>, lore-<n>) is resolved through the API's exact identifier filter first, then the detail is fetched. Detail shows identifier, source, author, approvals, tasks, latest build, description, linked builds, and files (first 20).
  • builds show now prints butter changes show <commit> --project <id> under the Commit line, which is the build-to-change join.
  • A 403 insufficient_scope naming read:changes prints a line saying to run butter auth login again.
  • The read-only scope preset now includes read:changes.
  • README documents both commands and the reissue note.

Things to look at twice

  • p4- prefix. A Perforce BuildRun stores p4-<n> but its change stores the bare <n>, and the API's identifier filter is exact and unvalidated, so passing p4-207 through returns 200 []. The client strips it and adds source_type=Changelist.
  • Digits mean change id. A bare Perforce changelist number passed to show is read as a change id; the error message and README point at changes list --identifier <n> for that case.
  • Lore vs Perforce share source_type=Changelist server-side, so --source lore|perforce filters the page client-side by the lore- prefix. A filtered page can hold fewer than --limit rows (documented).
  • Not resolvable from the client: a Lore or Perforce build linked to its changelist only through the server-side build_runs_changelists join, with a commit hash that is not the change identifier. That attribution belongs server-side.
  • --after, not --cursor: --cursor is already a boolean flag (butter mcp install --cursor) and can never carry a value.

Depends on

The server must allow read:changes on CLI-issued tokens. Until ButterStack/butter_stack#1961's PR deploys, butter auth login cannot mint a token with it, and these commands get a 403 with the reissue hint. Tokens issued before that deploy need butter auth login again.

Verification

  • npm test: 44/44 (12 new tests: list rendering and paging hint, flag-to-query mapping, lore/perforce split, --json envelope, bad --source, show by id, show by p4-<n>, show by SHA with --json, no-match error, 403 reissue hint, usage error, builds show hint).
  • Ran against a local ButterStack server with the server change: a real butter auth login loopback minted a token carrying read:changes, then changes list, changes list --source perforce, changes show p4-207, changes show <sha> --json, changes show lore-12, and a no-match lookup all behaved as above. A token without read:changes got the 403 plus the reissue line.

No version bump in this PR; a release is needed to publish it.

… its change

The changes API (commits and changelists across git, Perforce, and Lore)
had no CLI command, so "did my commit land in ButterStack?" could only be
answered in the web UI.

- `butter changes list --project <id>` with --source lore|git|perforce,
  --since, --identifier, --orphaned, --limit, --after <cursor>, --json.
  Lore and Perforce share one source type server-side and are split here
  by the lore- identifier prefix. --json keeps the pagination envelope.
- `butter changes show <change_id|commit>`: an all-digit argument is the
  change id; anything else (full SHA, p4-<n>, lore-<n>) is resolved
  through the exact identifier filter first. A Perforce build stores
  p4-<n> while its change stores <n>, so the prefix is stripped.
- `builds show` prints the `changes show` command for its commit.
- A 403 naming read:changes says to run `butter auth login` again, since
  tokens issued before the scope joined the CLI set do not carry it.
- The read-only scope preset now includes read:changes.

The paging flag is --after, not --cursor: --cursor is already the boolean
client flag of `butter mcp install`.
@ryanlitalien
ryanlitalien merged commit f527be6 into main Sep 25, 2026
1 check passed
@ryanlitalien
ryanlitalien deleted the feat/1961-changes-read-path branch September 25, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant