diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7e6bc51c..24ff0dae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,13 +105,17 @@ Use explicit singular noun/verb commands, such as `tea issue list` and missing fields when the shared prompting policy permits it, invoke services, and render results through injected streams. Destructive commands require confirmation or `--yes`; `--no-input` disables implicit interaction. JSON results go to stdout; -diagnostics and prompts go to stderr. Lists register `--limit`/`--page` with -`pageFlags` and print through `writeList`, which renders one `terminal.Table` -(ID or number first, then title or name, then state) through the pager. +diagnostics, prompts and the `✓`/`!` mutation messages (`done`/`warn`) go to stderr. +Lists register `-L/--limit` and `--page` with `pageFlags` and print through +`writeTable` with a `listSpec`: on a terminal an uppercase header row, a "Showing N of +M …" line, relative times and links in the first column; piped, tab-separated values +without header. Single items print through `writeView`. Every command has an +`Example`, and every command group a `Long` text. ### Internal Package Structure -- `internal/app`: Cobra commands and application dependency construction. +- `internal/app`: Cobra commands and application dependency construction. It never + calls the API directly: every request goes through `internal/services`. - `internal/services`: typed workflows over `gitea.dev/sdk`, independent of CLI flags and terminal I/O, including pagination policy. Services map SDK types to tea's output types; requests the SDK cannot make yet go through the raw helpers in diff --git a/docs/example-workflows.md b/docs/example-workflows.md index 9c7b372f..29c6d02f 100644 --- a/docs/example-workflows.md +++ b/docs/example-workflows.md @@ -21,7 +21,7 @@ a nonzero exit code. For an explicitly authorized merge in CI: ```sh -tea pr merge 123 --repo owner/repository --method rebase --no-input +tea pr merge 123 --repo owner/repository --rebase --yes --no-input ``` Keep credentials in the secret environment or credential store. Never write a token diff --git a/docs/migration-1.0.md b/docs/migration-1.0.md index 11e78c3a..dc88d3f3 100644 --- a/docs/migration-1.0.md +++ b/docs/migration-1.0.md @@ -1,10 +1,10 @@ # Migrating to tea 1.0 -tea 1.0 rebuilds the CLI around explicit, singular `noun verb` commands (similar -to GitHub's `gh`), a split configuration, and per-instance accounts. There are no -compatibility aliases: old command names, plural groups and most short flags are -gone, and scripts need updating. This guide lists what changed and what to use -instead. +tea 1.0 rebuilds the CLI around explicit, singular `noun verb` commands modeled +on GitHub's `gh` (including its verb aliases and short flags), a split +configuration, and per-instance accounts. Old command names, plural groups and +many old short flags are gone, and scripts need updating. This guide lists what +changed and what to use instead. Quick checklist: @@ -13,8 +13,8 @@ Quick checklist: 2. Move the old configuration away, or point tea 1.0 at a new directory ([Configuration](#old-configuration-file)). 3. Log in again ([Logging in](#logging-in-again)). -4. Replace the old `!tea login helper` Git credential entries - ([Git credential helper](#git-credential-helper)). +4. Run `tea auth setup-git` to replace the old `!tea login helper` Git + credential entries ([Git credential helper](#git-credential-helper)). 5. Regenerate shell completions ([Shell completion](#shell-completion)). 6. Update scripts: [global flags](#global-flags), [commands](#renamed-commands), [arguments](#changed-positional-arguments) and [output](#output-formats). @@ -118,16 +118,20 @@ never written to disk. `tea login helper setup` and `tea login add --git-credentials` wrote two entries to `~/.gitconfig` for each instance: an empty `credential..helper` and `!tea login helper`. tea 1.0 has no `login` command, so Git now gets an error -from that helper and falls back to prompting. Remove both entries, then register -the new helper: +from that helper and falls back to prompting. Register the new helper: ```sh -git config --global --get-all credential.https://gitea.example.org.helper -git config --global --unset-all credential.https://gitea.example.org.helper '^!tea login helper$' -git config --global --unset-all credential.https://gitea.example.org.helper '^$' tea auth setup-git --host https://gitea.example.org ``` +`tea auth setup-git` replaces every credential helper configured for the +instance URL, the old `!tea login helper` entry included, so that other helpers +neither answer for tea nor keep a copy of its token. Pass `--keep-helpers` to +add tea's helper after the existing ones instead; then remove the old entry +yourself with +`git config --global --unset-all credential.https://gitea.example.org.helper '^!tea login helper$'`. +`tea auth status` reports whether the helper is configured. + `tea repo clone`, `tea pr checkout`, `tea pr clean`, `tea repo sync` and `tea pr create --agit` authenticate Git without any helper; `tea auth setup-git` is only needed for plain `git` commands. Add `--local` to configure only the @@ -140,24 +144,28 @@ current repository. | `--repo` / `-r` | `--repo` / `-R` | | `--remote` / `-R` | `--remote` (no shorthand) | | `--login` / `-l` | `--host URL`, plus `--user NAME` when the instance has several accounts | -| `--output` / `-o`, `--fields` / `-f` | `--json [fields]` and `--jq ` | +| `--output` / `-o`, `--fields` / `-f` | `--json [fields]` and `--jq` / `-q ` | | `--debug` / `--vvv` | `--debug` | -| `--confirm` / `-y`, `--force` / `-f` on destructive commands | `--yes` | +| `--confirm` / `-y`, `--force` / `-f` on destructive commands | `--yes` / `-y` | `-R` now means `--repo`: `tea issue list -R origin` fails with `invalid repository URL` instead of selecting the `origin` remote. Use `--remote origin`. New global flags: `--no-input` disables prompts, editors and browsers, `--yes` -accepts confirmations, `--cwd` selects the directory used for repository +(`-y`) accepts confirmations, `--cwd` selects the directory used for repository discovery, and `--sudo USER` acts as another user (administrators only). ## Renamed commands -All command groups are singular, and the built-in aliases (`ls`, `rm`, `c`, `e`, -`i`, `ms`, `co`, `lgtm`, …) are gone. `tea alias set NAME EXPANSION` defines your -own top-level shortcuts, for example `tea alias set co 'pr checkout'`; aliases -for subcommands such as `issue ls` are not possible. +All command groups are singular, and the old built-in aliases (`c`, `e`, `i`, +`ms`, `lgtm`, …) are gone. Subcommands take gh's verb aliases instead: `ls` for +`list`, `rm` for `delete`, `new` for `create`, `add` for `issue comment create`, +`pr comment create`, `ssh-key create` and `time create`, `co` for +`pr checkout`, and `search issues` / `search prs` for `search issue` / +`search pr`. `tea --help` lists the aliases of each command. +`tea alias set NAME EXPANSION` defines your own top-level shortcuts, for example +`tea alias set prs 'pr list --author @me'`. Top-level groups: @@ -194,16 +202,16 @@ Subcommands: | Old | tea 1.0 | |---|---| | `issues reopen`, `pulls reopen` (alias `open`) | `issue reopen`, `pr reopen` | -| `pulls approve [comment]` | `pr review --event APPROVED [--body TEXT]` | -| `pulls reject ` | `pr review --event REQUEST_CHANGES --body TEXT` | -| `pulls review ` | `pr review ` | +| `pulls approve [comment]` | `pr review --approve [--body TEXT]` (`-a`) | +| `pulls reject ` | `pr review --request-changes --body TEXT` (`-r`) | +| `pulls review ` | `pr review ` (`--comment` / `-c` for a plain review comment) | | `pulls review-comments` | `pr review comments` | | `pulls reply`, `resolve`, `unresolve` | `pr review reply`, `pr review resolve`, `pr review unresolve` | -| `labels update --id ID` | `label edit ID` | -| `labels delete --id ID` | `label delete ID` | +| `labels update --id ID` | `label edit ` | +| `labels delete --id ID` | `label delete ...` | | `labels list --save` | `label export` | -| `milestones issues ` | `milestone issue list `, or `milestone issue list --by-name ` | -| `milestones issues add\|remove` | `milestone issue add\|remove ...` (`--by-name` selects by title) | +| `milestones issues ` | `milestone issue list ` (title or ID) | +| `milestones issues add\|remove` | `milestone issue add\|remove ...` | | `releases assets create` | `release asset upload` | | `times add` | `time create` | | `times list [user\|#issue]` | `time list [--username USER\|--issue N]` | @@ -214,7 +222,7 @@ Subcommands: | `actions runs delete` (alias `cancel`) | `run delete`; `run cancel` cancels without deleting | | `actions workflows dispatch` | `workflow run` | | `webhooks update` | `webhook edit` | -| `comments add [body]` | `issue comment create --body TEXT` (also `pr comment create`) | +| `comments add [body]` | `issue comment --body TEXT` (also `pr comment`); `--edit-last` and `--delete-last` change your last comment | | `notifications read\|unread [all\|id]` | `notification mark-read\|mark-unread [id]...`; use `--all` for every filtered notification | | `ssh-keys add` | `ssh-key create` | | `login helper setup` | `auth setup-git` | @@ -225,76 +233,105 @@ Renamed flags that appear on many commands: | Old | tea 1.0 | |---|---| | `--description` / `-d`, `--description-file` (issues, pulls, comments) | `--body`, `--body-file` | -| `--note` / `-n`, `--note-file` / `-f` (releases) | `--body`, `--body-file` | +| `--note` / `-n`, `--note-file` / `-f` (releases) | `--notes` / `-n`, `--notes-file` / `-F` (`--body` and `--body-file` are accepted too) | | `--content` / `-c` (wiki) | `--body`, `--body-file` | -| `--labels` / `-L`, `--assignees` / `-a`, `--milestones` / `-m` | `--label`, `--assignee`, `--milestone` | +| `--labels` / `-L`, `--assignees` / `-a`, `--milestones` / `-m` | `--label` / `-l`, `--assignee` / `-a`, `--milestone` / `-m` | | `--referenced-version` / `-v` | `--ref` | -| `--keyword` / `-k` | `--search` | +| `--keyword` / `-k` | `--search` / `-S` | | `--from` / `-F` (issue list) | `--since` | | `--states` / `-s`, `--types` / `-t` (notifications) | `--status`, `--type` | -| `--page` / `-p`, `--limit` / `--lm` | `--page`, `--limit` | -| `--style` / `-s` (pr merge) | `--method` / `-s` | +| `--page` / `-p`, `--limit` / `--lm` | `--page`, `--limit` / `-L` | +| `--style` / `-s` (pr merge) | `--merge`, `--squash` or `--rebase`, or `--method` / `-s`; without one, the repository's default merge style | | `--owner` / `-O`, `--init`, `--labels`, `--trustmodel`, `--branch`, `--template` (repo create) | `--org`, `--auto-init`, `--label-template`, `--trust-model`, `--default-branch`, `--as-template` | | `--inactive` (webhook) | `--active=false` | | `--out` (man) | `--output-file` / `-o` | -Most other short flags were dropped; the long names are unchanged unless listed -here. Two flags kept their name but changed meaning: +The long names are unchanged unless listed here. The short flags follow gh +instead of old tea: `-L` limit, `-s` state, `-l` label, `-a` assignee, `-A` +author, `-m` milestone, `-t` title, `-b` body, `-F` body file, `-e` editor, `-w` +web, `-S` search, plus the global `-R`, `-q` and `-y`. Some old shorthands now +mean something else: `-L` is `--limit` (was `--labels`), and `-d` on `pr create` +and `pr list` is `--draft` (was `--description`). Two flags kept their name but +changed meaning: - `workflow run`: `-f` was `--follow` and now means `--field KEY=VALUE`; `--follow` has no shorthand. `-r` (`--ref`) and `-i` (`--input`) are unchanged. - `api`: `--field` (`-f`) sent a string and `--Field` (`-F`) a typed value. Now `--raw-field` / `-f` sends a string and `--field` / `-F` a typed value, so the shorthands behave as before. `--data` / `-d` became `--input FILE` (or `-` for - stdin), `--include` lost `-i`, and `--output` / `-o` became `--output-file`. + stdin), and `--output` / `-o` became `--output-file`. ## Changed positional arguments | Command | Old | tea 1.0 | |---|---|---| -| `milestone close\|reopen\|delete\|edit\|view` | milestone name | milestone ID; add `--by-name` to select by exact title | -| `milestone issue list\|add\|remove` | milestone name | milestone ID; add `--by-name` to select by exact title | -| `release asset delete ` | attachment name | asset ID; add `--by-name` to select by exact name | -| `label edit\|delete` | `--id ID` | `ID` | +| `milestone close\|reopen\|delete\|edit\|view` | milestone name | milestone title or ID | +| `milestone issue list\|add\|remove` | milestone name | milestone title or ID | +| `release asset delete ` | attachment name | asset name or ID | +| `label edit\|delete` | `--id ID` | label name or ID | +| `team view\|edit\|delete`, `team member\|repo ...` | team ID | `ORG/TEAM` or team ID | | `repo delete` | `--name NAME --owner OWNER` | optional `OWNER/REPO` argument (or `--repo`) | -| `issue comment create` | optional body argument | `--body`, `--body-file` or `--editor` | +| `issue comment` | optional body argument | `--body`, `--body-file` or `--editor` | -A milestone whose title is a number is now read as an ID unless you pass -`--by-name`. Destructive commands still ask for confirmation unless `--yes` is -given. +A number is read as an ID; pass `--by-name` to match a milestone, label or asset +whose name is a number. Destructive commands still ask for confirmation unless +`--yes` is given. ## Output formats `--output` / `-o` (`table`, `simple`, `csv`, `tsv`, `yaml`, `json`) and `--fields` -are gone. Human output is plain text; scripts should use JSON: +are gone. Human output is text for people; scripts should use JSON: ```sh # old: tea issues list --fields index,title -o csv tea issue list --json number,title --jq '.[] | "\(.number),\(.title)"' # old: tea notifications --mine -o simple | xargs notify-send -tea notification list --mine --json title --jq '.[].title' | xargs -n1 notify-send +tea notification list -q '.[].title' | xargs -n1 notify-send ``` `--json` alone prints every field; `--json a,b` selects fields and rejects unknown -names; `--jq` filters the result. Commands that produce no resource, such as -deletes, reject `--json` with a usage error instead of printing nothing. +names; `--jq` / `-q` filters the result and implies `--json`. Commands that +produce no resource, such as deletes, reject `--json` with a usage error instead +of printing nothing. See `tea help formatting`. -Lists print aligned columns without a header row and with fewer columns than -before: the ID or number first, then the title or name, then the state and a few -details (`issue list` and `pr list` show number, title, state and author). Do not -parse them; the columns may change. On a color terminal the first column links to -the resource. Tabs and line breaks in list cells become spaces, and control -characters such as escape sequences are dropped from all human output, bodies -included. Commands that change several resources at once, such as -`issue close 1 2`, `issue edit`, `milestone close`, `release edit` or -`notification mark-read`, print one table of the changed items in the matching -list's columns. When a list continues past the requested page, tea prints a -`--page` hint on stderr. +Lists follow gh. On a terminal they print a "Showing 30 of 112 open issues in +alice/project" line, an uppercase header row and columns fitted to the terminal +width, with times relative ("about 3 hours ago"), states colored and the first +column linked to the web page (`tea config set hyperlinks never` turns the links +off). An empty list prints a message on stderr and exits 0. When stdout is not a +terminal, lists print tab-separated values without header, color or links, with +times in RFC 3339 UTC and stable columns, so `cut -f1` works; a `--page` hint +goes to stderr when more results follow. Still prefer `--json` in scripts: the +columns may change. Tabs and line breaks in list cells become spaces, and +control characters such as escape sequences are dropped from all human output, +bodies included. -Issue, pull request and other bodies are printed as raw Markdown instead of being -rendered. Long output on a terminal goes through a pager (`TEA_PAGER`, the -`pager` preference or `PAGER`). Each command starts the pager at most once. +Commands that change resources report each change on stderr, such as +`✓ Closed issue alice/project#5 (Fix login)` or `! … is already closed`, only +when stdout is a terminal. Create commands print the URL of the new resource +on stdout, so `url=$(tea issue create …)` works; other changes print nothing +when piped. With `--json` they print the changed resource instead. + +Issue, pull request, release and comment bodies are rendered as Markdown on a +terminal and printed as written when piped; `tea config set markdown raw` always +prints them as written. Long output on a terminal goes through a pager +(`TEA_PAGER`, the `pager` preference or `PAGER`). Each command starts the pager +at most once. + +## Exit codes + +| Code | Meaning | +|---|---| +| 0 | success, including an empty list | +| 1 | the command failed | +| 2 | usage error: an unknown command or flag, or a missing or invalid argument | +| 3 | canceled: a declined confirmation, an aborted prompt or a timeout | +| 4 | authentication is missing or was rejected | +| 8 | `tea pr checks`: some checks are still pending | +| 130 | interrupted with Ctrl-C | + +`tea help exit-codes` prints this table. ## Removed features @@ -304,7 +341,6 @@ rendered. Long output on a terminal goes through a pager (`TEA_PAGER`, the | Basic-auth token creation (`login add --user/--password/--otp/--scopes`) | create a token in the web UI, or `tea auth login --oauth` | | `login edit` | `tea config set --host URL ` | | Output formats and `--fields` | `--json` and `--jq` | -| Markdown rendering of bodies | raw Markdown, optionally paged | | `contrib/autocomplete.*` scripts and the `--generate-shell-completion` protocol | `tea completion ` | | `GH_TOKEN`, `GITEA_SERVER_*`, `GITEA_SCOPES` | `GITEA_TOKEN`, `tea auth login` | | `--vvv` | `--debug` | @@ -316,22 +352,37 @@ Completion scripts saved from an older `tea completion` (or the removed tea 1.0 rejects, so completion silently stops working. Regenerate them: ```sh -source <(tea completion bash) # ~/.bashrc -source <(tea completion zsh) # ~/.zshrc +eval "$(tea completion bash)" # ~/.bashrc +autoload -U compinit && compinit; source <(tea completion zsh) # ~/.zshrc tea completion fish > ~/.config/fish/completions/tea.fish -tea completion powershell | Out-String | Invoke-Expression # PowerShell profile +tea completion powershell | Out-String | Invoke-Expression # PowerShell profile ``` +`tea completion --help` explains the requirements of each shell and how +to install the script system-wide. + Packagers can ship the pre-generated scripts and man pages from the release docs archive, or run `make docs`. ## Behavior changes -- `pr checkout N` creates or reuses the local branch `pr/N`; it no longer - switches to another local branch that already points at the PR head. Use - `--branch NAME` to check out into a branch you choose (it must point at the PR - head; `--update` fast-forwards it) or `--detach` for a detached checkout. - `-b` now takes the branch name. +- `pr checkout N` (alias `co`) checks out a local branch named after the PR's + head branch when that name is free or already points at the head; otherwise + it uses `OWNER/BRANCH` for a fork or `pr/N`. The branch tracks the head + branch, so `git push` updates the pull request. Use `--branch NAME` (`-b`) to + choose the name, `--update` to fast-forward an existing branch, or `--detach` + for a detached checkout. +- `pr review` takes the review type as `--approve` / `-a`, `--request-changes` / + `-r` or `--comment` / `-c`; on a terminal, leaving it out starts an + interactive review. +- `notification` commands act on your whole inbox by default (the old + `--mine`). `-R OWNER/REPO` or `--this-repo` limits them to one repository. +- `config get KEY` prints only the value; `--show-source` adds where it comes + from. +- `auth status` groups accounts by instance, marks each as usable or not, and + reports whether the Git credential helper is configured. It exits with 4 when + there is no account or a credential is missing or rejected. `auth token` + prints the token of the active account for scripts. - `webhook edit` must be told what to do with the delivery authorization header on every call, because Gitea replaces it: pass `--authorization-header`, `--authorization-header-file` or `--clear-authorization`. The signing secret @@ -340,9 +391,11 @@ archive, or run `make docs`. pass `--all` to apply them to every notification matching the filters. - `--no-input` disables prompts, editors and browsers. With `--json`, tea never prompts unless `--interactive` is given; required values must come from flags. -- Destructive commands confirm on a terminal and need `--yes` elsewhere. -- `--limit` accepts 1 to 100 and `--page` a positive number; other values are - usage errors instead of being passed to the server. +- Destructive commands and `pr merge` confirm on a terminal and need `--yes` + (`-y`) elsewhere; the help of each such command lists `--yes`. +- `--limit` (`-L`) accepts 1 to 1000 and `--page` a positive number; other + values are usage errors instead of being passed to the server. tea pages + through the server's smaller pages to reach the limit. - In `--json` output, timestamps the server leaves unset print as `""`, or as `null` for optional fields such as deadlines, instead of `0001-01-01T00:00:00Z`.