v0.34.0 7c1886f

cli.md RawHistoryFolder

CLI

The ermite binary also drives a running server over its API. Run ermite <command> --help for the details of each command.

Connect to a server

Setting Environment Config file key
server address ERMITE_URL url
token (optional for public reads) ERMITE_TOKEN token.cmd, a command that prints it

The environment wins over the config file.

ermite config set url https://git.example.com
ermite config set token.cmd 'pass show ermite/token'
ermite config show [--json]   # path and values
ermite config unset <key>

The file holds no secret, only the command that fetches the token (pass, gopass show -o, op read, secret-tool lookup, …):

# ~/.config/ermite/config
url       = https://git.example.com
token.cmd = pass show ermite/token

Location: $XDG_CONFIG_HOME/ermite/config, ~/Library/Application Support/ermite/config on macOS, %AppData%\ermite\config on Windows. ERMITE_CONFIG names another file.

A token sent over plain http:// to a non-loopback host prints a warning.

How token.cmd runs

  • Through sh -c (cmd /C on Windows), at most once per run, only when a command needs the token.
  • It gets your terminal when stdin is one, so pinentry or biometric prompts work.
  • It must print the token alone on one line, under 4 KiB, within 60 seconds. On timeout or interrupt it is killed.
  • A failure is reported in one line, without the command's output.

It does not run:

  • when ERMITE_TOKEN is set;
  • for ermite version, ermite config or shell completion;
  • when ERMITE_URL points to another server than the file's url (the call is anonymous; set ERMITE_TOKEN);
  • when the file or its directory is writable by other users, or owned by another user (not checked on Windows).

Commands

ermite repo list | show <name> | create <name> [--description …] [--visibility public]
ermite repo edit <name> [--description …] [--visibility …] [--default-branch …] [--merge-modes …] [--allow-auto-merge] [--issues off|owner|public]
ermite repo delete <name> [--yes]

ermite token create <label> [--preset cli|agent|ci|runner] [--scope …] [--repo …] [--branch-prefix …] [--expires 30d]
ermite token list | revoke <id>

ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff]   # show prints the mergeability: up to date, behind, conflict
ermite mr comment <repo> <n> -m … [--path … --line N|A-B --side old|new] [--reply <id>] [--approve|--request-changes]
ermite mr resolve <repo> <n> <comment> | edit-comment <repo> <n> <comment> -m … | delete-comment <repo> <n> <comment>
ermite mr ready <repo> <n>                 # removes WIP:/[WIP], needs mr:write
ermite mr merge <repo> <n> --head <id> [--mode fast-forward|squash|merge] [--delete-branch] [--ignore-checks] | merge <repo> <n> --head <id> --auto [--mode …] | merge <repo> <n> --cancel-auto | close <repo> <n>

ermite issue create <repo> --title … [--body … | --body-file …] | list <repo> [--state open|closed|all]
ermite issue show <repo> <n> | comment <repo> <n> -m … | close <repo> <n> | reopen <repo> <n>
# issue show prints `closed by !N` when a merge closed it, and a `merge requests (K)` block (`!5 open  <title> · closes`); --json adds closed_by_merge_request and references

ermite run list <repo> [--workflow …] [--branch …] [--status …]
ermite run view <repo> <id> [--attempt n] [--log [--job …]] [--summary]
ermite run watch <repo> <id> [--log] | cancel <repo> <id> | rerun <repo> <id> [--failed]
ermite run artifacts <repo> <id> | download <repo> <id> [--name N]… [--dir D]
ermite workflow run <file> --repo <repo> [--ref …] [-f name=value …]
ermite workflow list <repo> [--json]
ermite workflow disable <repo> <file>    # no event starts a run of it; the file need not exist yet
ermite workflow enable <repo> <file>
ermite runner list | remove <id>
ermite secret set|list|delete …         # CI secrets (needs ERMITE_SECRETS_KEY)
ermite variable set|get|list|delete …
ermite environment create|list|delete … --repo <repo>

ermite mirror key show | rotate          # SSH key to add to the remote (rotate: owner only)
ermite mirror config [--enabled] [--url …{repo}…] [--cron …] [--all-branches] | config show   # global policy, owner only
ermite mirror add <repo> <url> [--cron …] [--all-branches] | remove <repo> | exclude <repo> | include <repo>
ermite mirror status [repo] | sync <repo>|--all [--force]   # sync --all: one row per repository, exit 1 if any failed

ermite web login                         # one-time sign-in link (60 s)
ermite web logout --all                  # sign every browser out
ermite web passkey list | remove <id>
ermite signing-key show                  # public key of squash and merge commits (server-signed)
ermite oidc rotate-key
ermite watch [repo] [--json]             # follow live events
ermite version                           # client and server versions
ermite completion bash|zsh|fish

