export GOTOOLCHAIN := go1.26.5 export GOPRIVATE := $(GOPRIVATE),amadan.net/oficina/estilo.git private_comma := , export GOPRIVATE := $(if $(GOPRIVATE),$(GOPRIVATE)$(private_comma))amadan.net/oficina/estilo.git,amadan.net/carlos/platform.git .PHONY: build release deps test test-vet test-fmt test-build test-go test-race test-browser # One binary, CGO_ENABLED=0, cgo-free SQLite (AGENTS.md / Deploys). Set # per-target rather than exported globally, because test-race is a # deliberate, documented exception — see its comment. build: CGO_ENABLED=0 go build ./... # release is what ships, and it is the ONLY way a binary should be built # for shipping. rastrillo's BuildVersion defaults to "dev", so an # unstamped binary answers GET /api/version with "dev" from every build # ever made — and that is the field the deploy check compares, so an # unstamped release makes the check structurally incapable of failing # while still reporting green. A plain `go build` sent to an instance is # exactly the mistake this target removes. # # linux/arm64 because that is what the instances run; building for this # Mac and shipping it is a silent architecture mismatch that uploads # fine and fails to exec. Override for a one-off: # make release RELEASE_GOARCH=amd64 # # -s -w drops the symbol table and DWARF. Panic traces keep their # function names and line numbers either way (pclntab is untouched); # what you lose is delve and gdb, so build without them for that. # # git describe --tags, because the tag is what gets published. A dirty # tree is refused rather than suffixed: a -dirty stamp names a commit # plus changes nobody can name, so two different binaries can carry the # identical string, which makes the check look reliable while it is not. # If you mean to ship uncommitted work, say so out loud: # make release VERSION=v0.1.0-wip APP := home RELEASE_GOOS ?= linux RELEASE_GOARCH ?= arm64 RELEASE_BIN := releases/$(APP)-$(RELEASE_GOOS)-$(RELEASE_GOARCH) VERSION ?= $(shell git describe --tags --always 2>/dev/null) release: @git diff --quiet && git diff --cached --quiet || \ { echo "refusing to build a release from a dirty tree"; exit 1; } @test -n "$(VERSION)" || \ { echo "make release: no version to stamp; commit, or name it: make release VERSION=v0.1.0"; exit 1; } @mkdir -p releases CGO_ENABLED=0 GOOS=$(RELEASE_GOOS) GOARCH=$(RELEASE_GOARCH) go build -trimpath \ -ldflags "-s -w -X amadan.net/rastrillo/rastrillo.BuildVersion=$(VERSION)" \ -o $(RELEASE_BIN) ./cmd/$(APP) @echo "built $(RELEASE_BIN) reporting version $(VERSION)" @echo "publish it with:" @echo " amadan release create -repo oficina/$(APP) -notes-file NOTES.md $(VERSION) $(RELEASE_BIN)" # The gate, split into named targets so CI can report them individually # (.amadan/ci.d calls these one at a time, each its own step). `make # test` runs exactly the same things in the same order, so the local # gate and CI stay ONE definition rather than two lists that drift # apart. Debugging CI is running `make test`. # # Order is cheapest-and-fastest first: vet and gofmt are near-instant, # build catches compile errors before time is spent on tests, plain # `go test` next, and -race — by far the slowest step — last. test: test-vet test-fmt test-build test-go test-race # The modules first, one directory at a time and retried. The hub # rate-limits public git reads per address (5 a second, a burst of 10), # and a cold module cache fetches estilo, rastrillo, the platform and # this repo at once — more than the burst — so vet failed every run # with HTTP 429 however long the runner waited between runs. What one # attempt fetched stays cached, so each retry asks for less. deps: @for dir in . handoff roster connect; do \ n=0; until (cd $$dir && go mod download); do \ n=$$((n+1)); [ $$n -lt 6 ] || exit 1; \ echo "go mod download in $$dir failed (the hub's git rate limit?); retrying in $$((n*5))s"; \ sleep $$((n*5)); \ done; \ done test-vet: deps CGO_ENABLED=0 go vet ./... CGO_ENABLED=0 go vet -tags browser ./internal/home/ cd handoff && CGO_ENABLED=0 go vet ./... cd roster && CGO_ENABLED=0 go vet ./... cd connect && CGO_ENABLED=0 go vet ./... # Fails on unformatted files rather than rewriting them. CI must never # silently "fix" a diff out from under a branch — a red test-fmt tells # the author to run `gofmt -w` themselves. test-fmt: @if [ -n "$$(gofmt -l .)" ]; then \ echo "gofmt needs to be run on:"; \ gofmt -l .; \ exit 1; \ fi test-build: CGO_ENABLED=0 go build ./... CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build ./cmd/home ./cmd/oficina ./internal/operatorlaunch cd handoff && CGO_ENABLED=0 go build ./... cd roster && CGO_ENABLED=0 go build ./... cd connect && CGO_ENABLED=0 go build ./... test-go: CGO_ENABLED=0 go test -count=1 ./... cd handoff && CGO_ENABLED=0 go test -count=1 ./... cd roster && CGO_ENABLED=0 go test -count=1 ./... cd connect && CGO_ENABLED=0 go test -count=1 ./... # The one concurrency-bearing package. internal/home's link store is # taken by a single DELETE ... RETURNING precisely so that two clicks on # one magic link mint one session, and its test races sixteen goroutines # at the same row to prove it. A race the detector would catch there is # a session minted twice. # # Deliberately NOT CGO_ENABLED=0: the race detector requires cgo (`go: # -race requires cgo`), so this one target runs with the ambient # CGO_ENABLED. Nothing it builds is shipped, so it does not violate the # cgo-free binary rule, which is about what gets deployed. test-race: go test -race -count=1 ./internal/home/ ./internal/operator/ ./internal/operatorlaunch/ ./internal/provisioner/ ./internal/home/directorydata/ cd handoff && CGO_ENABLED=1 go test -race -count=1 ./... cd roster && CGO_ENABLED=1 go test -race -count=1 ./... cd connect && CGO_ENABLED=1 go test -race -count=1 ./... # The browser checks, in a real Chromium (RASTRILLO_CHROME, on PATH, or # the newest in a Playwright cache: Linux's ~/.cache or macOS's # ~/Library/Caches). Deliberately NOT part of `test`: the gate CI runs on # every push must not need a browser. Run it before shipping a change to # the CSS or to rastrillo's pin — TestNoZoomOnTap is the iOS zoom-on-tap # check. Every test in the package runs, the browser ones (the `browser` # tag) with them, so none is left out by a -run filter that predates it. # test-vet vets these files with the tag, so they keep compiling. test-browser: CGO_ENABLED=0 go test -tags browser -count=1 ./internal/home/ .PHONY: credits credits: @set -eu; notices_tmp=$$(mktemp -d); trap 'rm -rf "$$notices_tmp"' EXIT; \ GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOHOSTARCH) CGO_ENABLED=0 \ go build -o "$$notices_tmp/notices" amadan.net/oficina/estilo.git/cmd/oficina-notices; \ GOOS=linux GOARCH=arm64 CGO_ENABLED=0 "$$notices_tmp/notices" \ -package ./cmd/home -lucide -onest \ -exclude amadan.net/oficina/estilo.git,amadan.net/oficina/home/handoff,amadan.net/oficina/home/roster \ > "$$notices_tmp/notices.json"; \ mv "$$notices_tmp/notices.json" internal/home/credits/notices.json