Contributing to grebe #
Issues and pull requests go to the Tangled mirror.
Setting up #
Everything needed is in the Nix development shell: Zig 0.17.0, git, the
Vulkan loader and validation layer, reuse, and zon2nix.
$ nix develop
$ zig build test --summary all
$ zig build run -- --all
zig build check compiles everything without running anything.
GREBE_SNAPSHOT=FILE grebe reads the history, draws one frame on the CPU
into FILE as a PPM, and quits: a way to look at what grebe draws without a
screenshot, and what tests that compare pictures are built on.
Before sending a change #
Each of these is checked by the workflow on every push:
reuse lintpasses.zig fmt --check --exclude zig-pkg .passes.zig build testpasses. The tests that read history build small repositories with git, under.zig-cache/tmp, with nobody's configuration; they skip where there is no git. It runs every test twice, the second time withGREBE_TEST_OBJECT_FORMAT=sha256, so that the repositories have SHA-256 object IDs rather than SHA-1's.nix buildsucceeds, which runs the tests again in the sandbox.nix build .#checks.x86_64-linux.vmpasses: grebe driven in a virtual machine (seetests/nixos/grebe.nixbelow).zig build -Dtarget=x86_64-windows-gnucompiles, andnix build .#msimakes the installer, twice, bit for bit the same.
Windows #
grebe.exe is cross-compiled; nothing here runs on Windows. Code for one
platform goes behind builtin.os.tag, and the Win32 API comes from
zigwin32, imported as win32 only for a Windows build. What differs is
small and kept where it differs:
src/history/git/Child.zigreads git's pipes. On Linux each is a file descriptor set not to block; on Windowsstd.process.spawnopens them for overlapped reads, and each keeps one read outstanding, whose event is what the loop waits on.src/Clock.zigtakes Windows' own time zone, from the IANA database built into the binary (-Dembed-tzdata, whichbuild.zigasks of zig-datetime for Windows, and gives fluent too, so that the two are one module).- zig-aerie's
desktop.Watcheris the registry's on Windows rather than the portal's.
The tests can run under Wine, though the ones that make repositories skip
unless a Windows git is on Wine's PATH (WINEPATH):
$ zig build test -Dtarget=x86_64-windows-gnu -fwine
A release is a tag v and the version in build.zig.zon. Pushing one runs
.forgejo/workflows/release.yaml, which builds the MSI, publishes it with
its SHA-256 on the release, and submits the version to the JCOllie winget
source as a pull request against jeff/winget-pkgs (JCOllie.Grebe), to
be merged there by hand. If that submission did not happen, run
.forgejo/workflows/winget.yaml by hand with the version and the MSI's
ProductCode, which msiinfo export grebe-X-x64.msi Property reads out of
the published installer.
A change to build.zig.zon regenerates build.zig.zon.nix in the same
commit:
$ nix develop -c zon2nix --17 --nix=build.zig.zon.nix build.zig.zon
How it is laid out #
src/history/— what is browsed.History.zigholds the commits;Loader.zigis the seam a backend sits behind (ADR 0001);git/is the one backend there is, which runsgit logand parses what it writes;git/Refs.zigis the only thing in it that writes.src/find/— what the find bar looks for, and what grebe matches itself;history/git/Find.zigis what git answers.src/graph/— where each commit's node and edges go in the graph, worked out a row at a time, in lanes rather than pixels.src/ui/— what is drawn: the panes and their dividers, the commit list and its graph, the details and diff, the file list, the menus and dialogs, the fonts, the colors.src/config/— the configuration:Config.zig, what a file decodes to;load.zig, finding it and evaluating it, and again when the desktop's settings change.src/input/— key bindings:gitk's, and the configuration's over them.src/Clock.zig— the user's time zone, and dates on it.src/App.zig— the window and the loop that runs it.src/testing/— repositories for the tests to read.tests/pointer.py— a pointer for a headless sway, which has none: moves, clicks and waits, from one virtual pointer kept for the whole script.dist/— what is installed beside the binary: the icon at each size, and the desktop entry, filled in with where the binary is by-Dbindir;dist/windows/grebe.wxs, the MSI's source; anddist/notices/, license texts the Windows build's notices need.tools/— programs the build runs:ico.zigpacks the icon's PNGs into the one grebe.exe carries,notices.zigwrites the notices of everything grebe.exe is built with, andproduct_code.zigreads the MSI's ProductCode out for the winget manifest. All three are harrier's.msi.nix— the MSI, as a Nix package:nix build .#msi.tests/nixos/grebe.nix— grebe in a NixOS virtual machine, on a headless sway drawing with lavapipe, reading a repository made for it: keys, a configured one among them, find, a click, the primary selection, the desktop going from dark to light, and a configuration with a mistake in it; and a repository with SHA-256 object IDs.nix build .#checks.x86_64-linux.vmruns it; it needs KVM, and takes about a minute once everything is built.tests/portal.py— a stand-in for the desktop portal's settings, for a test with no desktop: it serves a color scheme, and changes it on a schedule. Run it on the session bus of adbus-run-session, with Python'sdbus-next.
Text #
Nothing a user reads is written in the code. Every message is in
locales/en-US.ftl, named by an id, and reached through src/l10n.zig:
l10n.text(id) for one with no arguments, l10n.format or l10n.write
with arguments. A number goes in as a number (l10n.num), so that the
language decides how it is written and which plural it takes. Add the
English message with the code that shows it, and a comment saying what its
arguments are. Logs stay in English.
The history's own side of the seam says nothing in words: what went wrong is a status or an error, and the interface picks the message.
Conventions #
- Use the words in GLOSSARY.md, and add to it when a new thing gets a name. A decision that is hard to reverse, would surprise a reader, and was a real choice gets an ADR in docs/adr.
- Nothing on the UI side of the seam knows git's output format, or that a backend is a process.
- grebe writes to the repository only to make, rename, delete and reset
branches and to make and delete tags, through
history/git/Refs.zig, when the user has confirmed it in a dialog (ADR 0003). Anything more is a new decision. - Doc comments carry the explanation: why a thing is the way it is.