.PHONY: help test test-integration test-all rebuild rebuild-force rebuild-app rebuild-app-force rebuild-force-db clean restart up down logs ps test-env-up test-env-down verify-guidelines verify-quick release # Include .env file for environment variables (single source of truth) # Ignore if .env doesn't exist yet ifneq (,$(wildcard ./.env)) include .env export endif # Auto-detect container runtime: prefer docker, fall back to podman # Override with: CONTAINER_RUNTIME=podman make rebuild-app CONTAINER_RUNTIME ?= $(shell command -v docker 2>/dev/null || command -v podman 2>/dev/null) # Dev compose stack: base prod file merged with the dev override (local build + tests). # Prod deploy does NOT use this — it runs plain `docker compose` against the base file only. COMPOSE := $(CONTAINER_RUNTIME) compose -f docker-compose.yml -f docker-compose.dev.yml # Default target help: @echo "Available targets:" @echo "" @echo "Development:" @echo " make up - Start containers (if already built)" @echo " make rebuild - Rebuild all containers (database preserved)" @echo " make rebuild-force - Rebuild all containers, no cache (database preserved)" @echo " make rebuild-app - Rebuild app container only (fast, database preserved)" @echo " make rebuild-app-force - Rebuild app container only, no cache (database preserved)" @echo " make rebuild-force-db - Rebuild all containers, no cache (DELETES database)" @echo " make restart - Restart app container (preserves db)" @echo " make down - Stop all containers (preserves volumes)" @echo " make clean - Stop and remove all containers and volumes" @echo "" @echo "Status & Logs:" @echo " make ps - Show container status" @echo " make logs - Show container logs (follow mode)" @echo "" @echo "Testing:" @echo " make test - Run unit tests (from host, fast)" @echo " make test-integration - Run integration tests (in containers, matches prod)" @echo " make test-all - Run all tests (unit + integration)" @echo " make test-env-up - Start containers with test mode enabled" @echo " make test-env-down - Stop test environment" @echo "" @echo "Verification:" @echo " make verify-guidelines - Run comprehensive guidelines check" @echo " make verify-quick - Run quick guidelines check" @echo "" @echo "Release:" @echo " ./release v0.3.0 - Tag, push, and release (notes auto-generated from commits)" # Run unit tests locally (fast, no containers) test: go test ./internal/... -v -short # Run integration tests in containers (matches production environment) test-integration: @echo "Building test containers..." $(COMPOSE) --profile tests build @echo "Starting application containers..." $(COMPOSE) up -d db app @echo "Waiting for services to be healthy..." @until $(CONTAINER_RUNTIME) exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do \ echo " Database not ready yet..."; \ sleep 2; \ done; \ echo " ✓ Database is ready" @until $(CONTAINER_RUNTIME) exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do \ echo " Application not ready yet..."; \ sleep 2; \ done; \ echo " ✓ Application is ready" @echo "" @echo "Running integration tests in container..." $(COMPOSE) --profile tests run --rm tests @echo "" @echo "✅ Integration tests completed!" @echo "📝 Containers are still running. Use 'make logs' to view logs or 'make clean' to stop." # Run all tests test-all: test test-integration # Rebuild app container only (preserve DB, with cache) rebuild-app: @echo "Rebuilding app container (database stays running)..." $(COMPOSE) up --build --force-recreate -d app @echo "✓ App container rebuilt and restarted" # Rebuild app container only (preserve DB, no cache) rebuild-app-force: @echo "Force rebuilding app container (database stays running, no cache)..." $(COMPOSE) build --no-cache app $(COMPOSE) up --force-recreate -d app @echo "✓ App container rebuilt and restarted" # Rebuild all containers (preserve DB, with cache) rebuild: @echo "Rebuilding all containers (database preserved)..." $(COMPOSE) up --build --force-recreate -d @echo "✓ All containers rebuilt and restarted" # Rebuild all containers (preserve DB, no cache) rebuild-force: @echo "Force rebuilding all containers (database preserved, no cache)..." $(COMPOSE) build --no-cache $(COMPOSE) up --force-recreate -d @echo "✓ All containers rebuilt and restarted" # Rebuild all containers (remove DB, no cache) rebuild-force-db: @echo "Force rebuilding all containers (database will be DELETED, no cache)..." $(COMPOSE) down -v $(COMPOSE) build --no-cache $(COMPOSE) up --force-recreate -d @echo "✓ All containers rebuilt and restarted" # Stop and remove containers clean: $(COMPOSE) down -v # Quick start (if already built) up: $(COMPOSE) up -d # Stop all containers (alias for clean) down: $(COMPOSE) down # Restart app container (preserves database) restart: @echo "Restarting app container (database stays running)..." $(COMPOSE) restart app @echo "✓ App container restarted" # Show container status ps: $(COMPOSE) ps # Show container logs logs: $(COMPOSE) logs -f # Start containers with test mode enabled for manual testing test-env-up: @echo "Starting containers with test mode enabled..." TEST_MODE=true RATE_LIMIT_ENABLED=false REQUESTS_PER_MINUTE=1000 $(COMPOSE) up --build --force-recreate -d @echo "Waiting for services to be ready..." @until $(CONTAINER_RUNTIME) exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do sleep 1; done @until $(CONTAINER_RUNTIME) exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do sleep 1; done @echo "✓ Test environment is ready!" @echo "Application available at http://localhost:8765" # Stop test environment test-env-down: $(COMPOSE) down -v # Verify project guidelines compliance verify-guidelines: @echo "Running comprehensive project guidelines verification..." @./scripts/verify-guidelines.sh verify-quick: @echo "Running quick project guidelines verification..." @./scripts/verify-quick.sh # Create an annotated version tag carrying auto-generated release notes (git-cliff) # and push it. The tag push triggers .gitea/workflows/release.yml, which builds the # image and publishes a Gitea Release whose body is this tag's message. Notes come # entirely from Conventional Commits — no hand-written message required. # # git-cliff's --latest needs the tag to exist to scope the notes, so we create a # throwaway lightweight tag, generate the notes, replace it with an annotated tag, # then push. --cleanup=verbatim keeps the markdown "###" group headers (git's # default cleanup would strip lines starting with "#"). # # Requires git-cliff: https://git-cliff.org/install # Usage: make release VERSION=v0.3.0 release: @test -n "$(VERSION)" || { echo "Usage: make release VERSION=v0.3.0"; exit 1; } @command -v git-cliff >/dev/null 2>&1 || { echo "git-cliff not found — install: https://git-cliff.org/install"; exit 1; } @if git rev-parse "$(VERSION)" >/dev/null 2>&1; then echo "Tag $(VERSION) already exists locally — delete it first: git tag -d $(VERSION)"; exit 1; fi @echo "Generating release notes for $(VERSION)..." @git tag "$(VERSION)" HEAD && \ (git cliff --latest --config cliff.toml > .release-notes.tmp && git tag -d "$(VERSION)" >/dev/null) || \ { git tag -d "$(VERSION)" >/dev/null 2>&1; rm -f .release-notes.tmp; echo "git-cliff failed"; exit 1; } @git tag -a --cleanup=verbatim -F .release-notes.tmp "$(VERSION)" HEAD && rm -f .release-notes.tmp @git push origin "$(VERSION)" @echo "Pushed $(VERSION) — Gitea Actions will build the image and publish the Release."