Files
gitea-tea/docs/migration-1.0.md
T

24 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:

  1. Make sure your instances run Gitea 28 or newer (Server requirements).
  2. Move the old configuration away, or point tea 1.0 at a new directory (Configuration).
  3. Log in again (Logging in).
  4. Run tea auth setup-git to replace the old !tea login helper Git credential entries (Git credential helper).
  5. Regenerate shell completions (Shell completion).
  6. 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 help environment. 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.

tea 0.16 cloned over SSH when the login had an SSH key; tea 1.0 follows git_protocol, which defaults to https. To keep cloning over SSH, run tea config set git_protocol ssh (add --host <url> for one instance).

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, which takes NAME, OWNER/REPO, HOST/OWNER/REPO or a URL: tea issue list -R origin no longer selects the origin remote. Inside a checkout with that remote tea fails and suggests --remote origin; elsewhere origin is read as a repository of your account. 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
labels list (also listed organization labels; --exclude-org) label list lists the repository's labels only; use label list --org OWNER for an organization's, and drop --exclude-org
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
issues list outside a repository, --owner / --org tea search issue [--owner OWNER]
issues edit / pulls edit --milestone "" --remove-milestone (or --milestone 0)
--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
--desc (repo edit) --description / -d
--template true|false, --private true|false, --archived true|false (repo edit) --as-template, --private, tea repo archive / unarchive; these are on/off switches, so write --private=false (--private false names a repository false)
--owner / -O (repo fork) --org
--inactive (webhook) --active=false
--expires / -x (milestones create) --deadline
--kind issue|pull (milestones issues) --kind issues|pulls|all
--no-admin, --no-restricted, --inactive, --no-allow-git-hook, --no-allow-import-local, --no-allow-create-organization (admin users edit) --admin=false, --restricted=false, --active=false, --allow-git-hook=false, and so on
--allow-login (admin users edit) --prohibit-login=false
--password-stdin, --no-must-change-password (admin users) --password-file -, --must-change-password=false
-u, -p, -e (admin users create) the username argument or --username, --password, --email
--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: -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), and --output / -o became --output-file. api exits 1 on a response outside 2xx (0.16 printed the body and exited 0), -F key=@FILE keeps the file's trailing newline, and --output-file refuses to replace an existing 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)
pulls close|reopen|edit several indexes pr close|reopen|edit take one pull request (number, URL or branch); loop to change several
issue comment optional body argument --body, --body-file or --editor
secret set, variable set optional value argument, --file, --stdin --body VALUE, --body-file FILE (or -), or standard input; one trailing line break is removed

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 -b is now --body (it was --base, now -B), and release edit --draft / --prerelease are switches, so the 0.16 form --draft false is refused instead of being read as a tag; use --draft=false.
  • search repo --private and --archived are switches. Write --private=false where 0.16 accepted --private false.
  • pr review reply <number> <comment-id> takes the reply with --body, --body-file or --editor, not as a third argument. repo create names its template options --template and --trust-model; see tea repo create --help.
  • --repo takes OWNER/REPO or a URL; use --cwd DIR to point at a local checkout.
  • 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.
  • label list shows 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-org was removed.
  • 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 exits with 4 when there is no account or a credential is missing or rejected. auth token prints the active account's token.
  • 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 cannot be edited; recreate the hook to rotate it.
  • admin user edit --password no longer forces a password change at the next sign-in (admin user create still does); add --must-change-password to require one.
  • notification mark-read, mark-unread, pin and unpin act on the given IDs; 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 and pr merge confirm on a terminal and need --yes (-y) elsewhere.
  • --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.
  • 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. workflow run uses the checked-out branch (which must be on the server) or the default branch when there is no --ref, and refuses unknown inputs, missing required inputs and values that a choice, boolean or number input does not accept.
  • pr merge merges 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 set value wins over VISUAL/EDITOR, PAGER and BROWSER, which win over the defaults; TEA_EDITOR, TEA_PAGER and TEA_COLOR override both.