comma := , export GOPRIVATE := $(if $(GOPRIVATE),$(GOPRIVATE)$(comma))amadan.net/oficina/estilo.git .PHONY: build release test test-vet test-fmt test-build test-migrations test-go test-race test-browser # One binary, CGO_ENABLED=0, cgo-free SQLite (AGENTS.md / Deploys). Set # per-target below rather than exported globally, because test-race is # a deliberate, documented exception — see its comment. build: CGO_ENABLED=0 go build ./... # The ONLY way a binary should be built for shipping. rastrillo's # BuildVersion defaults to "dev", and an unstamped binary answers # GET /api/version with "dev" — which is the field used to tell what is # actually deployed, so an unstamped release makes the fleet unreadable # while still looking like a successful deploy. Shipping one by hand # with a plain `go build` is exactly the mistake this target removes. # # A dirty tree is refused: the sha would name a commit that is not what # is in the binary, which is worse than no stamp at all. # # The stamp was a short sha because there were no tags to describe. Now # that a release IS a tag, git describe --tags --always answers with the # tag when the build sits on one and falls back to the same short sha # when it does not, so nothing is lost and a published binary reports # the name it was published under. # # The artifact lands in releases/ under the name the publish step # expects: `amadan release create` uploads a file under its basename, # and that basename is what appears on the release page permanently. APP := sheets 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 six 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 minutes are spent on tests, # test-migrations needs the app to compile but no database and no test # suite, plain `go test` (tens of seconds, dominated by the store # package) comes next, and -race — by far the slowest step — runs last. test: test-vet test-fmt test-build test-migrations test-go test-race # Also vets the browser-tagged tests: they are outside `make test`, and a # test behind a tag that nothing compiles rots silently. test-vet: CGO_ENABLED=0 go vet ./... CGO_ENABLED=0 go vet -tags browser ./internal/sheets/ # 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 ./... # CI gate for AGENTS.md's additive-only migrations rule: models # (internal/sheets/migrations.go's Models) and migrations # (internal/sheets/migrations/*.sql, via Schema) must already agree. # Touches no database — it diffs structs against committed SQL, the # same computation `rastrillo migration generate` does before deciding # there is nothing to write — so it belongs this early, right after the # app is known to compile and well before test-go pays for real SQLite # files. A model changed without `rastrillo migration generate` run # after it fails here, not in production at the next boot. # # `go tool`, not a bare `rastrillo` on PATH: go.mod pins the CLI as a tool # at the same version as the module, so the check always runs the # migrate the app is built against — never whatever a machine happened # to have installed (a v0.25.0 CLI answering for a v0.23.0 app, until # the move to amadan.net/rastrillo/rastrillo). test-migrations: go tool rastrillo migration check # Includes internal/guardstest: the mechanical guards for the line cap, # forbidden imports, excelize confinement, calc isolation and sealtest # containment all run here as ordinary tests, plus internal/sheets/seal # and internal/sheets/xlsx's plaintext-leak checks (AGENTS.md / "The one # rule"). -count=1 so a cached "ok" from a previous run can never stand # in for actually having run. test-go: CGO_ENABLED=0 go test -count=1 ./... # The concurrency-bearing packages: store's one-connection writer pool # and its handle gate (AGENTS.md: "Scope tx, never the outer handle, # inside transactions, or the one-connection writer pool deadlocks"), # and live's fan-out hub, where one publisher and every open browser # tab meet. Scoped to those two rather than ./... because -race's # slowdown is worth paying where the concurrency actually lives, not # repeated across packages that don't touch goroutines. # # Deliberately NOT CGO_ENABLED=0: the race detector requires cgo (`go: # -race requires cgo`), so this one target runs with the ambient # CGO_ENABLED (cgo on, given a C toolchain is present). Nothing this # target builds is shipped — it only exercises store's own test suite — # so it does not violate the cgo-free binary rule, which is about what # gets deployed. test-race: go test -race -count=1 ./internal/sheets/store/... ./internal/sheets/live/... # The browser checks (internal/sheets/*_browser_test.go), in a real # Chromium: on PATH, via RASTRILLO_CHROME, or in a Playwright cache. # 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, and # TestDocumentStylesSurviveTheProductionCSP is the one browser check served # through rastrillo's production stack, so it sees the real CSP. test-browser: CGO_ENABLED=0 go test -tags browser -count=1 -run 'OnTap|DocumentStyles' ./internal/sheets/