diff --git a/cli-docs/authentication.md b/cli-docs/authentication.md new file mode 100644 index 00000000..02b29ca2 --- /dev/null +++ b/cli-docs/authentication.md @@ -0,0 +1,182 @@ +--- +sidebar_position: 3 +--- + +# Logging in + +`gt auth login` logs in to a Gitea instance. gt can authenticate in three ways: + +| Method | Flag | Best for | +| --- | --- | --- | +| OAuth in a web browser | `--oauth` | Interactive use, instances with single sign-on or two-factor authentication | +| Access token | `--with-token` | Any instance, machines without a browser | +| SSH key signing | `--ssh-key`, `--ssh-agent-key`, `--ssh-agent-principal` | Using an SSH key you already have instead of a token | + +gt never asks for your password. On a terminal, `gt auth login` without a method flag asks +for the instance, how to log in, where to keep the token, and whether Git should use gt +for the instance: + +```bash +gt auth login +``` + +## OAuth + +```bash +gt auth login --host https://gitea.example.com --oauth +``` + +gt opens the instance's authorization page in your browser, or prints its URL when no +browser opens. You sign in there as you do on the web, so single sign-on through an +external identity provider and two-factor authentication work unchanged. gt listens on +`127.0.0.1` for the answer, so the browser has to run on the same machine as gt. OAuth +needs this interaction and is refused with `--no-input`. + +By default the token has write access to everything but administration. `--scopes` +requests fewer, using the [token scope](/development/oauth2-provider#scopes) names: + +```bash +gt auth login --host https://gitea.example.com --oauth --scopes read:repository,read:user +``` + +gt refreshes an expiring OAuth token on its own. `gt auth refresh` refreshes it now. + +gt uses the OAuth application that Gitea registers by default for its command line +client, listed as `tea` in [`DEFAULT_APPLICATIONS`](/administration/config-cheat-sheet#oauth2-oauth2). +If an administrator turned it off, register an OAuth2 application that is not a +confidential client, with the redirect URI `http://127.0.0.1`, and pass its client ID with +`--client-id`. gt remembers the ID for the instance. + +## Access tokens + +1. Sign in to the instance in a web browser, with single sign-on or two-factor + authentication if the instance uses them. +2. Under **Settings > Applications**, create an access token with the scopes you need. +3. Save the token in a private file and log in with it: + + ```bash + gt auth login --host https://gitea.example.com --with-token < token.txt + ``` + + On a terminal, `--with-token` without input asks for the token instead. + +gt needs `read:user` to find out which account a token belongs to. Add the scopes of the +commands you use: + +| Scope | Commands | +| --- | --- | +| `repository` | `repo`, `pr`, `release`, `branch`, `run`, `workflow`, `secret` and `variable` of a repository, `webhook`, `wiki` | +| `issue` | `issue`, `label`, `milestone`, `time`, and comments on pull requests | +| `organization` | `org`, `team`, and `secret`, `variable` and `webhook` with `--org` | +| `notification` | `notification` | +| `package` | `package` | +| `user` | `ssh-key`, and `secret` and `variable` with `--account` | +| `admin` | `admin` | + +Choose `write:` for the commands that change something, `read:` for the ones that only +read. Gitea counts pull request comments, labels and milestones as issue operations, so +working with pull requests usually needs both `write:repository` and `write:issue`. The +[scopes table](/development/oauth2-provider#scopes) describes each scope. + +A token pasted with `--with-token` cannot be refreshed. When it expires or is revoked, log +in again. + +## Where credentials are kept + +gt keeps the instances and accounts in `hosts.yml` and its preferences in `config.yml`, in +its configuration directory. `gt config path` prints both files; `GITEA_CLI_CONFIG_DIR` +moves the directory. + +Tokens are not written to these files. `--credential-store` chooses where they go: + +- `keyring` (default): the system keyring, such as the macOS Keychain, the Windows + Credential Manager, or the Secret Service on Linux. When no keyring is available, the + login fails rather than falling back to a file. +- `file`: a `credentials` directory in the configuration directory that only your user + can read. Use it where no keyring exists, such as in a container or over SSH. + +A token given in `GITEA_TOKEN` is never stored. See [Using gt in CI](ci.md). + +`gt auth status` shows the accounts on each instance, the active one, and how each +authenticates; `gt auth status --verify` checks every credential against its instance. +`gt auth token` prints the token of the active account, for scripts that call the API +themselves. `gt auth logout` forgets an account and deletes its stored credential. + +## SSH keys + +Instead of a token, gt can sign each API request with an SSH key that is registered for +your account on the instance: + +```bash +gt auth login --host https://gitea.example.com --ssh-key ~/.ssh/id_ed25519 +``` + +When a certificate (`id_ed25519-cert.pub`) is next to the key, gt signs with the +certificate. gt asks for the passphrase of an encrypted key. To sign with a key held by +`ssh-agent`, pass its fingerprint with `--ssh-agent-key`; to sign with an agent +certificate, pass its principal with `--ssh-agent-principal`. Accounts that sign requests +have no token, so `gt auth token` has nothing to print for them. + +## Git over HTTPS + +gt's own clone and checkout commands authenticate without any setup. For `git push` and +`git fetch` over HTTPS, let Git ask gt for credentials: + +```bash +gt auth setup-git +gt auth setup-git --host gitea.example.com +# only for the current repository, always as alice +gt auth setup-git --local --user alice +``` + +Git then uses the active account of the instance, or the account of `--user`. Other +credential helpers, such as a system keychain, are turned off for the instance, since they +would answer first and keep a copy of gt's token; `--keep-helpers` keeps them. +`gt auth login` on a terminal offers to do this for you. + +## Several instances and accounts + +Run `gt auth login` once per instance and account. Commands pick the instance from the +repository's Git remote, `--host`, `GITEA_CLI_HOST`, the `default_host` setting, or the only +instance you are logged in to; `gt help environment` gives the full order. + +Each instance has one active account. `gt auth switch` changes it: with two accounts it +toggles between them, with more it asks which one. `--user`, or `GITEA_CLI_USER`, uses +another account for a single command: + +```bash +gt auth switch bob --host gitea.example.com +gt issue list --user alice +``` + +To use another instance by default outside a clone: + +```bash +gt config set default_host https://gitea.example.com +``` + +## Access proxies and client certificates + +An instance behind an access proxy may need extra headers or a TLS client certificate. +`--header` sends a header with every request to the instance; a value of `$NAME` or +`${NAME}` is read from that environment variable each time, so the secret stays out of +gt's files: + +```bash +gt auth login --host https://gitea.example.com --with-token \ + --header 'CF-Access-Client-Id: $CF_ID' \ + --header 'CF-Access-Client-Secret: $CF_SECRET' < token.txt +``` + +`--client-cert` presents a PEM client certificate, and `--client-key` names its +unencrypted PEM key when the key is not in the certificate file. Convert a PKCS#12 file +with: + +```bash +openssl pkcs12 -in cert.p12 -clcerts -nokeys -out cert.pem +openssl pkcs12 -in cert.p12 -nocerts -nodes -out key.pem +``` + +gt keeps these per instance; `gt config set headers`, `client_cert` and `client_key` with +`--host` change them later. `--insecure` skips TLS certificate verification for an +instance, for testing only. diff --git a/cli-docs/ci.md b/cli-docs/ci.md new file mode 100644 index 00000000..02c0f4c0 --- /dev/null +++ b/cli-docs/ci.md @@ -0,0 +1,92 @@ +--- +sidebar_position: 4 +--- + +# Using gt in CI + +In CI there is nobody to log in, answer a prompt or confirm a deletion. Give gt a token and +an instance through the environment, select the repository explicitly, and turn prompts off. + +## Environment + +| Variable | Description | +| --- | --- | +| `GITEA_TOKEN` | Access token, used instead of stored credentials | +| `GITEA_CLI_HOST` or `GITEA_INSTANCE_URL` | URL of the instance the token belongs to | +| `GITEA_CLI_REPO` | Repository as `OWNER/REPO` or URL, like `--repo` | +| `GITEA_INSTANCE_INSECURE` | With `GITEA_TOKEN`, skip TLS verification when `true` | +| `GITEA_INSTANCE_SSH_HOST` | With `GITEA_TOKEN`, the SSH host that identifies the instance's remotes | + +gt never stores a token it reads from `GITEA_TOKEN`, so nothing has to be cleaned up after +the job. Keep the token in your CI system's secrets; never write it into `config.yml` or +`hosts.yml`. Create it as described in [Logging in](authentication.md#access-tokens), with +only the scopes the job needs. + +`gt help environment` lists every variable gt reads. + +## Prompts and confirmations + +`--no-input` disables prompts and the editor and browser that some commands open on their +own. A command that would need to ask for something fails instead, with exit code 2. +Commands that change or delete something ask for confirmation; `--yes` (`-y`) gives it in +advance: + +```bash +gt pr merge 123 --repo owner/repository --rebase --yes --no-input +``` + +`gt auth login --oauth` needs a browser and does not work in CI. To store a token on a +machine that runs jobs, pipe it in instead: + +```bash +gt auth login --host https://gitea.example.com --with-token --credential-store file < token.txt +``` + +## Gitea Actions + +Gitea does not allow secret names starting with `GITEA_`, so store the token under +another name, such as `GT_TOKEN`, and pass it to gt as `GITEA_TOKEN`: + +```yaml +jobs: + deploy: + runs-on: ubuntu-latest + env: + GITEA_TOKEN: ${{ secrets.GT_TOKEN }} + GITEA_CLI_HOST: ${{ gitea.server_url }} + GITEA_CLI_REPO: ${{ gitea.repository }} + steps: + - name: install gt + run: curl -fsSL https://gitea.com/gitea/cli/raw/branch/main/scripts/install.sh | sh + - name: deploy to staging + run: gt workflow run deploy.yml --ref main --field env=staging --field version=1.2.3 --no-input +``` + +## Workflows and runs + +```bash +gt workflow list --no-input --json +gt workflow view deploy.yml --no-input --json +gt workflow run deploy.yml --ref main --field env=staging --field version=1.2.3 --no-input +gt workflow disable deploy.yml --yes --no-input +gt workflow enable deploy.yml --no-input +``` + +`gt workflow run` dispatches a workflow, which needs a `workflow_dispatch` trigger. To wait +for the run, add `--follow`, or find it with `gt run list` and watch it: + +```bash +gt run watch RUN_ID --timeout 10m --no-input +``` + +Both fail when the run completes without success, so the job fails with it. + +`gt pr checks` waits for the checks of a pull request with `--watch`, and exits with 8 +while checks are still pending: + +```bash +gt pr checks 13 --watch && gt pr merge 13 --yes --no-input +``` + +See [Output and scripting](scripting.md) for reading the results of a command, and for the +exit codes. diff --git a/cli-docs/index.md b/cli-docs/index.md new file mode 100644 index 00000000..4b369d89 --- /dev/null +++ b/cli-docs/index.md @@ -0,0 +1,62 @@ +--- +sidebar_position: 1 +slug: / +--- + +# Gitea CLI + +[gt](https://gitea.com/gitea/cli) is the official command line client for Gitea. +It works with repositories, issues, pull requests, releases, Actions workflows and runs, +packages, and the other resources of a Gitea instance, from a terminal or from a script. + +:::info Development version +These pages describe builds from the `main` branch of gitea/cli, published as `nightly` +binaries and images. +::: + +## Requirements + +- Gitea 28 or later. gt does not support older instances. +- Git on the `PATH`, for the commands that clone, check out or push, such as + `gt repo clone` and `gt pr checkout`. + +## Getting started + +1. [Install gt](installation.md). +2. [Log in](authentication.md) to your Gitea instance: + + ```bash + gt auth login + ``` + +3. Run commands from a clone of a repository on that instance. gt finds the repository + and the instance from the Git remotes: + + ```bash + gt issue list --label bug + gt pr checkout 13 + gt pr checks --watch + ``` + + Outside a clone, select the repository with `--repo` (`-R`), as `OWNER/REPO` or as a URL: + + ```bash + gt issue view 42 --repo gitea.com/gitea/cli + ``` + +`gt doctor` checks the setup that commands in the current directory use: the settings +files, the instance, the account and its credential, the repository, and Git. Each +problem it reports comes with the command that fixes it. + +## Commands + +Commands are a noun followed by a verb, such as `gt issue list` or `gt pr create`. +Every command has a `--help` page, which is also part of the +[command reference](reference/index.md). Besides the commands, gt has three help topics: + +- `gt help environment`: global flags, environment variables, and how gt selects the + instance and the repository +- `gt help formatting`: JSON output with `--json` and `--jq` +- `gt help exit-codes`: the exit codes + +For scripts and CI, read [Using gt in CI](ci.md) and [Output and scripting](scripting.md). diff --git a/cli-docs/installation.md b/cli-docs/installation.md new file mode 100644 index 00000000..fa2d7a6f --- /dev/null +++ b/cli-docs/installation.md @@ -0,0 +1,131 @@ +--- +sidebar_position: 2 +--- + +# Installation + +gt is a single binary, built for Linux, macOS, Windows, and FreeBSD. + +## Install script + +The install script downloads the binary for your system from +[dl.gitea.com](https://dl.gitea.com/gitea-cli/), verifies its SHA-256 checksum and installs +it as `gt`. + +On Linux, macOS or FreeBSD: + +```bash +curl -fsSL https://gitea.com/gitea/cli/raw/branch/main/scripts/install.sh | sh +``` + +It installs into `/usr/local/bin` when that directory is writable, else into `~/.local/bin`. + +On Windows, in PowerShell: + +```powershell +irm https://gitea.com/gitea/cli/raw/branch/main/scripts/install.ps1 | iex +``` + +It installs `gt.exe` into `%LOCALAPPDATA%\Programs\gitea-cli` and adds that directory to +your `PATH`. + +Both scripts read these environment variables: + +| Variable | Description | +| --- | --- | +| `GITEA_CLI_VERSION` | Version to install, such as `1.0.0`, or `nightly` for the latest build of the `main` branch. Default: the latest release. | +| `GITEA_CLI_INSTALL_DIR` | Directory to install gt into. | + +`install.sh` also accepts `--nightly` and `--version VERSION`: + +```bash +curl -fsSL https://gitea.com/gitea/cli/raw/branch/main/scripts/install.sh | sh -s -- --nightly +``` + +## Binaries + +Download releases from [dl.gitea.com/gitea-cli](https://dl.gitea.com/gitea-cli/). Builds of +the `main` branch are in its [`main`](https://dl.gitea.com/gitea-cli/main/) directory. Each +binary is available as is and compressed with xz, and has a matching `.sha256` checksum +file: + +```bash +VERSION=main +curl -sSLO "https://dl.gitea.com/gitea-cli/$VERSION/gitea-cli-$VERSION-linux-amd64" +curl -sSLO "https://dl.gitea.com/gitea-cli/$VERSION/gitea-cli-$VERSION-linux-amd64.sha256" +sha256sum -c "gitea-cli-$VERSION-linux-amd64.sha256" +sudo install -m 0755 "gitea-cli-$VERSION-linux-amd64" /usr/local/bin/gt +``` + +Each version also has a `gitea-cli--docs.tar.xz` archive with the command +reference, the man pages and the shell completion scripts. + +## Docker + +The [`gitea/gitea-cli`](https://hub.docker.com/r/gitea/gitea-cli) image runs `gt` as its +entry point. Releases are tagged with their version and `latest`, builds of the `main` +branch with `nightly`. + +```bash +docker run --rm -e GITEA_TOKEN -e GITEA_CLI_HOST=https://gitea.example.com \ + gitea/gitea-cli issue list --repo owner/repo +``` + +A container has no system keyring, so pass a token in the environment as above (see +[Using gt in CI](ci.md)). To keep the settings and a login between runs, mount a volume at +`/app`, the home directory of the image, and log in with `--credential-store file`. + +## Go + +With Go 1.27 or later: + +```bash +go install gitea.dev/cli/cmd/gt@latest +``` + +To build gt from a clone of [gitea/cli](https://gitea.com/gitea/cli), run `make`; it needs +GNU Make. + +## Shell completion + +`gt completion ` prints the completion script for bash, zsh, fish or PowerShell. +Completion includes your aliases and, after `--json`, the JSON fields of a command. Add +the line for your shell to its startup file: + +```bash +# ~/.bashrc (on macOS, ~/.bash_profile); needs the bash-completion package +eval "$(gt completion bash)" + +# ~/.zshrc, after compinit +source <(gt completion zsh) + +# fish, once +gt completion fish > ~/.config/fish/completions/gt.fish +``` + +In PowerShell, add this line to your profile: + +```powershell +gt completion powershell | Out-String | Invoke-Expression +``` + +`gt completion --help` shows the details for each shell. + +## Man pages + +`gt man` prints gt's full manual, and `gt man ` the man page of one command, such +as `gt man pr create`; `gt man` is not listed in `gt --help`. The man pages are also in the +docs archive of each version. + +```bash +# bash or zsh +man <(gt man) + +# write it to a file +gt man --output-file gt.1 +``` + +## Upgrading + +Run the install script again, download the new binary, or pull the new image. +`gt version` prints the version you run. diff --git a/cli-docs/scripting.md b/cli-docs/scripting.md new file mode 100644 index 00000000..ae940808 --- /dev/null +++ b/cli-docs/scripting.md @@ -0,0 +1,102 @@ +--- +sidebar_position: 5 +--- + +# Output and scripting + +gt prints text for people by default. Scripts should read JSON instead: the columns and +wording of the text output may change between releases. + +## JSON + +`--json` prints every field of the result, `--json FIELDS` only the comma-separated +fields you list: + +```bash +gt issue list --json +gt pr view 123 --json number,title,state +``` + +The **JSON FIELDS** section of a command's `--help` lists its fields, and shell completion +offers them after `--json`. An unknown field reports the available ones. Commands that +return no resource, such as deletes, reject `--json`. + +`--jq` (`-q`) filters the JSON with a [jq](https://jqlang.org) expression and implies +`--json`. It runs inside gt, so jq does not need to be installed. String results print +without quotes: + +```bash +gt issue list --json number,title --jq '.[] | "\(.number)\t\(.title)"' +``` + +Some fields cost extra requests, such as the reviewers of `gt pr list`. gt makes them only +when the output includes the field: `--json` names it, or `--json` selects every field and +the `--jq` expression names it. An expression that reaches such a field without naming it, +as `to_entries` does, sees it empty; name the field in `--json` instead. + +On a terminal, JSON is indented and colored; otherwise each value prints on one line. gt +does not prompt while it prints JSON; `--interactive` allows prompts anyway. +`gt help formatting` describes all of this. + +## Piped output + +JSON and other results go to stdout. Diagnostics, prompts and the messages that confirm a +change go to stderr, so they never end up in the data a script reads. + +Piped or redirected, lists print as tab-separated values without a header row, ready for +`cut` or `awk`: + +```bash +gt issue list --kind all --milestone v1.0 | cut -f1,2 +``` + +Color settings never color piped output. Only a command's own `--color always` does, such +as `gt pr diff --color always`. + +## 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, a prompt left with Esc or Ctrl-D, or a timeout | +| 4 | Authentication is missing or was rejected | +| 8 | `gt pr checks`: some checks are still pending | +| 130 | Interrupted with Ctrl-C, also at a prompt | + +A command may document further codes in its help. `gt auth status`, for example, exits +with 4 when there is no account or a credential is missing or rejected; with `--json` it +exits with 0 either way and reports the problem in its output. + +## The API + +`gt api` sends an authenticated request to the instance's API, for what no command covers +yet. The endpoint is a path under `/api/v1`, and `{owner}` and `{repo}` take the current +repository: + +```bash +gt api user --jq .login +gt api repos/{owner}/{repo}/issues -f title="Crash on start" -F labels='[3]' +gt api user/repos --paginate --jq '.[].full_name' +``` + +A response outside 2xx prints its body, then fails. `gt auth token` prints the token gt +uses, for tools that call the API themselves. + +## Aliases + +An alias is a shortcut that expands to a gt command with its arguments: + +```bash +# gt il runs gt issue list --label bug +gt alias set il "issue list --label bug" +# $1 takes the first argument: gt review 13 runs gt pr review 13 --approve +gt alias set review 'pr review $1 --approve' +``` + +`$1`, `$2` and so on take the arguments in that position; other arguments follow the +expansion, so `gt il --state all` runs `gt issue list --label bug --state all`. Aliases never +run a shell, and a built-in command always wins over an alias of the same name. +`--clobber` replaces an existing alias, `gt alias list` lists them and `gt alias delete` +removes one. They are kept in `config.yml`. diff --git a/cli-sidebars.js b/cli-sidebars.js new file mode 100644 index 00000000..47b419c6 --- /dev/null +++ b/cli-sidebars.js @@ -0,0 +1,21 @@ +// Sidebar of the gt (Gitea CLI) documentation (develop version). +// Released versions carry their own copy in +// cli-docs_versioned_sidebars/version--sidebars.json. +module.exports = { + cli: [ + { + type: 'doc', + id: 'index', + label: 'What is gt?', + }, + 'installation', + 'authentication', + 'ci', + 'scripting', + { + type: 'category', + label: 'Reference', + items: [{ type: 'autogenerated', dirName: 'reference' }], + }, + ], +};