Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Checkout

A browse root has to be on disk before gonk starts: gonk refuses to start on a root that names a missing directory, because that is the shape of a typo. On a new machine that means cloning every repository and writing one gonk.browse.root line for each. ikigai-gonk checkout does both, and keeps the clones current afterwards.

It is a command, like roborev file: it opens no store and binds no door, and the server itself has no code that fetches anything. So this page does not start a gonk at all.

Two repositories to check out

A real checkout clones from GitHub or another server. This page makes two repositories of its own to stand in for them, and clones them by file:// URL, which git treats like any other remote:

$ git init -q -b main upstream/notes
$ printf '# Notes\n' > upstream/notes/README.md
$ git -C upstream/notes add README.md
$ git -C upstream/notes -c user.name='A Reader' -c user.email=reader@example.com commit -qm 'Start the notes'
$ git init -q -b main upstream/specs
$ printf '# Specs\n' > upstream/specs/README.md
$ git -C upstream/specs add README.md
$ git -C upstream/specs -c user.name='A Reader' -c user.email=reader@example.com commit -qm 'Start the specs'

Clone, and write the roots

$ ikigai-gonk checkout --config-home "$PWD/.config/ikigai" --data-home "$PWD/.ikigai" "file://$PWD/upstream/notes" "file://$PWD/upstream/specs"
cloned     notes at …  …/.ikigai/checkouts/notes
cloned     specs at …  …/.ikigai/checkouts/specs

browse roots:
  gonk.browse.root = "notes=…/.ikigai/checkouts/notes"
  gonk.browse.root = "specs=…/.ikigai/checkouts/specs"

not written: add the lines above to …/.config/ikigai/config.toml, or run again with --write-config

