Files
foodster/Makefile
T
KessinenandEsa Kataja 174652778b Release: leftovers, the real dish list, and a day list that stays put (#2)
Tähteet — leftovers. Not a dish: it exists so a day can be recorded as "we ate what was already there" without inventing a meal nobody cooked. A special-flagged row created by migration 0002; never in the catalog, never editable, and excluded from the stage-2 suggester outright. It gets its own mark, a grey lidded tub — which exposed a bug where no categories drew the same icon as several.

The real dish list. The seed bundle is now your December 2024 list: 37 mains, 9 sides. Letut and Pannari dropped as not-dinners, Kanakintut folded into Broilerin koipireidet, has_sides assigned by rule.

The day list stays put. Choosing a day used to swap a panel in above the list and drop that day out of it, so rows below jumped up under the tap. The list is now the page; the selected day expands where it sits, with anchors so the viewport lands on the day rather than the top.

Release pipeline fixed. The last release silently pushed the previous image: release declared image and push as prerequisites and your make runs -j16, so they raced. Also, push re-derived the tag by date-sorting, which is ambiguous when two tags share a commit. It now reads what image recorded, and verifies afterwards that the registry serves what was built.

Co-authored-by: Esa Kataja <[email protected]>
Reviewed-on: #2
2026-09-05 20:24:02 +00:00

151 lines
5.8 KiB
Makefile

# Foodster. Run `make` for the target list.
COMPOSE ?= podman compose
BIN := foodster
PKG := ./cmd/foodster
STATIC := cmd/foodster/static
# What `make image` last built. push reads it rather than re-deriving the tag:
# sorting tags by date is ambiguous when two point at the same commit, and
# re-deriving is what let a parallel make push the wrong one.
TAGFILE := .release-tag
# Vendored Datastar client. Bump, run `make vendor`, commit the result.
DATASTAR_VERSION ?= v1.0.3
SEED ?= seeds/testi.json
# Registry coordinates, shared password and TZ live here. Gitignored.
ifneq (,$(wildcard .env))
include .env
export
endif
# Generated templ output is excluded — it is not ours to format.
GOFILES = $(shell find . -name '*.go' -not -name '*_templ.go' 2>/dev/null)
.DEFAULT_GOAL := help
.PHONY: help generate build run seed test smoke check lint fix icons vendor image push release up down logs clean
help: ## Show this help
@grep -hE '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) \
| awk -F':.*## ' '{printf " \033[1m%-9s\033[0m %s\n", $$1, $$2}'
generate: ## Generate Go from .templ files
go tool templ generate
build: generate ## Build ./foodster
CGO_ENABLED=0 go build -trimpath \
-ldflags="-s -w -X main.version=dev" -o $(BIN) $(PKG)
run: generate ## Run locally on :8080 (database in ./data)
FOODSTER_PASSWORD=$${FOODSTER_PASSWORD:-dev} go run $(PKG)
seed: ## Import a dish bundle (SEED=seeds/testi.json)
go run $(PKG) -import $(SEED)
test: generate ## Run unit tests
go test ./...
smoke: generate ## End-to-end check: auth, static assets, bundle import
./scripts/smoke.sh
# Sub-makes rather than prerequisites: these must run in order even under
# `make -j`, and a parallel run vets generated code that is being rewritten.
check: ## Everything that must pass before a commit
@$(MAKE) --no-print-directory lint
@$(MAKE) --no-print-directory test
@$(MAKE) --no-print-directory smoke
@echo "check: all passed"
icons: ## Rasterise home-screen PNGs from assets/icon.svg and optimise them
rsvg-convert -w 180 -h 180 assets/icon.svg -o $(STATIC)/apple-touch-icon.png
rsvg-convert -w 192 -h 192 assets/icon.svg -o $(STATIC)/icon-192.png
rsvg-convert -w 512 -h 512 assets/icon.svg -o $(STATIC)/icon-512.png
# oxipng -o max alone loses to optipng on the 512; --zopfli wins at every
# size. Slow, but these are three tiny files built by hand.
oxipng -o max --zopfli --quiet \
$(STATIC)/apple-touch-icon.png $(STATIC)/icon-192.png $(STATIC)/icon-512.png
@ls -l $(STATIC)/*.png
vendor: ## Re-download the Datastar client (DATASTAR_VERSION=v1.0.3)
curl -sSfL -o $(STATIC)/datastar.js \
"https://cdn.jsdelivr.net/gh/starfederation/datastar@$(DATASTAR_VERSION)/bundles/datastar.js"
@head -1 $(STATIC)/datastar.js
lint: generate ## go vet, gofmt check, golangci-lint when installed
go vet ./...
@bad=$$(gofmt -l $(GOFILES) 2>/dev/null); \
if [ -n "$$bad" ]; then echo "gofmt needed:"; echo "$$bad"; exit 1; fi
@if command -v golangci-lint >/dev/null 2>&1; then golangci-lint run; \
else echo "golangci-lint not installed - skipped"; fi
fix: ## Format Go and templ sources, tidy go.mod
@if [ -n "$(GOFILES)" ]; then gofmt -w $(GOFILES); fi
go tool templ fmt .
go mod tidy
image: ## Build and tag an image as vYYYYMMDD-N. Creates a git tag.
@test -n "$(FOODSTER_REPO)" || { echo "set FOODSTER_REPO in .env"; exit 1; }
@# A release tag must point into main, or the tag records a commit that
@# was never released.
@branch=$$(git symbolic-ref --short HEAD); \
if [ "$$branch" != "main" ]; then \
echo "releases are cut from main, not $$branch:"; \
echo " git switch main && git merge --ff-only dev"; \
exit 1; \
fi
@day=$$(date +%Y%m%d); \
tag="v$$day-$$(( $$(git tag -l "v$$day-*" | wc -l) + 1 ))"; \
echo "==> $$tag"; \
git tag "$$tag"; \
podman build --platform linux/amd64 --build-arg VERSION="$$tag" \
-t "$(FOODSTER_REPO):$$tag" -t "$(FOODSTER_REPO):latest" . ; \
echo "$$tag" > $(TAGFILE)
# Pushing reported success while uploading the previous release once, because
# nothing compared what was built against what arrived. So afterwards, ask the
# registry what it actually serves for each tag and fail if it is not the
# image we just built.
push: ## Push the newest tag and :latest, then verify the registry
@test -n "$(FOODSTER_REPO)" || { echo "set FOODSTER_REPO in .env"; exit 1; }
@test -f $(TAGFILE) || { echo "nothing built - run make image"; exit 1; }; \
tag=$$(cat $(TAGFILE)); \
built=$$(podman image inspect "$(FOODSTER_REPO):$$tag" --format '{{.Id}}' 2>/dev/null) || \
{ echo "no local image tagged $$tag - run make image"; exit 1; }; \
podman push "$(FOODSTER_REPO):$$tag"; \
podman push "$(FOODSTER_REPO):latest"; \
echo "==> verifying $$tag"; \
for ref in "$$tag" latest; do \
podman pull -q "$(FOODSTER_REPO):$$ref" >/dev/null 2>&1 || \
{ echo " FAIL $$ref is not in the registry"; exit 1; }; \
served=$$(podman image inspect "$(FOODSTER_REPO):$$ref" --format '{{.Id}}'); \
if [ "$$served" != "$$built" ]; then \
echo " FAIL $$ref serves $$served"; \
echo " expected $$built"; \
exit 1; \
fi; \
echo " ok $$ref"; \
done
# Sub-makes, not prerequisites. Under `make -j` — and -j16 is the default on
# at least one machine here — these run concurrently, so push resolves the
# newest tag and uploads :latest before image has finished building and
# tagging. That silently ships the previous release a second time.
release: ## Build, tag and push in one go
@$(MAKE) --no-print-directory image
@$(MAKE) --no-print-directory push
up: ## Start the stack
@mkdir -p data # or the engine creates it root-owned and the app cannot write
$(COMPOSE) up -d
down: ## Stop the stack
$(COMPOSE) down
logs: ## Follow app logs
$(COMPOSE) logs -f app
clean: ## Remove the binary and generated templates
rm -f $(BIN)
find . -name '*_templ.go' -delete