Issues need a server with the issues feature and ermite repo edit <repo> --issues owner (or --issues public, which also opens a moderated form to visitors of a public repository); they are written with the issue:write scope. See Issues.

Mirrors push repositories to a remote over SSH, never forcing. See Push mirror.

Releases

Releases need a server advertising the releases feature. Published releases follow repository visibility; drafts are visible only to release writers. Writes need release:write (included in the cli token preset); branch-limited tokens cannot write releases. On an older server the preset drops this scope with a note, while an explicit --scope release:write fails before creating a token.

ermite release create <repo> <tag> [--title T] [--notes TEXT | --notes-file F] [--target REF] [--draft] [--prerelease] [--latest] [--asset FILE[#LABEL]]… [--json]
ermite release list <repo> [--limit N] [--json]
ermite release view <repo> <tag|id|latest> [--json | --notes]
ermite release edit <repo> <tag|id> [--title T] [--notes TEXT | --notes-file F] [--tag NEW] [--target REF] [--draft | --publish] [--prerelease | --no-prerelease] [--latest]
ermite release delete <repo> <tag|id> [--yes]
ermite release upload <repo> <tag|id> FILE[#LABEL]… [--clobber] [--content-type T]
ermite release download <repo> <tag|id|latest> [--pattern GLOB]… [--name N]… [--dir D]

All-digit selectors are release ids; other selectors are tags. latest selects the latest release for view/download. --latest pins a release as latest. Non-draft releases require an existing tag, or --target to create one; an existing tag is never moved. --notes-file - reads Markdown from standard input. Edit changes only supplied fields: --title='' and --notes='' clear them; --publish and --no-prerelease clear the corresponding flags.

Create uploads all --asset files, then prints the release web URL and id. Each asset's name is the file basename; an optional #LABEL sets its display label (quote it). Upload accepts multiple file arguments, including shell-expanded globs. For create, repeat --asset for each file. Content type is detected from the extension, or sniffed from the first bytes; upload's --content-type overrides it. A duplicate name fails with a hint to use --clobber. A failed upload after create leaves the release in place and reports its id.

List defaults to 30 entries and follows server pagination up to --limit. View shows fields, notes and an asset table; --notes prints Markdown alone. Create, list and view support stable JSON (fields are listed in their help). Delete removes the release and assets, not its Git tag; it asks for confirmation only on a terminal unless --yes is given.

Download defaults to all assets, into --dir (default .). Repeat --name or quote and repeat --pattern to select a union of names and globs. Files are never overwritten. One interrupted stream is resumed automatically from the bytes already received; size and SHA-256 digest are verified. Failed or mismatching downloads are removed. Resume is within the command, not across invocations.

Output

  • --json prints stable field names (listed in each --help) on repo list|show|create|edit, issue create|list|show|comment|close|reopen, token create|list, run list|view|cancel|rerun, runner list, mirror key|config|status and version.
  • watch --json prints one object per line. merge_request_mergeability events carry mergeability (state, behind, conflict_paths…) and print as repo!n mergeability: behind by 2.
  • A merge needs passing checks on the head (no failed, queued or running checks); a head with no checks can merge. mr merge --ignore-checks bypasses this, needs mr:force-merge, and cannot be combined with --auto or --cancel-auto. The cli preset includes mr:force-merge; agent does not. Adding --scope mr:merge lets an agent merge only with passing checks.
  • mr merge --mode squash|merge creates a commit signed by the server's own key (generated on first start); the output names its fingerprint, ermite signing-key show prints the key. Every mode is allowed by default; repo edit --merge-modes fast-forward,squash disables merge.
  • mr merge --auto makes the server merge once CI passed on the head (repo edit --allow-auto-merge first); --cancel-auto cancels it; both are exclusive with each other and --auto with --delete-branch. watch --json adds merge_request_auto_merge_enabled, merge_request_auto_merge_cancelled (with reason), and auto (true) on merge_request_merged after an auto-merge. See Merge requests.
  • Errors are one line on stderr (ermite: …) with exit status 1.
  • Shell completion completes repository names from the server.

ermite mr create and ermite mr close print a confirmation followed by the full merge request web URL on its own line. With --json, merge request objects (create, show, list, close and merge) include url alongside the other fields.

source
1# CLI
2
3The `ermite` binary also drives a running server over its API. Run `ermite <command> --help` for the details of each command.
4
5## Connect to a server
6
7| Setting | Environment | Config file key |
8|---|---|---|
9| server address | `ERMITE_URL` | `url` |
10| token (optional for public reads) | `ERMITE_TOKEN` | `token.cmd`, a command that prints it |
11
12The environment wins over the config file.
13
14```sh
15ermite config set url https://git.example.com
16ermite config set token.cmd 'pass show ermite/token'
17ermite config show [--json]   # path and values
18ermite config unset <key>
19```
20
21The file holds no secret, only the command that fetches the token (`pass`, `gopass show -o`, `op read`, `secret-tool lookup`, …):
22
23```ini
24# ~/.config/ermite/config
25url       = https://git.example.com
26token.cmd = pass show ermite/token
27```
28
29Location: `$XDG_CONFIG_HOME/ermite/config`, `~/Library/Application Support/ermite/config` on macOS, `%AppData%\ermite\config` on Windows. `ERMITE_CONFIG` names another file.
30
31A token sent over plain `http://` to a non-loopback host prints a warning.
32
33### How `token.cmd` runs
34
35- Through `sh -c` (`cmd /C` on Windows), at most once per run, only when a command needs the token.
36- It gets your terminal when stdin is one, so pinentry or biometric prompts work.
37- It must print the token alone on one line, under 4 KiB, within 60 seconds. On timeout or interrupt it is killed.
38- A failure is reported in one line, without the command's output.
39
40It does not run:
41
42- when `ERMITE_TOKEN` is set;
43- for `ermite version`, `ermite config` or shell completion;
44- when `ERMITE_URL` points to another server than the file's `url` (the call is anonymous; set `ERMITE_TOKEN`);
45- when the file or its directory is writable by other users, or owned by another user (not checked on Windows).
46
47## Commands
48
49```sh
50ermite repo list | show <name> | create <name> [--description …] [--visibility public]
51ermite repo edit <name> [--description …] [--visibility …] [--default-branch …] [--merge-modes …] [--allow-auto-merge] [--issues off|owner|public]
52ermite repo delete <name> [--yes]
53
54ermite token create <label> [--preset cli|agent|ci|runner] [--scope …] [--repo …] [--branch-prefix …] [--expires 30d]
55ermite token list | revoke <id>
56
57ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff]   # show prints the mergeability: up to date, behind, conflict
58ermite mr comment <repo> <n> -m … [--path … --line N|A-B --side old|new] [--reply <id>] [--approve|--request-changes]
59ermite mr resolve <repo> <n> <comment> | edit-comment <repo> <n> <comment> -m … | delete-comment <repo> <n> <comment>
60ermite mr ready <repo> <n>                 # removes WIP:/[WIP], needs mr:write
61ermite mr merge <repo> <n> --head <id> [--mode fast-forward|squash|merge] [--delete-branch] [--ignore-checks] | merge <repo> <n> --head <id> --auto [--mode …] | merge <repo> <n> --cancel-auto | close <repo> <n>
62
63ermite issue create <repo> --title … [--body … | --body-file …] | list <repo> [--state open|closed|all]
64ermite issue show <repo> <n> | comment <repo> <n> -m … | close <repo> <n> | reopen <repo> <n>
65# issue show prints `closed by !N` when a merge closed it, and a `merge requests (K)` block (`!5 open  <title> · closes`); --json adds closed_by_merge_request and references
66
67ermite run list <repo> [--workflow …] [--branch …] [--status …]
68ermite run view <repo> <id> [--attempt n] [--log [--job …]] [--summary]
69ermite run watch <repo> <id> [--log] | cancel <repo> <id> | rerun <repo> <id> [--failed]
70ermite run artifacts <repo> <id> | download <repo> <id> [--name N]… [--dir D]
71ermite workflow run <file> --repo <repo> [--ref …] [-f name=value …]
72ermite workflow list <repo> [--json]
73ermite workflow disable <repo> <file>    # no event starts a run of it; the file need not exist yet
74ermite workflow enable <repo> <file>
75ermite runner list | remove <id>
76ermite secret set|list|delete …         # CI secrets (needs ERMITE_SECRETS_KEY)
77ermite variable set|get|list|delete …
78ermite environment create|list|delete … --repo <repo>
79
80ermite mirror key show | rotate          # SSH key to add to the remote (rotate: owner only)
81ermite mirror config [--enabled] [--url …{repo}…] [--cron …] [--all-branches] | config show   # global policy, owner only
82ermite mirror add <repo> <url> [--cron …] [--all-branches] | remove <repo> | exclude <repo> | include <repo>
83ermite mirror status [repo] | sync <repo>|--all [--force]   # sync --all: one row per repository, exit 1 if any failed
84
85ermite web login                         # one-time sign-in link (60 s)
86ermite web logout --all                  # sign every browser out
87ermite web passkey list | remove <id>
88ermite signing-key show                  # public key of squash and merge commits (server-signed)
89ermite oidc rotate-key
90ermite watch [repo] [--json]             # follow live events
91ermite version                           # client and server versions
92ermite completion bash|zsh|fish
93```
94
95Issues need a server with the `issues` feature and `ermite repo edit <repo> --issues owner` (or `--issues public`, which also opens a moderated form to visitors of a public repository); they are written with the `issue:write` scope. See [Issues](issues.md).
96
97Mirrors push repositories to a remote over SSH, never forcing. See [Push mirror](mirror.md).
98
99## Releases
100
101Releases need a server advertising the `releases` feature. Published releases follow
102repository visibility; drafts are visible only to release writers. Writes need
103`release:write` (included in the `cli` token preset); branch-limited tokens cannot
104write releases. On an older server the preset drops this scope with a note, while
105an explicit `--scope release:write` fails before creating a token.
106
107```sh
108ermite release create <repo> <tag> [--title T] [--notes TEXT | --notes-file F] [--target REF] [--draft] [--prerelease] [--latest] [--asset FILE[#LABEL]]… [--json]
109ermite release list <repo> [--limit N] [--json]
110ermite release view <repo> <tag|id|latest> [--json | --notes]
111ermite release edit <repo> <tag|id> [--title T] [--notes TEXT | --notes-file F] [--tag NEW] [--target REF] [--draft | --publish] [--prerelease | --no-prerelease] [--latest]
112ermite release delete <repo> <tag|id> [--yes]
113ermite release upload <repo> <tag|id> FILE[#LABEL]… [--clobber] [--content-type T]
114ermite release download <repo> <tag|id|latest> [--pattern GLOB]… [--name N]… [--dir D]
115```
116
117All-digit selectors are release ids; other selectors are tags. `latest` selects
118the latest release for view/download. `--latest` pins a release as latest.
119Non-draft releases require an existing tag, or `--target` to create one; an
120existing tag is never moved. `--notes-file -` reads Markdown from standard input.
121Edit changes only supplied fields: `--title=''` and `--notes=''` clear them;
122`--publish` and `--no-prerelease` clear the corresponding flags.
123
124Create uploads all `--asset` files, then prints the release web URL and id.
125Each asset's name is the file basename; an optional `#LABEL` sets its display
126label (quote it). Upload accepts multiple file arguments, including shell-expanded
127globs. For create, repeat `--asset` for each file. Content type is detected from
128the extension, or sniffed from the first bytes; upload's `--content-type` overrides
129it. A duplicate name fails with a hint to use `--clobber`. A failed upload after
130create leaves the release in place and reports its id.
131
132List defaults to 30 entries and follows server pagination up to `--limit`.
133View shows fields, notes and an asset table; `--notes` prints Markdown alone.
134Create, list and view support stable JSON (fields are listed in their help).
135Delete removes the release and assets, not its Git tag; it asks for confirmation
136only on a terminal unless `--yes` is given.
137
138Download defaults to all assets, into `--dir` (default `.`). Repeat `--name` or
139quote and repeat `--pattern` to select a union of names and globs. Files are never
140overwritten. One interrupted stream is resumed automatically from the bytes
141already received; size and SHA-256 digest are verified. Failed or mismatching
142downloads are removed. Resume is within the command, not across invocations.
143
144## Output
145
146- `--json` prints stable field names (listed in each `--help`) on `repo list|show|create|edit`, `issue create|list|show|comment|close|reopen`, `token create|list`, `run list|view|cancel|rerun`, `runner list`, `mirror key|config|status` and `version`.
147- `watch --json` prints one object per line. `merge_request_mergeability` events carry `mergeability` (`state`, `behind`, `conflict_paths`…) and print as `repo!n mergeability: behind by 2`.
148- A merge needs passing checks on the head (no failed, queued or running checks); a head with no checks can merge. `mr merge --ignore-checks` bypasses this, needs `mr:force-merge`, and cannot be combined with `--auto` or `--cancel-auto`. The `cli` preset includes `mr:force-merge`; `agent` does not. Adding `--scope mr:merge` lets an agent merge only with passing checks.
149- `mr merge --mode squash|merge` creates a commit signed by the server's own key (generated on first start); the output names its fingerprint, `ermite signing-key show` prints the key. Every mode is allowed by default; `repo edit --merge-modes fast-forward,squash` disables `merge`.
150- `mr merge --auto` makes the server merge once CI passed on the head (`repo edit --allow-auto-merge` first); `--cancel-auto` cancels it; both are exclusive with each other and `--auto` with `--delete-branch`. `watch --json` adds `merge_request_auto_merge_enabled`, `merge_request_auto_merge_cancelled` (with `reason`), and `auto` (true) on `merge_request_merged` after an auto-merge. See [Merge requests](merge-requests.md#auto-merge).
151- Errors are one line on stderr (`ermite: …`) with exit status 1.
152- Shell completion completes repository names from the server.
153
154`ermite mr create` and `ermite mr close` print a confirmation followed by the full
155merge request web URL on its own line. With `--json`, merge request objects
156(create, show, list, close and merge) include `url` alongside the other fields.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 42

↑↓ moveenter openesc close