.gitbay/wiki/Users.org
512 lines · 22464 bytes
1#+title: gitbay user guide
2
3Everything here works from stock OpenSSH — replace =gitbay= with
4=ssh git@<host>= in any command and it behaves identically. The CLI adds
5convenience (instance profiles, repo inference, =$EDITOR=), nothing more.
6=ssh git@<host> help= lists every command the server knows.
7
8* Installing the CLI
9
10#+begin_src sh
11go install gitbay.org/gitbay/cmd/gitbay@latest # any platform with Go
12
13brew tap krz/tap https://gitbay.org/krz/homebrew-tap.git
14brew install krz/tap/gitbay # Homebrew (macOS/Linux)
15#+end_src
16
17Or build from source: =go build ./cmd/gitbay= in a clone of
18=https://gitbay.org/krz/gitbay.git=.
19
20* Getting an account
21
22How you join depends on the instance's registration mode. On instances
23with web accounts enabled, =/register= offers the same signup as a
24browser form (paste your SSH public key); everything below works from
25the terminal alone:
26
27- closed :: an admin creates your account on the host and registers your
28 first SSH key. Nothing for you to do but hand over your public key.
29- invite :: you receive a single-use code by email. With the SSH key you
30 want to use:
31 #+begin_src sh
32 ssh git@<host> register --username you --invite <code>
33 #+end_src
34 Your account is active immediately; the invited address is your
35 verified email.
36- open ::
37 #+begin_src sh
38 ssh git@<host> register --username you --email you@example.org
39 #+end_src
40 A verification code arrives by mail. Until you run
41 =ssh git@<host> email verify <code>=, the account is pending: you can
42 run =whoami= and the email commands, and nothing else — no git, no
43 repos.
44
45* SSH keys
46
47Your key is your identity; there are no passwords anywhere. The SSH
48username is always =git= — the key alone determines who you are.
49
50#+begin_src sh
51gitbay auth keys list
52gitbay auth keys add --scope git < ~/.ssh/ci_key.pub # key on stdin
53gitbay auth keys remove SHA256:...
54#+end_src
55
56Scopes: =full= (default; git plus every control command), =git= (git
57transport only — right for automation keys, which then cannot touch
58issues, settings, or your account), or =runner= (the CI runner's
59protocol plus read-only git, for the key a =gitbay-runner= host holds;
60see [[Admin]]).
61
62A key belongs to exactly one account instance-wide. Registering a key
63someone else already holds is refused without telling you whose it is.
64
65* Verified commits
66
67The commit badge is driven by the *author* email and the signing key:
68=verified= means the signature is valid, the key is registered to an
69account, and the author email is a verified address on that account.
70
71For OpenPGP signing (git's default):
72#+begin_src sh
73gpg --armor --export you@example.org | gitbay auth pgp add
74#+end_src
75
76For SSH signing (=git config gpg.format ssh=): sign with any key
77registered on your account; your verified addresses act as the principal
78set. No separate registration step.
79
80Add and verify additional addresses with =email add <address>= /
81=email verify <code>= (requires the instance to have SMTP; otherwise an
82admin can assert an address for you). =email list= shows them. =email
83remove <address>= drops one, with two refusals: the primary stays until
84=email primary <address>= names another verified address, and the last
85verified address stays, since activation, login links and commit
86identity all resolve through verified addresses. Removing a verified
87address re-evaluates signature states, so a commit authored from it
88shows as =signed_email_mismatch= afterwards. A removed address is free
89for any account to claim.
90
91The states you will see, in decreasing order of trust: =verified=,
92=signed_unknown_key= (valid signature, key not registered here — register
93it and history upgrades retroactively), =signed_email_mismatch= (real
94key, author line claims someone else), =signed_key_expired= /
95=signed_key_revoked=, =bad_signature=, =unsigned=. Server-created commits
96(web edits, merge commits) are always =unsigned= — the server holds no
97signing key on principle.
98
99=repo bookmark <owner/name>= saves a repository to come back to and
100=repo unbookmark= drops it; =repo bookmarks= lists yours, each with how
101many people have bookmarked it. The web has the control on the
102repository header, the count in the facts bar, and the list at
103=/bookmarks=. Bookmarks are public and counted; pins are private and
104drive the rail. Read access is all a bookmark needs.
105
106A job's result is a property of the commit's *tree*, not its sha: a
107rebase onto a base that touched nothing the branch did gives every
108commit a new sha and the same tree, and a job that already passed for
109that tree is not run again — the new commit gets the earlier result as
110its =ci/<job>= status, naming the build it came from. Scheduled and tag
111jobs are never reused this way; their trigger is the clock or the tag.
112
113* Profiles
114
115A profile is what =/{owner}= shows: a one-line description, a website, a
116set of links, long-form about text, the repositories you can see, org
117membership, and a year of activity. Users and orgs have the same fields.
118Repository rows carry the listing metadata too — topics, license,
119default branch, last commit — so a client renders a profile listing the
120way =/{owner}= does. The web dispatches this command rather than
121assembling the page itself.
122
123#+begin_src sh
124gitbay profile show # your own
125gitbay profile show alice
126gitbay profile set --description "builds small tools" --website https://alice.example
127#+end_src
128
129About text is markdown by default, or org-mode. It takes inline text or
130stdin, so it can live in a file you keep:
131
132#+begin_src sh
133gitbay profile set --about "I maintain a few small tools."
134gitbay profile set --file - --about-format org < about.org
135#+end_src
136
137Up to five links, each =label|url= or a bare url, http(s) only. Passing
138=--link= replaces the whole set; a single empty one clears it:
139
140#+begin_src sh
141gitbay profile set --link "Mastodon|https://fosstodon.example/@alice" \
142 --link https://alice.example/now
143gitbay profile set --link "" # clear
144#+end_src
145
146Every field follows the same rule as the rest of the CLI: a flag you
147leave out is untouched, and ='' clears the one you name. Org profiles
148work the same way and need org admin:
149
150#+begin_src sh
151gitbay org profile krz --description "software and experiments" --about-format org --file - < krz.org
152gitbay org profile krz # no flags shows it
153#+end_src
154
155The web renders profiles but has no form for editing one, so the CLI is
156the only interface today. =profile set= is not =SSHOnly=, so the JSON API
157runs it like any other write command.
158
159* Repositories
160
161#+begin_src sh
162gitbay repo create you/project [--private]
163gitbay repo clone you/project
164gitbay repo list
165gitbay repo show you/project
166gitbay repo log you/project --limit 20 # commits with signature states
167gitbay repo fork other/project [--name mine]
168gitbay repo delete you/project --yes
169#+end_src
170
171=repo show= also reports your own state on the repository — =watch=
172(=watching= or =muted=; =repo unwatch= clears either) and =bookmarked= —
173and =fork_of= when it is a
174fork whose parent you can read, so a client draws a toggle rather than
175two blind buttons. Absent means none.
176
177Pushing is SSH-only. Public repositories are anonymously readable over
178HTTPS (and =git://= where enabled); private repositories exist only over
179SSH and answer "not found" to everyone without access.
180
181Access and settings (owner or =admin= grant):
182#+begin_src sh
183gitbay repo access grant you/project alice write # read | write | admin
184gitbay repo access revoke you/project alice
185gitbay repo settings protect you/project main # no force-push, no delete
186gitbay repo settings require-signed you/project on # every commit must verify
187gitbay repo settings git-daemon you/project on # expose over git://
188gitbay repo topics add you/project cli forge # free-form tags, shown on the web
189gitbay repo search forge # find repos by name/description/topic
190gitbay repo grep you/project "some string" # literal git grep over the default branch
191gitbay repo pin you/project # pin to your web dashboard
192gitbay repo unpin you/project
193gitbay repo archive you/project # read-only: pushes and issue/MR
194gitbay repo unarchive you/project # writes refused, browsing intact
195#+end_src
196
197Import from another forge — git data first, then optionally the GitHub
198issue and PR history (issues keep state/labels/comments; PRs land as
199closed or merged MRs with their discussion; originals are attributed
200inline since foreign authors have no local account; re-running resumes
201where it stopped):
202#+begin_src sh
203gitbay repo import you/mirror --from https://github.com/you/repo.git \
204 [--private] [--token-stdin] # token on stdin, never in the URL
205gitbay repo import-issues you/mirror --from you/repo --token-stdin
206#+end_src
207
208Moving between gitbay instances (no lock-in): run on the TARGET, with
209your key registered on both sides. Profile, repos with settings,
210issues, MRs, and comments replay with attribution; git data mirrors
211client-side through your own key. Keys never transfer and emails
212arrive unverified — trust is per-instance. Re-running resumes.
213Push-blocking policies (require-signed, protected branches) are
214deferred and printed for you to re-apply after the data lands.
215=gitbay auth export= alone doubles as a user-level backup.
216
217#+begin_src sh
218gitbay migrate --from old-instance.example [--from-port 22]
219#+end_src
220
221Mirroring keeps a foreign remote in sync during a gradual migration
222(repo admin; https remotes; the token is stored server-side for the
223recurring sync and never echoed back):
224
225#+begin_src sh
226gitbay repo mirror add you/project https://github.com/you/project.git \
227 --direction push --token-stdin # propagate after every local push
228gitbay repo mirror add you/copy https://github.com/them/theirs.git \
229 --direction pull # follow upstream; local pushes refused
230gitbay repo mirror list # sync status and last error, per mirror
231gitbay repo mirror sync / remove <id>
232#+end_src
233
234Markdown and org files render on the web when opened, the way a README
235does on the repository page, with relative links resolved against the
236file's directory; =source= in the file's action bar (or =?view=source=)
237shows the text instead. Other files show the text with highlighting.
238
239* Organizations
240
241Orgs share the owner namespace with users and own repositories at
242=org/repo=. By default members get write on all org repos; org admins
243get repo admin, create repos under the org, and manage membership.
244
245#+begin_src sh
246gitbay org create krz
247gitbay org members add krz alice [--role admin]
248gitbay org show krz
249gitbay org rename krz newname # clone URLs change
250gitbay org delete krz --yes # only when it owns no repositories
251#+end_src
252
253Large orgs scope access with teams: set what plain membership implies,
254then grant per-repo roles through named teams (org admins always keep
255admin; the default =write= keeps the simple model):
256
257#+begin_src sh
258gitbay org settings members-role krz none # write | read | none
259gitbay org team create krz core-devs
260gitbay org team add krz core-devs alice bob # org members only
261gitbay org team grant krz core-devs krz/gitbay write
262gitbay org team show krz core-devs # members + grants
263gitbay org team revoke / remove / delete ...
264#+end_src
265
266* Issues
267
268Anyone who can read a repository can file and comment. Closing/reopening
269is for the author or anyone with write; labels and assignees need write.
270
271#+begin_src sh
272gitbay issue create --title "it breaks" [--body "..." | --file -]
273gitbay issue list [--state open|closed|all]
274gitbay issue show 4
275gitbay issue comment 4 --message "same here"
276gitbay issue edit 4 --title "better title" [--body|--file -] # author or write
277gitbay issue close 4 / reopen 4
278gitbay issue label 4 --add bug --remove wontfix
279gitbay issue assign 4 --add alice
280gitbay issue milestone 4 v1.0 # or "none" to clear
281#+end_src
282
283Inside a clone, the repository is inferred from the =origin= remote —
284that is why no =owner/name= appears above. Anywhere else, pass it as the
285first argument. Long text: =--body= inline, =--file -= from stdin, or
286neither on a terminal and =$EDITOR= opens.
287
288Wikis are companion repositories edited by push — no separate storage,
289no web editor, the same rendering pipeline as READMEs (markdown and
290org). Access mirrors the parent repo (read to view, write to push);
291the companion is created on your first push and follows the repo
292through transfer and delete:
293
294#+begin_src sh
295git clone ssh://git@<host>/you/project.wiki.git
296# add Home.md (or .org), more pages, images; relative links between
297# pages just work on the web at /you/project/wiki
298git push
299#+end_src
300
301Releases anchor notes and binary assets to a pushed tag (write access;
302assets stream over SSH, capped by the instance's =max_asset_bytes=):
303
304#+begin_src sh
305gitbay release create v1.0 --title "First light" [--notes|--file -|$EDITOR]
306gitbay release asset add v1.0 tool-linux-amd64 < dist/tool-linux-amd64
307gitbay release asset get v1.0 tool-linux-amd64 > tool # or the web download link
308gitbay release list / show v1.0 / delete v1.0 --yes
309#+end_src
310
311The web shows them under the repository's =releases= tab with rendered
312notes, sha256 sums, and download links.
313
314Commit messages act on issues when the commits land on the default
315branch (direct push or MR merge): =closes/fixes/resolves #4= closes the
316issue with a linking comment, and a bare =#4= leaves a reference
317comment. Each issue/commit pair acts once, ever. Same repository only.
318
319Milestones group issues and MRs toward a release (write access to
320manage, attach with =issue milestone= / =mr milestone=; progress shows
321on the web at =/owner/name/milestones=):
322
323#+begin_src sh
324gitbay milestone create v1.0 --description "first release" --due 2027-01-01
325gitbay milestone list [--state open|closed|all]
326gitbay milestone close v1.0 / reopen v1.0
327#+end_src
328
329Issue templates: commit =.gitbay/issue-template.md= (and optional
330=issue-template-<name>.md= variants) to the default branch. =gitbay
331issue create= prefills =$EDITOR= with the default template, the web
332form prefills its textarea, and =gitbay issue templates= lists them.
333
334Lists narrow the same way on every surface: =issue list --label bug
335--assignee bob --author alice --milestone v1= (or =--milestone none=),
336=mr list --author bob --milestone v1=; the web's issue and merge request
337lists take the same names as query parameters, and each active filter
338shows with a link that drops it.
339
340Labels take a colour: =gitbay label set bug --color cf222e=; =label
341list= shows each with its colour and how many issues carry it, and
342=label remove= takes one off every issue. =issue label --add= still
343creates a colourless label on the fly.
344
345* Merge requests
346
347#+begin_src sh
348gitbay mr create --source feature --target main --title "add thing"
349gitbay mr create other/upstream --source you/fork:feature --target main --title "..."
350gitbay mr list / show 4 / diff 4
351gitbay mr checkout 4 # local branch mr/4 from the MR head
352gitbay mr review 4 --approve # or --request-changes / --comment
353gitbay mr merge 4 [--strategy ff|merge|squash|rebase]
354gitbay mr close 4
355#+end_src
356
357Semantics worth knowing:
358
359- the MR head lives in the *target* repository as
360 =refs/merge-requests/N/head= (fetchable by any reader), so an MR
361 survives deletion of its source branch or fork.
362- force-pushing the source updates the MR and marks existing reviews
363 stale.
364- default strategy: fast-forward when possible, else a merge commit.
365 Squash makes one commit authored by the MR author, committed by the
366 merger. Rebase replays a linear range preserving authors; it refuses
367 ranges containing merge commits, and when fast-forward is possible it
368 *is* one (original commits and signatures land untouched).
369- on =require_signed_commits= branches only fast-forwards of fully
370 verified commits merge; everything server-created is refused with
371 instructions to rebase locally. =gitbay mr rebase <n>= is those
372 instructions: in a clone of the target it replays the source branch
373 onto the target and force-pushes it, then =mr merge <n>= fast-forwards.
374 The replay is local, so the commits carry your signature and not the
375 server's — it holds no key. A branch living in a fork is refused, with
376 the repository to run it in; rebase it there.
377
378Repo admins can gate merges (=repo settings ...=): =require-approvals
379<n>= (fresh, non-author approvals; each reviewer's latest review is
380their stance, and a fresh request-changes blocks), =require-resolved=
381(no open review threads), =require-checks= (all statuses green), and
382=require-codeowners= (an approval from an owner of every owned changed
383file). Owners come from a =CODEOWNERS= file on the target branch, root
384or =.gitbay/=, gitignore-style patterns, last match wins. The toggle is
385the opt-in, so a repository can carry the file as documentation of who
386to ask without it gating merges; with it on and no file on the target
387branch, the merge is refused and says so. It does not wait on
388=require-approvals=.
389
390A merge request whose target is another open merge request's source
391branch is stacked on it: =mr create= says so, =mr show= carries
392=stacked_on= and =stacked=, and merging the lower one retargets the
393upper onto =main= with its reviews kept. See [[Stacked-MRs][Stacked
394merge requests]].
395
396Review threads anchor to diff lines:
397
398#+begin_src sh
399gitbay mr diff-comment 4 --path main.go --line 12 --message "use log here"
400gitbay mr diff-comment 4 --reply 7 --message "done" # join thread 7
401gitbay mr threads 4 # threads with staleness
402gitbay mr resolve 4 7 / unresolve 4 7
403#+end_src
404
405Threads render inline on the MR page. A force-push marks them stale
406(shown under "threads on earlier revisions") rather than guessing new
407anchors; =mr show= reports the unresolved count. Resolving is for the
408thread author, the MR author, or anyone with write.
409
410* CI builds
411
412A =.gitbay/ci.yml= in the repo runs jobs on every branch push:
413
414#+begin_src yaml
415jobs:
416 test:
417 steps:
418 - go test ./...
419#+end_src
420
421Each job becomes a build (=build list=, =build log=, the builds tab on
422the web) and a =ci/<job>= commit status, which =repo settings
423require-checks= can gate merges on. Steps run with =sh -c= on the
424instance's runner, stopping at the first failure; a broken config
425surfaces as a failed =ci/config= status. Environment: =GITBAY_REPO=,
426=GITBAY_SHA=, =GITBAY_REF=, =GITBAY_JOB=, =CI=true=.
427
428Secrets: =repo secret set <owner/name> <NAME>= reads the value from
429stdin (never argv) and injects it into the repo's builds as =$NAME=;
430=repo secret list= shows names only, and the value is never echoed
431back. Anyone with write access can read a secret from inside a build,
432so scope them accordingly.
433
434Schedules: a job with =schedule: "17 11,23 * * *"= (five-field cron,
435server-local time; lists, ranges, and steps supported) runs on its cron
436against the default branch instead of on push. A default-branch push
437registers or updates the schedule. A job with =tags: "v*"= runs when a
438matching tag is pushed — and only then; =schedule= and =tags= are
439mutually exclusive. =build trigger <owner/name> <job>= queues any job
440immediately, and =build cancel <owner/name> <n>= withdraws one, queued
441or running: a running build stops at the runner within seconds and the
442log says who cancelled it. Both need write access.
443
444* Large files (LFS)
445
446Standard Git LFS works over both transports with no setup beyond the
447usual =git lfs track=. SSH remotes authenticate through
448=git-lfs-authenticate= (deploy keys included: ro keys can download, rw
449keys upload); anonymous HTTPS clones of public repositories can fetch
450LFS objects with no credentials. Objects are verified against their
451sha256 on upload and capped at 512MB by default.
452
453* Pages
454
455On instances with =[pages] domain= set, a =pages= branch in any public
456repo is served as a static site: the repo named =pages= at
457=https://<you>.<domain>/=, every other repo at
458=https://<you>.<domain>/<repo>/=. Push HTML to publish; a CI job can
459build and push the branch for automatic deploys. Sites run on a
460separate origin — your scripts work, and the forge's cookies are out of
461reach.
462
463A repo can also serve its pages branch on a domain you own.
464=repo domain add <owner/name> <domain>= claims it and prints a DNS TXT
465challenge (=_gitbay-challenge.<domain>=); create the record, run
466=repo domain verify=, then point the domain's A/AAAA records at the
467instance (DNS-only if the domain sits behind a proxying provider — the
468instance issues its own certificates). Claims are exclusive per
469instance; unverified claims serve nothing and expire after 7 days.
470=repo domain list= reports pending/verified/expired.
471
472* Browser sessions
473
474=gitbay web login= mints a one-time URL; the session it opens lasts
475seven days. =gitbay web sessions list= shows each of yours by a short
476id with its creation and expiry, and =gitbay web sessions revoke <id>=
477or =--all= ends them from the terminal, which is where a lost laptop is
478handled.
479
480* Notifications
481
482When the instance has SMTP configured, activity mails you: someone
483opens an issue or MR on your repository, comments where you are a
484participant (author, commenter, reviewer), reviews, closes, or merges.
485You are never mailed about your own actions, and only verified primary
486addresses receive anything. Delivery retries on relay failure.
487
488* Scripting
489
490Every read command takes =--json= and emits one envelope:
491={"protocol_version": 1, "data": ...}=. stdout is data, stderr is
492messages. Exit codes are stable: 0 ok, 1 failure, 2 usage, 3 not found,
4934 denied, 5 server/protocol error. Nothing ever prompts; destructive
494commands take =--yes=.
495
496For HTTP automation see [[API]].
497
498* CLI setup
499
500#+begin_src sh
501gitbay remote add myforge forge.example.org [--port n] [--user u] --default
502gitbay remote list
503gitbay init [name] [--private] # git init + repo create + origin, in one step
504#+end_src
505
506Configuration lives at =~/.config/gitbay/config.toml=. The CLI shells
507out to your real =ssh=, so =~/.ssh/config=, the agent, and hardware keys
508all apply. It shares one connection per instance through a control
509socket under =~/.ssh=: the first command in five minutes pays the
510handshake and the rest ride it. =no_multiplex = true= on an instance in
511the config turns that off. Man pages: =gitbay man --dir <dir>=; completions:
512=gitbay completion bash|zsh|fish=.