Skip to content

Contributing

This is a summary. The authoritative version is CONTRIBUTING.md in the repository, which is what a reviewer will hold a pull request to.

Open an issue first. loadout is opinionated on purpose, and the most common reason to decline a pull request is that the change belongs somewhere other than the shipped catalog — not that it is wrong.

Small fixes, typos and clearly-scoped bugs need no discussion.

Requires Go, plus shfmt and prettier for the format check.

Terminal window
git clone https://github.com/loadoutsh/loadout.git
cd loadout
git config core.hooksPath hooks # formats staged files on commit
make check # what CI runs

Test against a scratch configuration rather than your own:

Terminal window
export XDG_CONFIG_HOME=/tmp/lo-test
mkdir -p $XDG_CONFIG_HOME/loadout
cp testdata/fixture/config.yaml $XDG_CONFIG_HOME/loadout/
make install && loadout apply
BucketTest
CoreUseful to nearly everyone, contains no personal values
PackTied to one tool or ecosystem, off by default
ConfigContains a path, host, org or identity that varies per user
DropPersonal, dead upstream, or a bad default for strangers

Two decisions already made, which need a strong argument to revisit:

  • Nothing shadows a familiar command. ls, del and ping were dropped for this. Changing what ls does breaks scripts and surprises people.
  • Nothing ships an alias for unmaintained software. If a modern equivalent is wanted, add it under a new name.

Each of these was a real bug, not a style preference.

  • Generated shell must work in zsh and bash, on Linux and macOS. ${=var}, ${var:t} and local -A are banned and tested for; getopts needs local OPTIND=1. See Shell portability.
  • A tilde does not expand inside double quotes. Interpolate paths with the shpath template helper.
  • Secrets never go in a file loadout controls.
  • Anything that lands on PATH is verified. HTTPS on every redirect hop, checksum before install, no archive members with .. or absolute paths, writes by rename.
  • Do not loosen the web interface’s Host check. It is the DNS rebinding defence on an API that rewrites what a shell executes at login.

Parsing is not behaviour. zsh -n and bash -n both pass on code that is wrong at runtime. Compare what functions do.

Golden files pin the whole output. testdata/golden holds the complete generated shell for both dialects on both platforms. Re-record with make golden and review the diff, because that diff is what lands in every user’s shell.

A check that cannot fail is worse than no check. When adding one, make it fail first.

Terminal window
make check # build, vet, test, format — what CI runs
make golden # re-record golden files after an intended change
make install # build into ~/.local/bin
make release # cross-compile all four platforms into dist/
make tap # render the Homebrew formula from dist/checksums.txt
make notes # preview release notes for VERSION

Formatting is enforced: shfmt for shell, gofmt for Go, Prettier for markdown, all via ./bin/fmt.

  • Comments explain why, especially where the code looks odd. Most odd-looking code in this project is defending against something specific.
  • Commit messages describe what changed and what it means, not just the diff.
  • Nothing personal in the repository: no real hostnames, organisation names, paths or emails. The fixture and every example use illustrative values.

The documentation site lives in its own repository. The alias, function and pack reference pages are generated from the catalog — edit internal/catalog/data/ in the loadout repository, then re-run npm run gen here and commit the result.