# Backend battery as one command. `make battery` IS the manual battery every session used to retype; # it is the entry point CI will call, so what runs locally and what runs there cannot drift. # # Run from backend/. Toolchain and linter are PINNED here (never "latest"): the linter's findings are a # gate, and a gate that changes under you on someone else's machine is not a gate. GO ?= go GO_MIN_VERSION := 1.26.4 GOLANGCI_LINT ?= golangci-lint GOLANGCI_VERSION := 2.12.2 # backend/bin/ is already gitignored, so the vet tool leaves no untracked file behind. TMVET := bin/tmvet .PHONY: build vet fmt lint test battery battery-stand tools-check build: tools-check $(GO) build ./... # `-vettool` REPLACES the standard vet suite rather than adding to it (measured: a copylocks finding # that plain `go vet` reports disappears under -vettool), so both passes have to run. Both tag sets too: # the paid `live` tests compile only under -tags live and would otherwise rot unseen. vet: $(GO) vet ./... $(GO) vet -tags live ./... $(GO) build -o $(TMVET) ./cmd/tmvet $(GO) vet -vettool=$(TMVET) ./... $(GO) vet -tags live -vettool=$(TMVET) ./... # `gofmt -l` exits 0 even when it names files, so the emptiness of its output is the assertion. fmt: @test -z "$$(gofmt -l .)" || { echo "gofmt: not formatted:"; gofmt -l .; exit 1; } tools-check: @$(GO) version | grep -qE 'go1\.(2[6-9]|[3-9][0-9])' || { \ echo "Go $(GO_MIN_VERSION)+ required (go.mod floor); got: $$($(GO) version)"; exit 1; } @$(GOLANGCI_LINT) --version 2>/dev/null | grep -q " $(GOLANGCI_VERSION) " || { \ echo "golangci-lint $(GOLANGCI_VERSION) required (findings are version-dependent)."; \ echo "install: https://github.com/golangci/golangci-lint/releases/tag/v$(GOLANGCI_VERSION)"; exit 1; } lint: tools-check $(GOLANGCI_LINT) run --timeout=15m ./... # -race needs cgo (a C toolchain). If it is missing this fails loudly — dropping -race would turn a # missing toolchain into a green run that proved less than it claims. test: $(GO) test ./... -race -count=1 # The hermetic battery: everything that is green on a bare clone. It ends by NAMING the tests that did # not run, because a skip is invisible in `ok` lines and a silent skip reads as coverage. battery: build vet fmt lint test @echo "--- did NOT run (no stand data; see battery-stand) ---" @$(GO) test ./... -count=1 -v > .skips.log 2>&1 || { echo "the skip-harvest pass FAILED:"; \ grep -E '^(---|\s+---) FAIL|^FAIL' .skips.log; rm -f .skips.log; exit 1; } @grep -- '--- SKIP' .skips.log || echo "(none)" @rm -f .skips.log # The stand battery: adds the corpus-gated tests. With the flags set, MISSING data fails instead of # skipping, so "the corpus is here" is asserted rather than assumed. battery-stand: build vet fmt lint TM_MINER_PARITY=1 TM_CHECKER_LABELS=1 $(GO) test ./... -race -count=1