21 KiB
Migrating to tea 1.0
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:
- Make sure your instances run Gitea 28 or newer (Server requirements).
- Move the old configuration away, or point tea 1.0 at a new directory (Configuration).
- Log in again (Logging in).
- Run
tea auth setup-gitto replace the old!tea login helperGit credential entries (Git credential helper). - Regenerate shell completions (Shell completion).
- Update scripts: global flags, commands, arguments and output.
Run tea <command> --help for the full flag list of any command.
Server requirements
Requires Gitea 28 or newer. tea no longer probes /api/v1/version or
/swagger.v1.json to adapt to older servers: on an older instance, commands
that need a newer endpoint fail with the server's HTTP error instead of a
"not supported" message.
Configuration and login
Old configuration file
Older releases kept logins and preferences in one file, config.yml in the tea
directory under the platform configuration directory (~/.config/tea/config.yml
on Linux; releases up to 0.16 also fell back to ~/.tea/tea.yml when that file was missing). tea 1.0 stores preferences in
config.yml and instances and accounts in hosts.yml, in the same directory by
default; tea config path prints both paths. When it finds an old-format file it
stops with:
legacy tea configuration: archive the old config.yml or select a separate TEA_CONFIG_DIR, then run tea auth login; existing files have not been changed
Nothing is imported automatically. Either archive the old file:
mv ~/.config/tea/config.yml ~/.config/tea/config.yml.old # adjust the path on macOS and Windows
mv ~/.tea/tea.yml ~/.tea/tea.yml.old # if you still use the oldest location
or keep it and give tea 1.0 its own directory with TEA_CONFIG_DIR. Do not run
an old tea against a directory written by tea 1.0: it reads the new config.yml
as an empty login list and overwrites it on its next save.
tea config list shows effective preferences. Old settings map as follows:
Old config.yml |
tea 1.0 |
|---|---|
preferences.editor: true |
tea config set editor "<command>" (a command, not a switch) |
preferences.flag_defaults.remote |
tea config set remote <name> |
login default: true |
tea config set default_host <url> |
login insecure, ssh_host |
tea config set --host <url> insecure true, ... ssh_host <host[:port]> |
login ssh_key, ssh_agent, ssh_certificate_principal, ssh_key_agent_pub |
tea config set --host <url> --user <name> ssh_key … (also ssh_agent, ssh_certificate_principal, ssh_key_fingerprint) |
Logging in again
Accounts are identified by instance URL and username instead of a login name.
tea auth login --host <url> replaces tea login add:
| Old | tea 1.0 |
|---|---|
tea login add --url URL --token TOKEN |
tea auth login --host URL --with-token < token-file (the token is read from stdin) |
tea login add --oauth |
tea auth login --host URL --oauth |
tea login add --ssh-key PATH / -s |
tea auth login --host URL --ssh-key PATH |
--ssh-agent-key / -a, --ssh-agent-principal / -c |
same long flags, no shorthands |
--insecure / -i |
--insecure |
--user, --password, --otp, --scopes (create a token with basic auth) |
removed: create a token in the web UI (Settings → Applications) or use --oauth |
--name, --no-version-check |
removed |
--redirect-url |
removed: OAuth always listens on a random loopback port |
--git-credentials / --helper / -j |
tea auth setup-git --host URL |
Token logins of the old release kept the token in plain text under
logins[].token in the old config.yml, so you can pipe it straight into
tea auth login --with-token instead of creating a new one.
Credentials go to the operating-system keyring (service tea-cli-v2). Pass
--credential-store file to store them in private files under the configuration
directory instead. The old release's OAuth tokens (keyring service tea-cli, and
credentials.json in the old configuration directory) are not read; delete them once you
have logged in again.
Managing accounts:
| Old | tea 1.0 |
|---|---|
tea login list, tea login status, tea whoami |
tea auth status (--verify checks each credential) |
tea login default NAME |
tea config set default_host URL, and tea auth switch USER --host URL for the active account |
tea login edit |
tea config set --host URL <key> <value> |
tea login delete NAME, tea logout NAME |
tea auth logout --host URL [--user USER] |
tea login oauth-refresh |
tea auth refresh |
Environment variables
| Variable | tea 1.0 |
|---|---|
GITEA_TOKEN, GITEA_INSTANCE_URL |
supported; TEA_HOST is an alternative to GITEA_INSTANCE_URL. A GITEA_TOKEN without either was ignored; it now makes authenticated commands fail with exit 4, so set the instance or unset the token to use stored credentials |
GITEA_INSTANCE_INSECURE, GITEA_INSTANCE_SSH_HOST |
supported |
GH_TOKEN |
no longer read: use GITEA_TOKEN |
GITEA_SERVER_URL, GITEA_SERVER_TOKEN, GITEA_SERVER_USER, GITEA_SERVER_PASSWORD, GITEA_SERVER_OTP, GITEA_SCOPES |
removed with tea login add |
New variables: TEA_CONFIG_DIR, TEA_HOST, TEA_USER, TEA_REPO, TEA_EDITOR,
TEA_PAGER and TEA_COLOR; see tea config --help. Environment credentials are
never written to disk.
Git credential helper
tea login helper setup and tea login add --git-credentials wrote two entries
to ~/.gitconfig for each instance: an empty credential.<url>.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. Register the new helper:
tea auth setup-git --host https://gitea.example.org
It replaces every helper configured for the instance URL, the old entry
included. With --keep-helpers it adds tea's helper after them instead; then
remove the old entry with
git config --global --unset-all credential.https://gitea.example.org.helper '^!tea login helper$'.
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
current repository.
Global flags
| Old | tea 1.0 |
|---|---|
--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 / -q <expression> |
--debug / --vvv |
--debug |
--confirm / -y, --force / -f on destructive commands |
--yes / -y |
-R now means --repo: tea issue list -R origin fails with a usage error
(did you mean --remote origin?) instead of selecting the origin remote. Use
--remote origin.
New global flags: --no-input disables prompts, editors and browsers, --yes
(-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 old built-in aliases (c, e, i,
ms, lgtm, …) are gone. Subcommands take gh's verb aliases instead (ls,
rm, new, add, co, search issues, search prs); tea <command> --help
lists them. tea alias set NAME EXPANSION defines your own top-level shortcuts, for example
tea alias set prs 'pr list --author @me'.
Top-level groups:
| Old | tea 1.0 |
|---|---|
issues, issue, i |
issue |
pulls, pull, pr |
pr |
labels |
label |
milestones, ms |
milestone |
releases, r |
release |
times, t |
time |
organizations, org |
org |
repos |
repo |
branches, b |
branch |
actions secrets |
secret |
actions variables |
variable |
actions runs |
run |
actions workflows |
workflow |
webhooks, hooks |
webhook |
comments, c |
issue comment / pr comment |
notifications, n |
notification |
ssh-keys |
ssh-key |
admin users, admin u |
admin user |
logins, login, logout, whoami |
auth (see Logging in) |
clone, C |
repo clone |
open, o |
removed (see Removed features) |
Groups no longer run a default action: tea issues listed issues and
tea issues 5 showed issue 5; use tea issue list and tea issue view 5.
The same applies to pulls, notifications and the other groups.
Subcommands:
| Old | tea 1.0 |
|---|---|
issues reopen, pulls reopen (alias open) |
issue reopen, pr reopen |
pulls approve <n> [comment] |
pr review <n> --approve [--body TEXT] (-a) |
pulls reject <n> <comment> |
pr review <n> --request-changes --body TEXT (-r) |
pulls review <n> |
pr review <n> (--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 <name-or-id> |
labels delete --id ID |
label delete <name-or-id>... |
labels list --save |
label export |
milestones issues <name> |
milestone issue list <milestone> (title or ID) |
milestones issues add|remove |
milestone issue add|remove <milestone> <number>... |
releases assets create |
release asset upload |
times add |
time create |
times list [user|#issue] |
time list [--username USER|--issue N] |
repos search |
search repo |
repos create-from-template |
repo create --template OWNER/REPO |
actions secrets create |
secret set (value: see below) |
actions variables set |
variable set (value: see below) |
actions variables list --name NAME |
variable view NAME |
actions runs delete (alias cancel) |
run delete; run cancel cancels without deleting |
actions workflows dispatch |
workflow run |
webhooks update |
webhook edit |
comments add <n> [body] |
issue comment <n> --body TEXT (also pr comment); --edit-last and --delete-last change your last comment |
comments edit <id> [body], comments delete <id>..., a body piped on stdin |
issue comment edit <id> --body TEXT, one issue comment delete <id> per comment, --body-file - to read stdin |
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 |
completion pwsh |
completion powershell |
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) |
--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 / -l, --assignee / -a, --milestone / -m |
--referenced-version / -v |
--ref |
--keyword / -k |
--search / -S |
--from / -F (issue list) |
--since |
--states / -s, --types / -t (notifications) |
--status, --type |
--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 |
Other long names changed too, mostly on admin user, repo fork, repo edit and
repo migrate; an old name fails with an unknown-flag error, so check --help
of each command you script. 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), -d on pr create is
--draft (was --description), -b on pr create is --body (was --base;
use -B, otherwise the pull request silently targets the default branch), -r
on pr create is --reviewer (was --repo; use -R) and -o on secret and
variable is --org (was --output). Two flags kept their name but
changed meaning:
workflow run:-fwas--followand now means--field KEY=VALUE;--followhas no shorthand.-r(--ref) and-i(--input) are unchanged.api:--field(-f) sent a string and--Field(-F) a typed value. Now--raw-field/-fsends a string and--field/-Fa typed value, so the shorthands behave as before.--data/-dbecame--input FILE(or-for stdin), and--output/-obecame--output-file.
Changed positional arguments
| Command | Old | tea 1.0 |
|---|---|---|
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 <tag> |
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 |
optional body argument | --body, --body-file or --editor |
secret set, variable set |
<name> [<value>], --file, --stdin |
value from --body, --body-file FILE|- or piped standard input |
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 text for people; scripts should use JSON:
# 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 -q '.[].title' | xargs -n1 notify-send
--json alone prints every field; --json a,b selects fields and rejects unknown
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.
When stdout is not a terminal, lists print tab-separated values without header,
color or links, with times in RFC 3339 UTC, and a --page hint goes to stderr;
prefer --json in scripts. Commands that change resources report each change on
stderr only on a terminal; warnings, such as a step that was skipped or failed,
always go to stderr. Create commands print the URL of the new resource on
stdout, so url=$(tea issue create …) works; other changes print nothing when
piped. Bodies are rendered as Markdown only on a terminal (tea config set markdown raw turns this off). See tea help formatting for the terminal layout.
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
| Feature | Replacement |
|---|---|
tea open [number|issues|pulls|releases|commits|branches|wiki|labels|milestones|activity|settings|<ref>] |
tea issue view N --web, tea pr view N --web, tea repo view --web; for other pages open $(tea repo view --json url --jq .url)/releases and similar |
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 <key> <value> |
Output formats and --fields |
--json and --jq |
contrib/autocomplete.* scripts and the --generate-shell-completion protocol |
tea completion <shell> |
GH_TOKEN, GITEA_SERVER_*, GITEA_SCOPES |
GITEA_TOKEN, tea auth login |
--vvv |
--debug |
Shell completion
Completion scripts saved from an older tea completion (or the removed
contrib/autocomplete.* files) call tea with --generate-shell-completion, which
tea 1.0 rejects, so completion silently stops working. Regenerate them:
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 <shell> --help explains each shell's requirements.
Packagers can ship the pre-generated scripts and man pages from the release docs
archive, or run make docs.
Behavior changes
- Short flags changed meaning:
pr create -bis now--body(it was--base, now-B), andrelease edit --draft/--prereleaseare switches, so the 0.16 form--draft falseis refused instead of being read as a tag; use--draft=false. search repo --privateand--archivedare switches. Write--private=falsewhere 0.16 accepted--private false.pr review reply <number> <comment-id>takes the reply with--body,--body-fileor--editor, not as a third argument.repo createnames its template options--templateand--trust-model; seetea repo create --help.--repotakesOWNER/REPOor a URL; use--cwd DIRto point at a local checkout.pr checkout N(aliasco) 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 usesOWNER/BRANCHfor a fork orpr/N. The branch tracks the head branch, sogit pushupdates the pull request. Use--branch NAME(-b) to choose the name,--updateto fast-forward an existing branch, or--detachfor a detached checkout.pr reviewtakes the review type as--approve/-a,--request-changes/-ror--comment/-c; on a terminal, leaving it out starts an interactive review.label listshows only the labels of the selected scope: the repository, or the organization with--org ORG. 0.16 also listed the organization labels of an organization-owned repository;--exclude-orgwas removed.notificationcommands act on your whole inbox by default (the old--mine).-R OWNER/REPOor--this-repolimits them to one repository.config get KEYprints only the value;--show-sourceadds where it comes from.auth statusexits with 4 when there is no account or a credential is missing or rejected.auth tokenprints the active account's token.webhook editmust be told what to do with the delivery authorization header on every call, because Gitea replaces it: pass--authorization-header,--authorization-header-fileor--clear-authorization. The signing secret cannot be edited; recreate the hook to rotate it.notification mark-read,mark-unread,pinandunpinact on the given IDs; pass--allto apply them to every notification matching the filters.--no-inputdisables prompts, editors and browsers. With--json, tea never prompts unless--interactiveis given; required values must come from flags.- Destructive commands and
pr mergeconfirm on a terminal and need--yes(-y) elsewhere. --limit(-L) accepts 1 to 1000 and--pagea 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
--jsonoutput, timestamps the server leaves unset print as"", or asnullfor optional fields such as deadlines, instead of0001-01-01T00:00:00Z. - More input is checked before anything is sent: release titles, issue titles
and comment bodies cannot be empty, a review needs a body or inline comments
(unless it approves), team creation needs a permission, migrations from
GitHub, Gitea, GitLab and Gogs need an access token, and
workflow runmust find its workflow and check the inputs (without--refit uses the checked-out branch, else the default branch). pr mergemerges the head it read and refuses when the branch moved in the meantime; run it again to merge the new head.- Preferences follow the git/gh order: an explicit
tea config setvalue wins overVISUAL/EDITOR,PAGERandBROWSER, which win over the defaults;TEA_EDITOR,TEA_PAGERandTEA_COLORoverride both.