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.
Before a large change
Section titled “Before a large change”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.
Getting set up
Section titled “Getting set up”Requires Go, plus shfmt and prettier for the format check.
git clone https://github.com/loadoutsh/loadout.gitcd loadoutgit config core.hooksPath hooks # formats staged files on commitmake check # what CI runsTest against a scratch configuration rather than your own:
export XDG_CONFIG_HOME=/tmp/lo-testmkdir -p $XDG_CONFIG_HOME/loadoutcp testdata/fixture/config.yaml $XDG_CONFIG_HOME/loadout/make install && loadout applyWhere a change belongs
Section titled “Where a change belongs”| Bucket | Test |
|---|---|
| Core | Useful to nearly everyone, contains no personal values |
| Pack | Tied to one tool or ecosystem, off by default |
| Config | Contains a path, host, org or identity that varies per user |
| Drop | Personal, 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,delandpingwere dropped for this. Changing whatlsdoes breaks scripts and surprises people. - Nothing ships an alias for unmaintained software. If a modern equivalent is wanted, add it under a new name.
Rules that exist for a reason
Section titled “Rules that exist for a reason”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}andlocal -Aare banned and tested for;getoptsneedslocal OPTIND=1. See Shell portability. - A tilde does not expand inside double quotes. Interpolate paths with the
shpathtemplate helper. - Secrets never go in a file loadout controls.
- Anything that lands on
PATHis 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
Hostcheck. It is the DNS rebinding defence on an API that rewrites what a shell executes at login.
Testing
Section titled “Testing”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.
The commands
Section titled “The commands”make check # build, vet, test, format — what CI runsmake golden # re-record golden files after an intended changemake install # build into ~/.local/binmake release # cross-compile all four platforms into dist/make tap # render the Homebrew formula from dist/checksums.txtmake notes # preview release notes for VERSIONFormatting is enforced: shfmt for shell, gofmt for Go, Prettier for markdown, all via ./bin/fmt.
Conventions
Section titled “Conventions”- 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.
Changing this site
Section titled “Changing this site”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.