| 1 | comma := , |
| 2 | export GOPRIVATE := $(if $(GOPRIVATE),$(GOPRIVATE)$(comma))amadan.net/oficina/estilo.git |
| 3 | |
| 4 | .PHONY: build release test test-vet test-fmt test-build test-migrations test-go test-race test-browser |
| 5 | |
| 6 | # One binary, CGO_ENABLED=0, cgo-free SQLite (AGENTS.md / Deploys). Set |
| 7 | # per-target below rather than exported globally, because test-race is |
| 8 | # a deliberate, documented exception — see its comment. |
| 9 | build: |
| 10 | CGO_ENABLED=0 go build ./... |
| 11 | |
| 12 | # The ONLY way a binary should be built for shipping. rastrillo's |
| 13 | # BuildVersion defaults to "dev", and an unstamped binary answers |
| 14 | # GET /api/version with "dev" — which is the field used to tell what is |
| 15 | # actually deployed, so an unstamped release makes the fleet unreadable |
| 16 | # while still looking like a successful deploy. Shipping one by hand |
| 17 | # with a plain `go build` is exactly the mistake this target removes. |
| 18 | # |
| 19 | # A dirty tree is refused: the sha would name a commit that is not what |
| 20 | # is in the binary, which is worse than no stamp at all. |
| 21 | # |
| 22 | # The stamp was a short sha because there were no tags to describe. Now |
| 23 | # that a release IS a tag, git describe --tags --always answers with the |
| 24 | # tag when the build sits on one and falls back to the same short sha |
| 25 | # when it does not, so nothing is lost and a published binary reports |
| 26 | # the name it was published under. |
| 27 | # |
| 28 | # The artifact lands in releases/ under the name the publish step |
| 29 | # expects: `amadan release create` uploads a file under its basename, |
| 30 | # and that basename is what appears on the release page permanently. |
| 31 | APP := sheets |
| 32 | RELEASE_GOOS ?= linux |
| 33 | RELEASE_GOARCH ?= arm64 |
| 34 | RELEASE_BIN := releases/$(APP)-$(RELEASE_GOOS)-$(RELEASE_GOARCH) |
| 35 | VERSION ?= $(shell git describe --tags --always 2>/dev/null) |
| 36 | |
| 37 | release: |
| 38 | @git diff --quiet && git diff --cached --quiet || \ |
| 39 | { echo "refusing to build a release from a dirty tree"; exit 1; } |
| 40 | @test -n "$(VERSION)" || \ |
| 41 | { echo "make release: no version to stamp; commit, or name it: make release VERSION=v0.1.0"; exit 1; } |
| 42 | @mkdir -p releases |
| 43 | CGO_ENABLED=0 GOOS=$(RELEASE_GOOS) GOARCH=$(RELEASE_GOARCH) go build -trimpath \ |
| 44 | -ldflags "-s -w -X amadan.net/rastrillo/rastrillo.BuildVersion=$(VERSION)" \ |
| 45 | -o $(RELEASE_BIN) ./cmd/$(APP) |
| 46 | @echo "built $(RELEASE_BIN) reporting version $(VERSION)" |
| 47 | @echo "publish it with:" |
| 48 | @echo " amadan release create -repo oficina/$(APP) -notes-file NOTES.md $(VERSION) $(RELEASE_BIN)" |
| 49 | |
| 50 | # The gate, split into named targets so CI can report them individually |
| 51 | # (.amadan/ci.d calls these one at a time, each its own step). `make |
| 52 | # test` runs exactly the same six things in the same order, so the |
| 53 | # local gate and CI stay ONE definition rather than two lists that |
| 54 | # drift apart. Debugging CI is running `make test`. |
| 55 | # |
| 56 | # Order is cheapest-and-fastest first: vet and gofmt are near-instant, |
| 57 | # build catches compile errors before minutes are spent on tests, |
| 58 | # test-migrations needs the app to compile but no database and no test |
| 59 | # suite, plain `go test` (tens of seconds, dominated by the store |
| 60 | # package) comes next, and -race — by far the slowest step — runs last. |
| 61 | test: test-vet test-fmt test-build test-migrations test-go test-race |
| 62 | |
| 63 | # Also vets the browser-tagged tests: they are outside `make test`, and a |
| 64 | # test behind a tag that nothing compiles rots silently. |
| 65 | test-vet: |
| 66 | CGO_ENABLED=0 go vet ./... |
| 67 | CGO_ENABLED=0 go vet -tags browser ./internal/sheets/ |
| 68 | |
| 69 | # Fails on unformatted files rather than rewriting them. CI must never |
| 70 | # silently "fix" a diff out from under a branch — a red test-fmt tells |
| 71 | # the author to run `gofmt -w` themselves. |
| 72 | test-fmt: |
| 73 | @if [ -n "$$(gofmt -l .)" ]; then \ |
| 74 | echo "gofmt needs to be run on:"; \ |
| 75 | gofmt -l .; \ |
| 76 | exit 1; \ |
| 77 | fi |
| 78 | |
| 79 | test-build: |
| 80 | CGO_ENABLED=0 go build ./... |
| 81 | |
| 82 | # CI gate for AGENTS.md's additive-only migrations rule: models |
| 83 | # (internal/sheets/migrations.go's Models) and migrations |
| 84 | # (internal/sheets/migrations/*.sql, via Schema) must already agree. |
| 85 | # Touches no database — it diffs structs against committed SQL, the |
| 86 | # same computation `rastrillo migration generate` does before deciding |
| 87 | # there is nothing to write — so it belongs this early, right after the |
| 88 | # app is known to compile and well before test-go pays for real SQLite |
| 89 | # files. A model changed without `rastrillo migration generate` run |
| 90 | # after it fails here, not in production at the next boot. |
| 91 | # |
| 92 | # `go tool`, not a bare `rastrillo` on PATH: go.mod pins the CLI as a tool |
| 93 | # at the same version as the module, so the check always runs the |
| 94 | # migrate the app is built against — never whatever a machine happened |
| 95 | # to have installed (a v0.25.0 CLI answering for a v0.23.0 app, until |
| 96 | # the move to amadan.net/rastrillo/rastrillo). |
| 97 | test-migrations: |
| 98 | go tool rastrillo migration check |
| 99 | |
| 100 | # Includes internal/guardstest: the mechanical guards for the line cap, |
| 101 | # forbidden imports, excelize confinement, calc isolation and sealtest |
| 102 | # containment all run here as ordinary tests, plus internal/sheets/seal |
| 103 | # and internal/sheets/xlsx's plaintext-leak checks (AGENTS.md / "The one |
| 104 | # rule"). -count=1 so a cached "ok" from a previous run can never stand |
| 105 | # in for actually having run. |
| 106 | test-go: |
| 107 | CGO_ENABLED=0 go test -count=1 ./... |
| 108 | |
| 109 | # The concurrency-bearing packages: store's one-connection writer pool |
| 110 | # and its handle gate (AGENTS.md: "Scope tx, never the outer handle, |
| 111 | # inside transactions, or the one-connection writer pool deadlocks"), |
| 112 | # and live's fan-out hub, where one publisher and every open browser |
| 113 | # tab meet. Scoped to those two rather than ./... because -race's |
| 114 | # slowdown is worth paying where the concurrency actually lives, not |
| 115 | # repeated across packages that don't touch goroutines. |
| 116 | # |
| 117 | # Deliberately NOT CGO_ENABLED=0: the race detector requires cgo (`go: |
| 118 | # -race requires cgo`), so this one target runs with the ambient |
| 119 | # CGO_ENABLED (cgo on, given a C toolchain is present). Nothing this |
| 120 | # target builds is shipped — it only exercises store's own test suite — |
| 121 | # so it does not violate the cgo-free binary rule, which is about what |
| 122 | # gets deployed. |
| 123 | test-race: |
| 124 | go test -race -count=1 ./internal/sheets/store/... ./internal/sheets/live/... |
| 125 | |
| 126 | # The browser checks (internal/sheets/*_browser_test.go), in a real |
| 127 | # Chromium: on PATH, via RASTRILLO_CHROME, or in a Playwright cache. |
| 128 | # Deliberately NOT part of `test`: the gate CI runs on every push must |
| 129 | # not need a browser. Run it before shipping a change to the CSS or to |
| 130 | # rastrillo's pin — TestNoZoomOnTap is the iOS zoom-on-tap check, and |
| 131 | # TestDocumentStylesSurviveTheProductionCSP is the one browser check served |
| 132 | # through rastrillo's production stack, so it sees the real CSP. |
| 133 | test-browser: |
| 134 | CGO_ENABLED=0 go test -tags browser -count=1 -run 'OnTap|DocumentStyles' ./internal/sheets/ |
| 135 | |