| 1 | export GOTOOLCHAIN := go1.26.5 |
| 2 | export GOPRIVATE := $(GOPRIVATE),amadan.net/oficina/estilo.git |
| 3 | |
| 4 | private_comma := , |
| 5 | export GOPRIVATE := $(if $(GOPRIVATE),$(GOPRIVATE)$(private_comma))amadan.net/oficina/estilo.git,amadan.net/carlos/platform.git |
| 6 | |
| 7 | .PHONY: build release deps test test-vet test-fmt test-build test-go test-race test-browser |
| 8 | |
| 9 | # One binary, CGO_ENABLED=0, cgo-free SQLite (AGENTS.md / Deploys). Set |
| 10 | # per-target rather than exported globally, because test-race is a |
| 11 | # deliberate, documented exception — see its comment. |
| 12 | build: |
| 13 | CGO_ENABLED=0 go build ./... |
| 14 | |
| 15 | # release is what ships, and it is the ONLY way a binary should be built |
| 16 | # for shipping. rastrillo's BuildVersion defaults to "dev", so an |
| 17 | # unstamped binary answers GET /api/version with "dev" from every build |
| 18 | # ever made — and that is the field the deploy check compares, so an |
| 19 | # unstamped release makes the check structurally incapable of failing |
| 20 | # while still reporting green. A plain `go build` sent to an instance is |
| 21 | # exactly the mistake this target removes. |
| 22 | # |
| 23 | # linux/arm64 because that is what the instances run; building for this |
| 24 | # Mac and shipping it is a silent architecture mismatch that uploads |
| 25 | # fine and fails to exec. Override for a one-off: |
| 26 | # make release RELEASE_GOARCH=amd64 |
| 27 | # |
| 28 | # -s -w drops the symbol table and DWARF. Panic traces keep their |
| 29 | # function names and line numbers either way (pclntab is untouched); |
| 30 | # what you lose is delve and gdb, so build without them for that. |
| 31 | # |
| 32 | # git describe --tags, because the tag is what gets published. A dirty |
| 33 | # tree is refused rather than suffixed: a -dirty stamp names a commit |
| 34 | # plus changes nobody can name, so two different binaries can carry the |
| 35 | # identical string, which makes the check look reliable while it is not. |
| 36 | # If you mean to ship uncommitted work, say so out loud: |
| 37 | # make release VERSION=v0.1.0-wip |
| 38 | APP := home |
| 39 | RELEASE_GOOS ?= linux |
| 40 | RELEASE_GOARCH ?= arm64 |
| 41 | RELEASE_BIN := releases/$(APP)-$(RELEASE_GOOS)-$(RELEASE_GOARCH) |
| 42 | VERSION ?= $(shell git describe --tags --always 2>/dev/null) |
| 43 | |
| 44 | release: |
| 45 | @git diff --quiet && git diff --cached --quiet || \ |
| 46 | { echo "refusing to build a release from a dirty tree"; exit 1; } |
| 47 | @test -n "$(VERSION)" || \ |
| 48 | { echo "make release: no version to stamp; commit, or name it: make release VERSION=v0.1.0"; exit 1; } |
| 49 | @mkdir -p releases |
| 50 | CGO_ENABLED=0 GOOS=$(RELEASE_GOOS) GOARCH=$(RELEASE_GOARCH) go build -trimpath \ |
| 51 | -ldflags "-s -w -X amadan.net/rastrillo/rastrillo.BuildVersion=$(VERSION)" \ |
| 52 | -o $(RELEASE_BIN) ./cmd/$(APP) |
| 53 | @echo "built $(RELEASE_BIN) reporting version $(VERSION)" |
| 54 | @echo "publish it with:" |
| 55 | @echo " amadan release create -repo oficina/$(APP) -notes-file NOTES.md $(VERSION) $(RELEASE_BIN)" |
| 56 | |
| 57 | # The gate, split into named targets so CI can report them individually |
| 58 | # (.amadan/ci.d calls these one at a time, each its own step). `make |
| 59 | # test` runs exactly the same things in the same order, so the local |
| 60 | # gate and CI stay ONE definition rather than two lists that drift |
| 61 | # apart. Debugging CI is running `make test`. |
| 62 | # |
| 63 | # Order is cheapest-and-fastest first: vet and gofmt are near-instant, |
| 64 | # build catches compile errors before time is spent on tests, plain |
| 65 | # `go test` next, and -race — by far the slowest step — last. |
| 66 | test: test-vet test-fmt test-build test-go test-race |
| 67 | |
| 68 | # The modules first, one directory at a time and retried. The hub |
| 69 | # rate-limits public git reads per address (5 a second, a burst of 10), |
| 70 | # and a cold module cache fetches estilo, rastrillo, the platform and |
| 71 | # this repo at once — more than the burst — so vet failed every run |
| 72 | # with HTTP 429 however long the runner waited between runs. What one |
| 73 | # attempt fetched stays cached, so each retry asks for less. |
| 74 | deps: |
| 75 | @for dir in . handoff roster connect; do \ |
| 76 | n=0; until (cd $$dir && go mod download); do \ |
| 77 | n=$$((n+1)); [ $$n -lt 6 ] || exit 1; \ |
| 78 | echo "go mod download in $$dir failed (the hub's git rate limit?); retrying in $$((n*5))s"; \ |
| 79 | sleep $$((n*5)); \ |
| 80 | done; \ |
| 81 | done |
| 82 | |
| 83 | test-vet: deps |
| 84 | CGO_ENABLED=0 go vet ./... |
| 85 | CGO_ENABLED=0 go vet -tags browser ./internal/home/ |
| 86 | cd handoff && CGO_ENABLED=0 go vet ./... |
| 87 | cd roster && CGO_ENABLED=0 go vet ./... |
| 88 | cd connect && CGO_ENABLED=0 go vet ./... |
| 89 | |
| 90 | # Fails on unformatted files rather than rewriting them. CI must never |
| 91 | # silently "fix" a diff out from under a branch — a red test-fmt tells |
| 92 | # the author to run `gofmt -w` themselves. |
| 93 | test-fmt: |
| 94 | @if [ -n "$$(gofmt -l .)" ]; then \ |
| 95 | echo "gofmt needs to be run on:"; \ |
| 96 | gofmt -l .; \ |
| 97 | exit 1; \ |
| 98 | fi |
| 99 | |
| 100 | test-build: |
| 101 | CGO_ENABLED=0 go build ./... |
| 102 | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build ./cmd/home ./cmd/oficina ./internal/operatorlaunch |
| 103 | cd handoff && CGO_ENABLED=0 go build ./... |
| 104 | cd roster && CGO_ENABLED=0 go build ./... |
| 105 | cd connect && CGO_ENABLED=0 go build ./... |
| 106 | |
| 107 | test-go: |
| 108 | CGO_ENABLED=0 go test -count=1 ./... |
| 109 | cd handoff && CGO_ENABLED=0 go test -count=1 ./... |
| 110 | cd roster && CGO_ENABLED=0 go test -count=1 ./... |
| 111 | cd connect && CGO_ENABLED=0 go test -count=1 ./... |
| 112 | |
| 113 | # The one concurrency-bearing package. internal/home's link store is |
| 114 | # taken by a single DELETE ... RETURNING precisely so that two clicks on |
| 115 | # one magic link mint one session, and its test races sixteen goroutines |
| 116 | # at the same row to prove it. A race the detector would catch there is |
| 117 | # a session minted twice. |
| 118 | # |
| 119 | # Deliberately NOT CGO_ENABLED=0: the race detector requires cgo (`go: |
| 120 | # -race requires cgo`), so this one target runs with the ambient |
| 121 | # CGO_ENABLED. Nothing it builds is shipped, so it does not violate the |
| 122 | # cgo-free binary rule, which is about what gets deployed. |
| 123 | test-race: |
| 124 | go test -race -count=1 ./internal/home/ ./internal/operator/ ./internal/operatorlaunch/ ./internal/provisioner/ ./internal/home/directorydata/ |
| 125 | cd handoff && CGO_ENABLED=1 go test -race -count=1 ./... |
| 126 | cd roster && CGO_ENABLED=1 go test -race -count=1 ./... |
| 127 | cd connect && CGO_ENABLED=1 go test -race -count=1 ./... |
| 128 | |
| 129 | # The browser checks, in a real Chromium (RASTRILLO_CHROME, on PATH, or |
| 130 | # the newest in a Playwright cache: Linux's ~/.cache or macOS's |
| 131 | # ~/Library/Caches). Deliberately NOT part of `test`: the gate CI runs on |
| 132 | # every push must not need a browser. Run it before shipping a change to |
| 133 | # the CSS or to rastrillo's pin — TestNoZoomOnTap is the iOS zoom-on-tap |
| 134 | # check. Every test in the package runs, the browser ones (the `browser` |
| 135 | # tag) with them, so none is left out by a -run filter that predates it. |
| 136 | # test-vet vets these files with the tag, so they keep compiling. |
| 137 | test-browser: |
| 138 | CGO_ENABLED=0 go test -tags browser -count=1 ./internal/home/ |
| 139 | |
| 140 | .PHONY: credits |
| 141 | credits: |
| 142 | @set -eu; notices_tmp=$$(mktemp -d); trap 'rm -rf "$$notices_tmp"' EXIT; \ |
| 143 | GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOHOSTARCH) CGO_ENABLED=0 \ |
| 144 | go build -o "$$notices_tmp/notices" amadan.net/oficina/estilo.git/cmd/oficina-notices; \ |
| 145 | GOOS=linux GOARCH=arm64 CGO_ENABLED=0 "$$notices_tmp/notices" \ |
| 146 | -package ./cmd/home -lucide -onest \ |
| 147 | -exclude amadan.net/oficina/estilo.git,amadan.net/oficina/home/handoff,amadan.net/oficina/home/roster \ |
| 148 | > "$$notices_tmp/notices.json"; \ |
| 149 | mv "$$notices_tmp/notices.json" internal/home/credits/notices.json |
| 150 | |