refactor(makefile): auto-detect container runtime, remove systemd workarounds

Replace hardcoded 'podman' with auto-detected CONTAINER_RUNTIME variable
that prefers docker and falls back to podman. Override with:
  CONTAINER_RUNTIME=podman make rebuild-app

Remove the ensure_healthy and compose_up macros that worked around
podman-compose hanging on non-systemd systems (e.g., Void Linux with
runt). These are no longer needed — healthchecks are now handled by
plain wait loops in the targets themselves, and compose up -d no longer
blocks on health conditions.
This commit is contained in:
2026-05-24 20:23:23 -04:00
parent 0af940c7b3
commit fb9427a864
+27 -77
View File
@@ -7,47 +7,9 @@ ifneq (,$(wildcard ./.env))
export export
endif endif
# On non-systemd systems (e.g., Void Linux with runit), podman healthchecks are # Auto-detect container runtime: prefer docker, fall back to podman
# never triggered automatically because podman relies on systemd timers. # Override with: CONTAINER_RUNTIME=podman make rebuild-app
# Additionally, podman-compose up -d blocks forever waiting for service_healthy CONTAINER_RUNTIME ?= $(shell command -v docker 2>/dev/null || command -v podman 2>/dev/null)
# 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 # Default target
help: help:
@@ -86,24 +48,23 @@ test:
# Run integration tests in containers (matches production environment) # Run integration tests in containers (matches production environment)
test-integration: test-integration:
@echo "Building test containers..." @echo "Building test containers..."
podman compose --profile tests build $(CONTAINER_RUNTIME) compose --profile tests build
@echo "Starting application containers..." @echo "Starting application containers..."
$(call compose_up,db app) $(CONTAINER_RUNTIME) compose up -d db app
$(ensure_healthy)
@echo "Waiting for services to be healthy..." @echo "Waiting for services to be healthy..."
@until podman exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do \ @until $(CONTAINER_RUNTIME) exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do \
echo " Database not ready yet..."; \ echo " Database not ready yet..."; \
sleep 2; \ sleep 2; \
done; \ done; \
echo " ✓ Database is ready" echo " ✓ Database is ready"
@until podman exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do \ @until $(CONTAINER_RUNTIME) exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do \
echo " Application not ready yet..."; \ echo " Application not ready yet..."; \
sleep 2; \ sleep 2; \
done; \ done; \
echo " ✓ Application is ready" echo " ✓ Application is ready"
@echo "" @echo ""
@echo "Running integration tests in container..." @echo "Running integration tests in container..."
podman compose --profile tests run --rm tests $(CONTAINER_RUNTIME) compose --profile tests run --rm tests
@echo "" @echo ""
@echo "✅ Integration tests completed!" @echo "✅ Integration tests completed!"
@echo "📝 Containers are still running. Use 'make logs' to view logs or 'make clean' to stop." @echo "📝 Containers are still running. Use 'make logs' to view logs or 'make clean' to stop."
@@ -114,87 +75,76 @@ test-all: test test-integration
# Rebuild app container only (preserve DB, with cache) # Rebuild app container only (preserve DB, with cache)
rebuild-app: rebuild-app:
@echo "Rebuilding app container (database stays running)..." @echo "Rebuilding app container (database stays running)..."
$(call compose_up,--build --force-recreate app) $(CONTAINER_RUNTIME) compose up --build --force-recreate -d app
$(ensure_healthy)
@echo "✓ App container rebuilt and restarted" @echo "✓ App container rebuilt and restarted"
# Rebuild app container only (preserve DB, no cache) # Rebuild app container only (preserve DB, no cache)
rebuild-app-force: rebuild-app-force:
@echo "Force rebuilding app container (database stays running, no cache)..." @echo "Force rebuilding app container (database stays running, no cache)..."
podman compose build --no-cache app $(CONTAINER_RUNTIME) compose build --no-cache app
$(call compose_up,--force-recreate app) $(CONTAINER_RUNTIME) compose up --force-recreate -d app
$(ensure_healthy)
@echo "✓ App container rebuilt and restarted" @echo "✓ App container rebuilt and restarted"
# Rebuild all containers (preserve DB, with cache) # Rebuild all containers (preserve DB, with cache)
rebuild: rebuild:
@echo "Rebuilding all containers (database preserved)..." @echo "Rebuilding all containers (database preserved)..."
$(call compose_up,--build --force-recreate) $(CONTAINER_RUNTIME) compose up --build --force-recreate -d
$(ensure_healthy)
@echo "✓ All containers rebuilt and restarted" @echo "✓ All containers rebuilt and restarted"
# Rebuild all containers (preserve DB, no cache) # Rebuild all containers (preserve DB, no cache)
rebuild-force: rebuild-force:
@echo "Force rebuilding all containers (database preserved, no cache)..." @echo "Force rebuilding all containers (database preserved, no cache)..."
podman compose build --no-cache $(CONTAINER_RUNTIME) compose build --no-cache
$(call compose_up,--force-recreate) $(CONTAINER_RUNTIME) compose up --force-recreate -d
$(ensure_healthy)
@echo "✓ All containers rebuilt and restarted" @echo "✓ All containers rebuilt and restarted"
# Rebuild all containers (remove DB, no cache) # Rebuild all containers (remove DB, no cache)
rebuild-force-db: rebuild-force-db:
@echo "Force rebuilding all containers (database will be DELETED, no cache)..." @echo "Force rebuilding all containers (database will be DELETED, no cache)..."
podman compose down -v $(CONTAINER_RUNTIME) compose down -v
podman compose build --no-cache $(CONTAINER_RUNTIME) compose build --no-cache
$(call compose_up,--force-recreate) $(CONTAINER_RUNTIME) compose up --force-recreate -d
$(ensure_healthy)
@echo "✓ All containers rebuilt and restarted" @echo "✓ All containers rebuilt and restarted"
# Stop and remove containers # Stop and remove containers
clean: clean:
podman compose down -v $(CONTAINER_RUNTIME) compose down -v
# Quick start (if already built) # Quick start (if already built)
up: up:
$(compose_up) $(CONTAINER_RUNTIME) compose up -d
$(ensure_healthy)
# Stop all containers (alias for clean) # Stop all containers (alias for clean)
down: down:
podman compose down $(CONTAINER_RUNTIME) compose down
# Restart app container (preserves database) # Restart app container (preserves database)
restart: restart:
@echo "Restarting app container (database stays running)..." @echo "Restarting app container (database stays running)..."
podman compose restart app $(CONTAINER_RUNTIME) compose restart app
@echo "✓ App container restarted" @echo "✓ App container restarted"
# Show container status # Show container status
ps: ps:
podman compose ps $(CONTAINER_RUNTIME) compose ps
# Show container logs # Show container logs
logs: logs:
podman compose logs -f $(CONTAINER_RUNTIME) compose logs -f
# Start containers with test mode enabled for manual testing # Start containers with test mode enabled for manual testing
test-env-up: test-env-up:
@echo "Starting containers with test mode enabled..." @echo "Starting containers with test mode enabled..."
@if [ -d /run/systemd/system ]; then \ TEST_MODE=true RATE_LIMIT_ENABLED=false REQUESTS_PER_MINUTE=1000 $(CONTAINER_RUNTIME) compose up --build --force-recreate -d
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..." @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 $(CONTAINER_RUNTIME) 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 @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 "✓ Test environment is ready!"
@echo "Application available at http://localhost:8765" @echo "Application available at http://localhost:8765"
# Stop test environment # Stop test environment
test-env-down: test-env-down:
podman compose down -v $(CONTAINER_RUNTIME) compose down -v
# Verify project guidelines compliance # Verify project guidelines compliance
verify-guidelines: verify-guidelines: