# nuntius. # # Everything below the variable block is the standard recipe set from the `justfile` # skill, identical in every repository; project values live in the variable block only. binary := "nuntius" package := "./cmd/server" # What the test and bench recipes sweep. Scoped to the logic packages; the # thin cmd/ would drag the coverage total under the 80 percent floor. Keep # it equal to the Tests step in .gitea/workflows/test.yml. packages := "./internal/..." # The memory fence for the test recipes: a cgroup ceiling with swap off, so a # runaway run dies as a failed run and never eats the machine. 4G is the # default; the suite is small and quiet, so no raise is warranted. memlimit := "4G" bindir := env_var_or_default("BINDIR", env_var("HOME") / ".local" / "bin") default: @just --list # Compile. Zero errors, zero warnings; -trimpath and -buildvcs make the binary place-independent and version-stamped. build: CGO_ENABLED=0 go build -trimpath -buildvcs=true -ldflags "-s -w" -o bin/{{binary}} {{package}} # The test gate: the suite, no cache, the coverage floor, under the memory fence. test: #!/usr/bin/env perl my @fence = (q{systemd-run}, q{--user}, q{--scope}, q{-p}, q{MemoryMax={{memlimit}}}, q{-p}, q{MemorySwapMax=0}); system(@fence, q{go}, q{test}, q{-count=1}, q{-timeout}, q{10m}, q{-coverprofile}, q{coverage.out}, qw({{packages}})) == 0 or die qq{the test suite failed\n}; open(my $c, q{-|}, q{go}, q{tool}, q{cover}, q{-func=coverage.out}) or die qq{cover: $!}; my $total; while (my $l = <$c>) { $total = $1 if $l =~ m{^total:\s+\S+\s+([0-9.]+)%} } close($c); die qq{no total line in coverage.out\n} unless defined $total; printf qq{Total coverage: %s%%\n}, $total; exit($total < 80 ? 1 : 0); # The same suite under the race detector. The expensive one, still fenced. race: systemd-run --user --scope -p MemoryMax={{memlimit}} -p MemorySwapMax=0 go test -race -count=1 -timeout 10m {{packages}} # Fast scoped run for iterating. This is the one that runs after every edit. unit pkgs=packages run=".*": systemd-run --user --scope -p MemoryMax={{memlimit}} -p MemorySwapMax=0 go test {{pkgs}} -run '{{run}}' # Time-boxed fuzz of one target in one package. The package is required; never a gate. fuzz target pkg fuzztime="60s": systemd-run --user --scope -p MemoryMax={{memlimit}} -p MemorySwapMax=0 go test -run '^$' -fuzz '{{target}}' -fuzztime={{fuzztime}} {{pkg}} # A ceiling would distort the measurement, and the recipe's own rule is that nothing else runs while it does. # Benchmarks. On an idle machine only, and deliberately unfenced. bench pkgs=packages: go test -run '^$' -bench=. -benchmem -count=5 {{pkgs}} # Format in place. fmt: gofmt -w . # Zero diff. Prints nothing when everything is formatted. fmt-check: #!/usr/bin/env perl open(my $g, q{-|}, q{gofmt}, q{-l}, q{.}) or die qq{gofmt: $!}; my @bad = <$g>; close($g); print @bad; exit(@bad ? 1 : 0); # Both static gates: go vet and go fix -diff. vet: go vet ./... go fix -diff ./... # The definition of done, in one command. Once per task, never per edit. gates: build fmt-check vet test race # Build artefacts only, not the installed binary. clean: rm -rf bin/ coverage.out # Build, then copy the binary into bindir. install: build install -d "{{bindir}}" install -m 755 bin/{{binary}} "{{bindir}}/{{binary}}" # Remove the installed binary. uninstall: rm -f "{{bindir}}/{{binary}}" # Run the program. The flag is there because `go run` does not stamp the build otherwise. run: go run -buildvcs=true {{package}} # Run with watch or hot reload, where the project has one. dev: go run -buildvcs=true {{package}} # Coverage report as an HTML map from the gate's profile; not standard because the gate needs only the numeric floor, and a browser artefact is exploration, not a gate. coverage-html: test go tool cover -html=coverage.out -o coverage.html