Contributing

This page is short. The repository holds the technical depth, and duplicating it here means one of the two goes stale.

Where things are

Source, issues, pull requestsgithub.com/giancarlosisasi/labdash
The design referenceDesign
This sitelabdash/website/ in the same repository

labdash is built and maintained by Giancarlos Isasi.

Building it

git clone https://github.com/giancarlosisasi/labdash.git
cd labdash
make build
make test

Go 1.26 or newer. No CGO, no code generation step, and no other tool required. make on its own lists the targets.

The fastest way to see whether a change to the appearance worked:

make preview                       # labdash theme preview, in this terminal

Running the tests

make test                          # everything
make test-race                     # the same suite under the race detector
make bench                         # the latency budgets
go test ./internal/tui/...         # one package tree
go test -run TestTHM07 ./internal/tui/theme

Every feature carries an identifier, and its test rows are written before the code that satisfies them. A test function's name embeds the identifier, so a failure names the feature: TestTHM07_T2_EveryGlyphIsOneSafeCell. Grepping for THM-07 finds the feature, its tests, its screen and its page.

Golden tests cover each screen in four states: populated, empty, error and narrow, and every screen is recorded at each colour tier and in both font modes.

Golden files

A golden file is a screen, recorded byte for byte, colour included. It lives at testdata/golden/<name>.golden beside the package that draws it.

go test ./...                      # compare against what is recorded
go test ./... -update              # rewrite from the current output

Three rules make this worth having:

  • A golden diff is reviewed like code. It is a screen changing. If you cannot say what changed and why, do not accept it.
  • CI never passes -update. A golden that no longer matches fails the build; it does not rewrite itself. A workflow that gains the flag fails its own check.
  • Every golden test fixes its width. A test that reads the real terminal's size is a flake waiting for a different CI runner.

Colour is inside the file on purpose. A colour regression is the hardest kind to spot by eye and the easiest to catch by diff, and the failure message escapes the sequences so the diff is readable.

Determinism

Nothing calls time.Now directly. A Clock is threaded through, and tests inject a fixed one, which is what lets a frame containing a timestamp live in a golden file at all. Anything else that would vary between runs has to be injected the same way — a -update producing a large diff for no reason is the signal that something was not.

The fake GitLab

No unit test needs a real instance. internal/testsupport/fakegitlab is an in-process server that answers the GraphQL endpoint, the four REST endpoints labdash uses, and GET /version:

srv := fakegitlab.New(t,
    fakegitlab.WithVersion("17.9.0"),
    fakegitlab.WithLatency("ReviewRequested", 3*time.Second),
    fakegitlab.WithFailure("Pipelines", fakegitlab.RateLimited),
)

It is a scenario builder rather than a cassette player, because latency, failure, rate limiting and pagination are what most of the tests are about, and none of the four is expressible in a recorded response body. Bodies still come from fixtures, for shape fidelity.

The credential guard

Every package's TestMain runs two suite-wide checks:

  • goleak, so a goroutine that outlives its test fails the package that leaked it.
  • The credential-shape scan, over everything the test binary wrote to stdout and stderr and over every file under testdata. It looks for GitLab's prefixed tokens, 64-character hex, Bearer credentials and the PRIVATE-TOKEN header, and it fails the package that produced one.

The guard reports the location and the length, never the value, because a failing build log is public. It exists because a test leaked a real token into its own output once already.

The documentation site

The site is Rspress v2 in labdash/website/:

cd labdash/website
pnpm install
pnpm run dev      # http://localhost:3000
pnpm run build    # the merge requirement

pnpm run build passing is the primary success criterion. It catches broken links, missing navigation entries, invalid frontmatter and failed MDX imports, which are the four ways a documentation site rots.

Two conventions worth knowing:

  • Second person, present tense, active voice. "Press r to refresh this section", never "the section can be refreshed by the user".
  • Numbers, not adjectives. "one request for a whole group tree, rather than one per project", never "blazing fast".

VOICE.md in that directory holds the rest, and it is binding on every page.

Two things on this site are generated from the Go source: the keyboard reference, from labdash keys --markdown, and the per-instance key table on instances. Do not hand-edit either; CI regenerates them and fails on a diff. Every other page is written by a person.

Review checklist

Each of these is a bug that has already happened somewhere:

  • No colour literal outside internal/tui/theme. CI enforces it.
  • Every state has a glyph, a colour and a word. Colour alone fails for colour-blind readers, at NO_COLOR, and in every screenshot.
  • Unicode and ASCII renderings have identical column widths. Every glyph is one cell, every fallback is one character, and no glyph is East Asian Wide or from a private-use area.
  • Contrast is computed, not eyeballed.
  • No token in a log, an error string, or a URL. Tests print token lengths, never values.
  • Every subprocess sends stdout and stderr to io.Discard. Output leaking into a full-screen application corrupts the frame.
  • The documentation ships with the change. The pages a change touches are part of the same change.
Nothing from a real instance, ever

No real project name, username, hostname, group path or token appears anywhere in the code, the tests, the demo data or these pages. Every example on this site uses fictional projects, and demo data comes from a fixture generator.

Proposing a change

Open an issue that names the behaviour you want and the screen it belongs on. A proposal that can name the feature identifiers it touches is easier to review, and labdash keys --list prints every built-in action name.

Three kinds of help are worth more than code:

  • Testing on hardware nobody here has. macOS and Linux credential paths, and any self-managed instance.
  • Terminal reports. The support matrix is marked tested only where somebody ran it.
  • detailedMergeStatus values in the wild. A blocker that renders as a title-cased raw enum is a mapping gap, and the exact value is what fixes it.

Rules on this site have their reason written next to them. If one looks wrong, argue with the reason.