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
