.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 # Include .env file for environment variables (single source of truth) # Ignore if .env doesn't exist yet ifneq (,$(wildcard ./.env)) include .env export endif # On non-systemd systems (e.g., Void Linux with runit), podman healthchecks are # never triggered automatically because podman relies on systemd timers. # Additionally, podman-compose up -d blocks forever waiting for service_healthy # conditions that never resolve. This macro: # 1. Waits for the DB to accept connections, triggers its healthcheck # 2. Starts the app container, waits for it, triggers its healthcheck # On systemd systems, the runtime check skips everything. # See: https://github.com/containers/podman/pull/27033 define ensure_healthy @if [ ! -d /run/systemd/system ]; then \ echo "Non-systemd: waiting for database..."; \ i=0; \ until podman exec bookhoard_db pg_isready -U postgres >/dev/null 2>&1; do \ i=$$((i+1)); if [ $$i -gt 30 ]; then echo " Database failed to start"; exit 1; fi; \ sleep 2; \ done; \ podman healthcheck run bookhoard_db >/dev/null 2>&1 || true; \ podman start bookhoard 2>/dev/null || true; \ echo "Non-systemd: waiting for application..."; \ i=0; \ until podman exec bookhoard curl -sf http://localhost:8765/health >/dev/null 2>&1; do \ i=$$((i+1)); if [ $$i -gt 30 ]; then echo " Application failed to start"; exit 1; fi; \ sleep 2; \ done; \ podman healthcheck run bookhoard >/dev/null 2>&1 || true; \ echo " Services healthy"; \ fi endef # podman-compose up -d blocks forever on non-systemd waiting for service_healthy. # This macro runs compose with a timeout on non-systemd to let it create containers # without hanging. Pass compose flags via the COMPOSE_UP_ARGS variable. # Usage: $(call compose_up,--build --force-recreate) # On systemd systems, the timeout is skipped. define compose_up @if [ -d /run/systemd/system ]; then \ podman compose up $1 -d; \ else \ timeout 15 podman compose up $1 -d || true; \ fi endef # 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" # 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..." podman compose --profile tests build @echo "Starting application containers..." $(call compose_up,db app) $(ensure_healthy) @echo "Waiting for services to be healthy..." @until podman 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 podman 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..." podman 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)..." $(call compose_up,--build --force-recreate app) $(ensure_healthy) @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)..." podman compose build --no-cache app $(call compose_up,--force-recreate app) $(ensure_healthy) @echo "✓ App container rebuilt and restarted" # Rebuild all containers (preserve DB, with cache) rebuild: @echo "Rebuilding all containers (database preserved)..." $(call compose_up,--build --force-recreate) $(ensure_healthy) @echo "✓ All containers rebuilt and restarted" # Rebuild all containers (preserve DB, no cache) rebuild-force: @echo "Force rebuilding all containers (database preserved, no cache)..." podman compose build --no-cache $(call compose_up,--force-recreate) $(ensure_healthy) @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)..." podman compose down -v podman compose build --no-cache $(call compose_up,--force-recreate) $(ensure_healthy) @echo "✓ All containers rebuilt and restarted" # Stop and remove containers clean: podman compose down -v # Quick start (if already built) up: $(compose_up) $(ensure_healthy) # Stop all containers (alias for clean) down: podman compose down # Restart app container (preserves database) restart: @echo "Restarting app container (database stays running)..." podman compose restart app @echo "✓ App container restarted" # Show container status ps: podman compose ps # Show container logs logs: podman compose logs -f # Start containers with test mode enabled for manual testing test-env-up: @echo "Starting containers with test mode enabled..." @if [ -d /run/systemd/system ]; then \ TEST_MODE=true RATE_LIMIT_ENABLED=false REQUESTS_PER_MINUTE=1000 podman compose up --build --force-recreate -d; \ else \ TEST_MODE=true RATE_LIMIT_ENABLED=false REQUESTS_PER_MINUTE=1000 timeout 15 podman compose up --build --force-recreate -d || true; \ fi $(ensure_healthy) @echo "Waiting for services to be ready..." @until podman exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do sleep 1; done @until podman 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: podman 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