Assisted-by: GLM 5.3 Flash
4.3 KiB
Development
How to work on nuntius.
Prerequisites
- Go 1.27.1, the version recorded in
go.mod, the newest stable release. - just for the recipes.
- gcc for
just gates: the race detector needs cgo.
Nothing else. There is no Node, no Python, and no third-party Go module
beyond the first-party interpres
TOML parser.
Setup
git clone https://sourcedock.dev/petrbalvin/nuntius.git
cd nuntius
just build
Configuration lives in TOML, read from /etc/nuntius/config.toml by
default; the NUNTIUS_CONFIG environment variable points the server at any
file you like, which keeps development free of root-owned paths:
export NUNTIUS_CONFIG=./config.toml
export NUNTIUS_SMTP_PASSWORD=admin # the placeholder the template references
just run # first start writes the starter config
The starter forms point at example.com addresses, so submissions return
500 send_failed until config.toml carries real SMTP settings; strict
environment expansion means every ${VAR} the config references must be set
before startup.
Recipes
Every recipe in the project's file, taken from the file itself:
| Recipe | What it does |
|---|---|
just default |
prints the recipe list |
just build |
CGO_ENABLED=0 go build -trimpath -buildvcs=true -ldflags "-s -w" into bin/nuntius |
just test |
the suite with no test cache, then the 80 % coverage floor over ./internal/... |
just race |
the same suite under the race detector |
just unit |
fast scoped run for iterating: cached, no race, no coverage |
just fuzz |
time-boxed fuzz of one target in one package |
just bench |
benchmarks |
just fmt |
gofmt -w . |
just fmt-check |
zero gofmt diff; prints nothing when everything is formatted |
just vet |
go vet ./... and go fix -diff ./... |
just gates |
build, fmt-check, vet, test, race: the definition of done, once per task |
just clean |
removes bin/ and coverage.out |
just install |
builds, then copies the binary into ~/.local/bin (override with BINDIR) |
just uninstall |
removes the installed binary |
just run |
go run -buildvcs=true ./cmd/server |
just dev |
the same as run: nuntius carries no watch or reload tool |
just coverage-html |
HTML coverage report from the gate's profile; an extension, not a gate |
The test, race, unit and fuzz recipes run under a cgroup memory fence, so a runaway test dies at the ceiling instead of eating the machine.
Running a single test
go test -run TestName ./internal/handler/
Add -v for the sub-test names, and -race when the change touches
concurrency. -count=1 defeats the test cache when a result looks stale;
just unit keeps the cache on purpose, because a scoped iterating run wants
to be instant.
Coverage
just test
go tool cover -func=coverage.out
The total: line is the number that matters, and it stays at 80 percent or
more. Coverage is measured over the logic packages only; cmd/server is
thin glue around them. For the HTML map:
just coverage-html
Benchmarks
just bench
Benchmark on an idle machine, and compare only runs made in one process against each other: runs in separate processes, or on a loaded machine, differ by more than the effects being measured.
Fuzzing
Two fuzz targets exist: FuzzValidate in internal/contactform and
FuzzLoadConfig in internal/config. They are exploration, never a gate;
time-box one explicitly:
just fuzz FuzzValidate ./internal/contactform 30s
Debugging the build
go build -gcflags='-m' ./... # inlining decisions
go build -gcflags='-S' ./... # what the compiler generated
Continuous integration
Workflows live in .gitea/workflows/ and run on the project's own runners.
They are written by hand rather than through just, but they enforce the
same set of gates, so a green just gates locally is the fastest way to a
green pipeline. The per-workflow table is in
CONTRIBUTING.md.
Releases
Releases are cut by merging development into main and tagging vX.Y.Z.
The tag drives the release workflow, which builds the assets and publishes
the notes it extracted from CHANGELOG.md.