Each URL is cloned into checkouts/<name> under the data home (~/.ikigai/checkouts/<name> on a machine that names no homes), where <name> is the URL’s last path segment without .git, and it is also the browse root’s name. name=url sets a different one (kata=git@github.com:kenn-io/kata.git), and --dir puts the clones somewhere else. --config-home and --data-home are the server’s two flags, so the checkout writes the config.toml the server will read and clones under the data home it will use; this page names both inside its own directory. A path under your home directory is printed and written with ~, and any other in full, which is why the lines above begin with …: where this page’s directory is depends on where you run it. (Before gonk PR 106, ledger #918, checkout had neither flag and read the process’s homes, so this page had to set HOME for it.)

Without --write-config the lines are only printed. With it, the missing ones are appended to the config home’s config.toml:

$ ikigai-gonk checkout --config-home "$PWD/.config/ikigai" --data-home "$PWD/.ikigai" "file://$PWD/upstream/notes" "file://$PWD/upstream/specs" --write-config
current    notes at …  …/.ikigai/checkouts/notes
current    specs at …  …/.ikigai/checkouts/specs

browse roots:
  gonk.browse.root = "notes=…/.ikigai/checkouts/notes"
  gonk.browse.root = "specs=…/.ikigai/checkouts/specs"

…/.config/ikigai/config.toml:
  added      gonk.browse.root = "notes=…/.ikigai/checkouts/notes"
  added      gonk.browse.root = "specs=…/.ikigai/checkouts/specs"

gonk reads gonk.browse.root at startup only: restart gonk to serve the new root(s).
$ cat .config/ikigai/config.toml
gonk.browse.root = "notes=…/.ikigai/checkouts/notes"
gonk.browse.root = "specs=…/.ikigai/checkouts/specs"

The second run found both clones already there and current, so it cloned nothing, and only wrote the config. The rules for that file are conservative: when it exists, it is copied beside itself before anything is appended (a backup line names the copy), no other line is touched, a root already configured for the same directory is left alone, and a name already configured for a different directory is a conflict that writes nothing for that name.

Keeping them current

Upstream moves on:

$ printf 'A second line.\n' >> upstream/notes/README.md
$ git -C upstream/notes -c user.name='A Reader' -c user.email=reader@example.com commit -qam 'A second line'

Run the same command again, and each clone is fetched and its default branch fast-forwarded:

$ ikigai-gonk checkout --config-home "$PWD/.config/ikigai" --data-home "$PWD/.ikigai" "file://$PWD/upstream/notes" "file://$PWD/upstream/specs"
updated    notes …..… (1 commit(s), fast-forward)  …/.ikigai/checkouts/notes
current    specs at …  …/.ikigai/checkouts/specs

browse roots:
  gonk.browse.root = "notes=…/.ikigai/checkouts/notes"
  gonk.browse.root = "specs=…/.ikigai/checkouts/specs"

not written: add the lines above to …/.config/ikigai/config.toml, or run again with --write-config

A root gonk already serves needs no restart after an update: gonk watches it, and a fast-forward is a change on disk like any other (the watcher is shown working in Browse roots). Only a NEW root needs the restart the first run asked for.

Every checkout, without the URLs

Repeating the URL list is fine once and tiresome on a timer. --all updates every checkout already in the managed directory, each from its own origin and under its own directory name. To see what else it reports, check out a third repository and do not write its root:

$ git init -q -b main upstream/tools
$ printf '# Tools\n' > upstream/tools/README.md
$ git -C upstream/tools add README.md
$ git -C upstream/tools -c user.name='A Reader' -c user.email=reader@example.com commit -qm 'Start the tools'
$ ikigai-gonk checkout --config-home "$PWD/.config/ikigai" --data-home "$PWD/.ikigai" "file://$PWD/upstream/tools"
cloned     tools at …  …/.ikigai/checkouts/tools

browse roots:
  gonk.browse.root = "tools=…/.ikigai/checkouts/tools"

not written: add the lines above to …/.config/ikigai/config.toml, or run again with --write-config
$ printf 'A third line.\n' >> upstream/notes/README.md
$ git -C upstream/notes -c user.name='A Reader' -c user.email=reader@example.com commit -qam 'A third line'
$ ikigai-gonk checkout --config-home "$PWD/.config/ikigai" --data-home "$PWD/.ikigai" --all
updated    notes …..… (1 commit(s), fast-forward)  …/.ikigai/checkouts/notes
current    specs at …  …/.ikigai/checkouts/specs
current    tools at …  …/.ikigai/checkouts/tools

coverage of …/.ikigai/checkouts by the roots in …/.config/ikigai/config.toml:
  unused     tools  …/.ikigai/checkouts/tools  no gonk.browse.root points into it: gonk does not browse it (`checkout <url> --write-config` adds its line)

Each checkout gets its line, in the same words as before, and then a coverage report that changes nothing: unused for a checkout no gonk.browse.root points into (gonk does not browse it, so it is fetched for nothing), and missing for a root that points into the managed directory at nothing (gonk will refuse to start until it exists). Coverage is information; it does not change the exit status.

What --all never does is as deliberate: it clones nothing, writes no config (--all --write-config is refused), and takes no URLs. And a bare checkout with no URLs is an error that names --all, rather than quietly being it, because a command that fetches every repository should say so on its own line.

And nothing else

A fast-forward is all it does. A checkout with local changes (untracked files included), on another branch, or with commits upstream does not have is refused and left exactly as it was: nothing is reset, stashed, merged or discarded.

$ printf 'work in progress\n' > .ikigai/checkouts/specs/wip.txt
$ ikigai-gonk checkout --config-home "$PWD/.config/ikigai" --data-home "$PWD/.ikigai" --all
current    notes at …  …/.ikigai/checkouts/notes
REFUSED    specs  …/.ikigai/checkouts/specs
  it has local changes (1 path(s); `git -C …/.ikigai/checkouts/specs status`). A managed checkout is fast-forwarded only; nothing was fetched or changed
current    tools at …  …/.ikigai/checkouts/tools

coverage of …/.ikigai/checkouts by the roots in …/.config/ikigai/config.toml:
  unused     tools  …/.ikigai/checkouts/tools  no gonk.browse.root points into it: gonk does not browse it (`checkout <url> --write-config` adds its line)
$ cat .ikigai/checkouts/specs/wip.txt
work in progress

Each checkout is reported on its own and the rest still run. Any refusal or failure makes the exit status 1, so a scheduled run that something has stopped is visible to whatever ran it, and a refusal names the command that shows what is in the way.

git runs with its standard input closed and GIT_TERMINAL_PROMPT=0, and ssh in BatchMode unless you have already said how to run ssh, so a repository that wants a password fails with git’s own message instead of waiting for one. Credential helpers and ssh agents work as usual.

⚠ A managed clone shows its default branch. Work happening on other branches or in other worktrees (an agent loop’s worktrees, say) is not what gonk browses there; point a root at that worktree by hand if that is what you want to read.

On a timer

Nothing in the server fetches, so a periodic update is checkout --all run by the system’s scheduler, and gonk needs no restart for it. On macOS, a launchd agent beside gonk’s own (~/Library/LaunchAgents/dev.ikigai-rs.gonk-checkout.plist), every fifteen minutes:

<plist version="1.0"><dict>
  <key>Label</key><string>dev.ikigai-rs.gonk-checkout</string>
  <key>ProgramArguments</key><array>
    <string>/bin/sh</string>
    <string>-c</string>
    <string>date; exec /Users/you/.cargo/bin/ikigai-gonk checkout --all</string>
  </array>
  <key>StartInterval</key><integer>900</integer>
  <key>StandardOutPath</key><string>/tmp/ikigai-gonk-checkout.log</string>
  <key>StandardErrorPath</key><string>/tmp/ikigai-gonk-checkout.log</string>
</dict></plist>
$ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.ikigai-rs.gonk-checkout.plist

On Linux, the same as a crontab line (crontab -e):

*/15 * * * * (date; $HOME/.cargo/bin/ikigai-gonk checkout --all) >> /tmp/ikigai-gonk-checkout.log 2>&1

The date is there because the command prints no time of its own and neither scheduler adds one. Under either, git runs without a terminal: an HTTPS remote needs a credential helper and an SSH remote a key the agent already holds, or that checkout is a FAILED line with git’s own message rather than a hang.