.gitbay/wiki/Users.org

v1.16.0
gitbay/.gitbay/wiki/Users.org rendered · source · history · blame · raw

559 lines · 25417 bytes

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