Files
nuntius/docs/DEVELOPMENT.md
T

131 lines
4.3 KiB
Markdown
Raw Normal View History

# Development
How to work on nuntius.
## Prerequisites
- Go 1.27.1, the version recorded in `go.mod`, the newest stable release.
- [just](https://github.com/casey/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`](https://sourcedock.dev/petrbalvin/interpres)
TOML parser.
## Setup
```sh
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:
```sh
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
```sh
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
```sh
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:
```sh
just coverage-html
```
## Benchmarks
```sh
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:
```sh
just fuzz FuzzValidate ./internal/contactform 30s
```
## Debugging the build
```sh
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](../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`.