diff --git a/Makefile b/Makefile index 600cd9f..e2e223f 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,48 @@ ifneq (,$(wildcard ./.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:" @@ -46,7 +88,8 @@ test-integration: @echo "Building test containers..." podman compose --profile tests build @echo "Starting application containers..." - podman compose up -d db app + $(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..."; \ @@ -71,27 +114,31 @@ test-all: test test-integration # Rebuild app container only (preserve DB, with cache) rebuild-app: @echo "Rebuilding app container (database stays running)..." - podman compose up --build --force-recreate -d app + $(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 - podman compose up --force-recreate -d 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)..." - podman compose up --build --force-recreate -d + $(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 - podman compose up --force-recreate -d + $(call compose_up,--force-recreate) + $(ensure_healthy) @echo "✓ All containers rebuilt and restarted" # Rebuild all containers (remove DB, no cache) @@ -99,7 +146,8 @@ rebuild-force-db: @echo "Force rebuilding all containers (database will be DELETED, no cache)..." podman compose down -v podman compose build --no-cache - podman compose up --force-recreate -d + $(call compose_up,--force-recreate) + $(ensure_healthy) @echo "✓ All containers rebuilt and restarted" # Stop and remove containers @@ -108,7 +156,8 @@ clean: # Quick start (if already built) up: - podman compose up -d + $(compose_up) + $(ensure_healthy) # Stop all containers (alias for clean) down: @@ -131,7 +180,12 @@ logs: # 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 podman compose up --build --force-recreate -d + @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