feat(deploy): split compose into prod base + dev override

Restructure the container setup to support registry-based deployment:
the default docker-compose.yml now pulls a prebuilt app image from the
Gitea container registry instead of building locally, while a new
docker-compose.dev.yml override preserves the local build + integration
test workflow for development.

Why:
- Production and self-hosting should consume a published image, not
  rebuild from source on the host. The default `docker compose up` now
  pulls the app image (git.linuxhg.com/bookhoard/bookhoard) alongside the
  public postgres image, with no build step required.
- Development still needs to build from source and run integration
  tests, so those concerns move to an override file the Makefile applies.
  Shared config (env, volumes, ports, healthchecks) lives in one place to
  avoid drift between environments.

Changes:
- docker-compose.yml (prod base): the app service now references
  `image: git.linuxhg.com/bookhoard/bookhoard:${IMAGE_TAG:-latest}` instead
  of a build context. The tests service is removed (moved to the
  override). IMAGE_TAG lets deployers pin or roll back a specific version.
- docker-compose.dev.yml (new override): adds the local `build:` context
  for the app and defines the integration `tests` service (profile-gated).
  Everything else is inherited from the base file via compose merging.
- Makefile: introduce a COMPOSE variable that merges the base and
  override (`-f docker-compose.yml -f docker-compose.dev.yml`); all dev
  targets now use it. Plain `docker compose` against the base file only
  remains the production path.
- README: quickstart updated to pull and start prebuilt images; clone URL
  points at the Gitea instance.

The development workflow (`make rebuild-app`, `make test-integration`,
etc.) is functionally unchanged.
This commit is contained in:
2026-07-29 15:47:40 -04:00
parent 75b33fdae6
commit 76c6826920
4 changed files with 89 additions and 69 deletions
+24 -20
View File
@@ -11,6 +11,10 @@ endif
# 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:"
@@ -48,9 +52,9 @@ test:
# Run integration tests in containers (matches production environment)
test-integration:
@echo "Building test containers..."
$(CONTAINER_RUNTIME) compose --profile tests build
$(COMPOSE) --profile tests build
@echo "Starting application containers..."
$(CONTAINER_RUNTIME) compose up -d db app
$(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..."; \
@@ -64,7 +68,7 @@ test-integration:
echo " ✓ Application is ready"
@echo ""
@echo "Running integration tests in container..."
$(CONTAINER_RUNTIME) compose --profile tests run --rm tests
$(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."
@@ -75,67 +79,67 @@ test-all: test test-integration
# Rebuild app container only (preserve DB, with cache)
rebuild-app:
@echo "Rebuilding app container (database stays running)..."
$(CONTAINER_RUNTIME) compose up --build --force-recreate -d app
$(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)..."
$(CONTAINER_RUNTIME) compose build --no-cache app
$(CONTAINER_RUNTIME) compose up --force-recreate -d app
$(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)..."
$(CONTAINER_RUNTIME) compose up --build --force-recreate -d
$(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)..."
$(CONTAINER_RUNTIME) compose build --no-cache
$(CONTAINER_RUNTIME) compose up --force-recreate -d
$(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)..."
$(CONTAINER_RUNTIME) compose down -v
$(CONTAINER_RUNTIME) compose build --no-cache
$(CONTAINER_RUNTIME) compose up --force-recreate -d
$(COMPOSE) down -v
$(COMPOSE) build --no-cache
$(COMPOSE) up --force-recreate -d
@echo "✓ All containers rebuilt and restarted"
# Stop and remove containers
clean:
$(CONTAINER_RUNTIME) compose down -v
$(COMPOSE) down -v
# Quick start (if already built)
up:
$(CONTAINER_RUNTIME) compose up -d
$(COMPOSE) up -d
# Stop all containers (alias for clean)
down:
$(CONTAINER_RUNTIME) compose down
$(COMPOSE) down
# Restart app container (preserves database)
restart:
@echo "Restarting app container (database stays running)..."
$(CONTAINER_RUNTIME) compose restart app
$(COMPOSE) restart app
@echo "✓ App container restarted"
# Show container status
ps:
$(CONTAINER_RUNTIME) compose ps
$(COMPOSE) ps
# Show container logs
logs:
$(CONTAINER_RUNTIME) compose logs -f
$(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 $(CONTAINER_RUNTIME) compose up --build --force-recreate -d
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
@@ -144,7 +148,7 @@ test-env-up:
# Stop test environment
test-env-down:
$(CONTAINER_RUNTIME) compose down -v
$(COMPOSE) down -v
# Verify project guidelines compliance
verify-guidelines: