gg — command-line client
Speaks /api/mobile/v1, the same surface the Android app in ../android
uses. The two are kept in step deliberately: a capability that exists on
the phone exists here, and a field that changes shape breaks both at once
rather than drifting apart.
cargo build --release # target/release/gg
cargo test
Signing in
Mint a token on the web under Settings → API tokens with the Mobile app bundle ticked, then either paste the pairing URI the QR encodes:
gg login 'gitgud://pair?base=https://forge.example.com&token=ggp_…'
or give the two fields:
gg login --url https://forge.example.com # prompts for the token, unechoed
gg login --url https://forge.example.com --token-stdin < token.txt
Credentials land in ~/.config/gitgud/config.toml, mode 0600.
GITGUD_URL and GITGUD_TOKEN override the file; --url and --token
override both.
Which repository
Commands that need one take --repo owner/name. Without it, the git
remote of the current directory is used when it points at this forge —
so inside a checkout, gg ci list just works. gg login --default-repo owner/name sets a fallback for everywhere else.
Commands
gg whoami who the token belongs to, and its scopes
gg repos [--sort pushed|name|ci] repositories this token can reach
gg repo [owner/name] counts, permissions, latest run
gg ci list [--limit N] recent workflow runs
gg ci show [N] [--steps] one run: jobs, steps, artifacts
gg ci logs [N] [--job JOB] a job's logs, grouped by step
gg ci rerun N [--job JOB] re-queue a run, or one job of it
gg ci cancel N cancel a queued or running run
gg ci watch [N] poll until it settles; exits non-zero on failure
gg issues list [--state open|closed|all] [--search TEXT]
gg issue show N [--no-comments]
gg issue comment N --body TEXT (or --body-file PATH, or piped stdin)
gg issue close|reopen N
gg pulls list [--state open|closed|merged|all]
gg pull show N
gg pull comment N --body TEXT
gg pull review N --state approve|request-changes|comment [--body TEXT]
gg pull merge N [--message TEXT] [--override-rules]
gg wiki list | gg wiki show SLUG
gg packages list | gg package show FORMAT NAME | gg package usage FORMAT
gg dashboard [--refresh SECS] interactive CI dashboard
--json on any command prints the server’s response verbatim instead of
a table, which is the intended way to script against it.
Dashboard
gg dashboard opens runs, jobs and logs over one repository.
j/k, ↑/↓ move Enter open the run / the job's logs
Tab switch pane l logs for the selected job
r refresh now Esc back a level
R re-run C cancel
q quit
Write actions are capitalized so neither is a neighbour of a navigation
key — re-running someone else’s release because you reached for r is
not a recoverable mistake.
Packaging
CI builds a .deb and an .rpm on every push to master and publishes
both to this forge’s own registries — gg installs from the same place
the server package does:
# Debian / Ubuntu (trixie, forky)
sudo apt install gg
# Fedora / RHEL / openSUSE
sudo dnf install gg
The workflow prints the repository lines to add first; see the cli job
in .forgejo/workflows/build.yml.
Both packages come from metadata in Cargo.toml, and both build by
hand — a packaging bug should be reproducible without pushing a commit:
cargo deb # target/debian/gg_<version>_<arch>.deb
cargo generate-rpm # target/generate-rpm/gg-<version>-<release>.<arch>.rpm
cli/Dockerfile is what CI drives. It compiles the crate twice, once on
Debian trixie and once on EL9, because a Rust binary is linked against
the glibc of whatever built it — one binary packaged for both families
would install cleanly and then die with version GLIBC_2.41 not found.
It also runs cargo fmt --check, cargo clippy -D warnings and
cargo test on the way past, so a failing tree produces no package.
docker buildx build --target packages \
--output type=local,dest=./cli-dist --file cli/Dockerfile .
The build context is the repository root, not cli/: the copyright file
in both packages is the repo’s LICENSE.md.
Layout
| path | what |
|---|---|
src/cli.rs |
the clap command tree |
src/api/ |
HTTP client and the wire types, mirroring the server’s JSON module |
src/commands/ |
one module per noun, plus repo inference and body input |
src/output.rs |
colour, the CI status vocabulary, tables, relative times |
src/tui/ |
the dashboard |