feat: implement docker infrastructure (US-001)
- Add docker-compose.yml with postgres, redis, api, and web services - Add multi-stage Dockerfile for API (Python 3.11) - Add multi-stage Dockerfile for web (Node.js 20 + nginx) - Add Makefile with common development commands - Add .env.example with all required environment variables - Add placeholder pyproject.toml and package.json for builds - Configure health checks for all services - Setup persistent volumes for postgres, redis, and repos - Run services as non-root users
This commit is contained in:
@@ -0,0 +1,726 @@
|
||||
# Docker Infrastructure Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Set up complete Docker-based development environment with PostgreSQL, Redis, Traefik, Authentik, FastAPI backend, and React frontend.
|
||||
|
||||
**Architecture:** Multi-service Docker Compose setup with Traefik as reverse proxy, PostgreSQL for data, Redis for caching, Authentik for auth, FastAPI backend, and Vite React frontend. All services include health checks and persistent volumes.
|
||||
|
||||
**Tech Stack:** Docker 24.0+, Docker Compose 2.20+, Make, Python 3.11+, Node.js 20+
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
/
|
||||
├── docker-compose.yml # All services orchestration
|
||||
├── .env.example # Required environment variables
|
||||
├── Makefile # Common commands
|
||||
├── apps/
|
||||
│ ├── api/
|
||||
│ │ ├── Dockerfile # Multi-stage Python build
|
||||
│ │ └── pyproject.toml # Python dependencies (placeholder)
|
||||
│ └── web/
|
||||
│ ├── Dockerfile # Multi-stage Node build
|
||||
│ └── package.json # Node dependencies (placeholder)
|
||||
└── data/
|
||||
└── repos/ # Git repository storage volume
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Create Project Directory Structure
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/api/`
|
||||
- Create: `apps/web/`
|
||||
- Create: `data/repos/`
|
||||
|
||||
- [ ] **Step 1: Create directory structure**
|
||||
|
||||
```bash
|
||||
mkdir -p apps/api apps/web data/repos
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Create placeholder files for Docker build context**
|
||||
|
||||
Create `apps/api/pyproject.toml`:
|
||||
```toml
|
||||
[project]
|
||||
name = "headquarter-api"
|
||||
version = "0.1.0"
|
||||
description = "Headquarter platform API"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi>=0.104.0",
|
||||
"uvicorn[standard]>=0.24.0",
|
||||
"sqlalchemy>=2.0.0",
|
||||
"asyncpg>=0.29.0",
|
||||
"alembic>=1.12.0",
|
||||
"pydantic>=2.5.0",
|
||||
"pydantic-settings>=2.1.0",
|
||||
"python-jose[cryptography]>=3.3.0",
|
||||
"python-multipart>=0.0.6",
|
||||
"httpx>=0.25.0",
|
||||
"structlog>=23.2.0",
|
||||
"cryptography>=41.0.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.4.0",
|
||||
"pytest-asyncio>=0.21.0",
|
||||
"mypy>=1.7.0",
|
||||
"ruff>=0.1.0",
|
||||
"httpx>=0.25.0",
|
||||
]
|
||||
```
|
||||
|
||||
Create `apps/web/package.json`:
|
||||
```json
|
||||
{
|
||||
"name": "headquarter-web",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.20.0",
|
||||
"axios": "^1.6.0",
|
||||
"tailwindcss": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"typescript": "^5.3.0",
|
||||
"vite": "^5.0.0",
|
||||
"eslint": "^8.55.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
"@typescript-eslint/parser": "^6.14.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"postcss": "^8.4.32"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/ data/
|
||||
git commit -m "chore: create project directory structure"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Create API Dockerfile
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/api/Dockerfile`
|
||||
|
||||
- [ ] **Step 1: Write multi-stage API Dockerfile**
|
||||
|
||||
```dockerfile
|
||||
# Build stage
|
||||
FROM python:3.11-slim as builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Install build dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies
|
||||
COPY pyproject.toml .
|
||||
RUN pip install --no-cache-dir --user -e ".[dev]"
|
||||
|
||||
# Production stage
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Create non-root user
|
||||
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libpq5 \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy dependencies from builder
|
||||
COPY --from=builder /root/.local /home/appuser/.local
|
||||
ENV PATH=/home/appuser/.local/bin:$PATH
|
||||
|
||||
# Copy application code
|
||||
COPY --chown=appuser:appgroup . .
|
||||
|
||||
# Create directories for repo storage
|
||||
RUN mkdir -p /data/repos && chown -R appuser:appgroup /data/repos
|
||||
|
||||
# Switch to non-root user
|
||||
USER appuser
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
|
||||
|
||||
# Run the application
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/api/Dockerfile
|
||||
git commit -m "feat: add multi-stage API Dockerfile"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Create Web Frontend Dockerfile
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/web/Dockerfile`
|
||||
- Create: `apps/web/nginx.conf`
|
||||
|
||||
- [ ] **Step 1: Write multi-stage Web Dockerfile**
|
||||
|
||||
```dockerfile
|
||||
# Build stage
|
||||
FROM node:20-alpine as builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy package files
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build production bundle
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
|
||||
|
||||
# Copy custom nginx config
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy built assets from builder
|
||||
COPY --from=builder --chown=nextjs:nodejs /build/dist /usr/share/nginx/html
|
||||
|
||||
# Create required directories
|
||||
RUN mkdir -p /var/cache/nginx /var/run && \
|
||||
chown -R nextjs:nodejs /var/cache/nginx /var/run /usr/share/nginx/html
|
||||
|
||||
# Switch to non-root user
|
||||
USER nextjs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
|
||||
|
||||
# Start nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Write nginx configuration**
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Enable gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
|
||||
|
||||
# Handle client-side routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/web/Dockerfile apps/web/nginx.conf
|
||||
git commit -m "feat: add multi-stage web frontend Dockerfile with nginx"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Create Docker Compose Configuration
|
||||
|
||||
**Files:**
|
||||
- Create: `docker-compose.yml`
|
||||
|
||||
- [ ] **Step 1: Write Docker Compose file**
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# PostgreSQL Database
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: hq-postgres
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-headquarter}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-headquarter}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-headquarter}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./init-scripts:/docker-entrypoint-initdb.d:ro
|
||||
ports:
|
||||
- "5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-headquarter} -d ${POSTGRES_DB:-headquarter}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
networks:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
# Redis Cache
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: hq-redis
|
||||
command: redis-server --appendonly yes
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
ports:
|
||||
- "6379:6379"
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 5s
|
||||
networks:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
# Traefik Reverse Proxy
|
||||
traefik:
|
||||
image: traefik:v3.0
|
||||
container_name: hq-traefik
|
||||
command:
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--ping=true"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8080:8080" # Dashboard
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./traefik:/etc/traefik:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "traefik", "healthcheck"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
networks:
|
||||
- frontend
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.traefik.rule=Host(`traefik.hq.local`)"
|
||||
- "traefik.http.routers.traefik.service=api@internal"
|
||||
- "traefik.http.routers.traefik.entrypoints=web"
|
||||
|
||||
# Authentik - Authentication Server
|
||||
authentik-server:
|
||||
image: ghcr.io/goauthentik/server:2024.2
|
||||
container_name: hq-authentik
|
||||
command: server
|
||||
environment:
|
||||
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-change-me-in-production}
|
||||
AUTHENTIK_REDIS__HOST: redis
|
||||
AUTHENTIK_POSTGRESQL__HOST: postgres
|
||||
AUTHENTIK_POSTGRESQL__NAME: ${POSTGRES_DB:-headquarter}
|
||||
AUTHENTIK_POSTGRESQL__USER: ${POSTGRES_USER:-headquarter}
|
||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${POSTGRES_PASSWORD:-headquarter}
|
||||
volumes:
|
||||
- ./authentik/media:/media
|
||||
- ./authentik/custom-templates:/templates
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9443:9443"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- backend
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.authentik.rule=Host(`auth.hq.local`)"
|
||||
- "traefik.http.routers.authentik.entrypoints=web"
|
||||
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
|
||||
|
||||
# API Service
|
||||
api:
|
||||
build:
|
||||
context: ./apps/api
|
||||
dockerfile: Dockerfile
|
||||
container_name: hq-api
|
||||
environment:
|
||||
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-headquarter}:${POSTGRES_PASSWORD:-headquarter}@postgres:5432/${POSTGRES_DB:-headquarter}
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
AUTHENTIK_URL: http://authentik-server:9000
|
||||
AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID}
|
||||
AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_SECRET}
|
||||
JWT_SECRET: ${JWT_SECRET:-change-me-in-production}
|
||||
REPO_BASE_PATH: /data/repos
|
||||
volumes:
|
||||
- repo_data:/data/repos
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
authentik-server:
|
||||
condition: service_started
|
||||
networks:
|
||||
- backend
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.api.rule=Host(`api.hq.local`)"
|
||||
- "traefik.http.routers.api.entrypoints=web"
|
||||
- "traefik.http.services.api.loadbalancer.server.port=8000"
|
||||
|
||||
# Web Frontend
|
||||
web:
|
||||
build:
|
||||
context: ./apps/web
|
||||
dockerfile: Dockerfile
|
||||
container_name: hq-web
|
||||
environment:
|
||||
VITE_API_URL: http://api.hq.local
|
||||
VITE_AUTH_URL: http://auth.hq.local
|
||||
depends_on:
|
||||
- api
|
||||
networks:
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.web.rule=Host(`hq.local`) || Host(`www.hq.local`)"
|
||||
- "traefik.http.routers.web.entrypoints=web"
|
||||
- "traefik.http.services.web.loadbalancer.server.port=80"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
repo_data:
|
||||
|
||||
networks:
|
||||
frontend:
|
||||
driver: bridge
|
||||
backend:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add docker-compose.yml
|
||||
git commit -m "feat: add Docker Compose with all services"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Create Environment Configuration Template
|
||||
|
||||
**Files:**
|
||||
- Create: `.env.example`
|
||||
|
||||
- [ ] **Step 1: Write environment template**
|
||||
|
||||
```bash
|
||||
# Database Configuration
|
||||
POSTGRES_USER=headquarter
|
||||
POSTGRES_PASSWORD=change-me-in-production
|
||||
POSTGRES_DB=headquarter
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
|
||||
# Authentik Configuration
|
||||
AUTHENTIK_SECRET_KEY=change-me-in-production
|
||||
AUTHENTIK_CLIENT_ID=your-authentik-client-id
|
||||
AUTHENTIK_CLIENT_SECRET=your-authentik-client-secret
|
||||
AUTHENTIK_URL=http://auth.hq.local
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET=change-me-in-production
|
||||
JWT_ALGORITHM=HS256
|
||||
JWT_EXPIRATION_HOURS=24
|
||||
|
||||
# Application Configuration
|
||||
APP_ENV=development
|
||||
DEBUG=true
|
||||
LOG_LEVEL=info
|
||||
REPO_BASE_PATH=/data/repos
|
||||
|
||||
# Frontend Configuration
|
||||
VITE_API_URL=http://api.hq.local
|
||||
VITE_AUTH_URL=http://auth.hq.local
|
||||
|
||||
# Docker Configuration
|
||||
COMPOSE_PROJECT_NAME=headquarter
|
||||
DOCKER_NETWORK=headquarter_default
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add .env.example
|
||||
git commit -m "docs: add environment configuration template"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Create Makefile
|
||||
|
||||
**Files:**
|
||||
- Create: `Makefile`
|
||||
|
||||
- [ ] **Step 1: Write Makefile**
|
||||
|
||||
```makefile
|
||||
.PHONY: help up down logs migrate test lint clean build
|
||||
|
||||
# Default target
|
||||
help:
|
||||
@echo "Headquarter Development Commands"
|
||||
@echo "================================"
|
||||
@echo "make up - Start all services"
|
||||
@echo "make down - Stop all services"
|
||||
@echo "make logs - View service logs"
|
||||
@echo "make migrate - Run database migrations"
|
||||
@echo "make test - Run test suites"
|
||||
@echo "make lint - Run linting"
|
||||
@echo "make build - Build all Docker images"
|
||||
@echo "make clean - Remove containers and volumes"
|
||||
@echo "make shell - Open shell in API container"
|
||||
|
||||
# Start services
|
||||
up:
|
||||
docker compose up -d
|
||||
@echo "Services starting..."
|
||||
@echo "API: http://api.hq.local"
|
||||
@echo "Web: http://hq.local"
|
||||
@echo "Auth: http://auth.hq.local"
|
||||
@echo "Traefik: http://traefik.hq.local:8080"
|
||||
|
||||
# Stop services
|
||||
down:
|
||||
docker compose down
|
||||
|
||||
# View logs
|
||||
logs:
|
||||
docker compose logs -f
|
||||
|
||||
# View specific service logs
|
||||
logs-api:
|
||||
docker compose logs -f api
|
||||
|
||||
logs-web:
|
||||
docker compose logs -f web
|
||||
|
||||
logs-db:
|
||||
docker compose logs -f postgres
|
||||
|
||||
# Run database migrations
|
||||
migrate:
|
||||
docker compose exec api alembic upgrade head
|
||||
|
||||
# Create new migration
|
||||
migration:
|
||||
docker compose exec api alembic revision --autogenerate -m "$(message)"
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
docker compose exec api pytest -v
|
||||
|
||||
# Run linting
|
||||
lint:
|
||||
docker compose exec api ruff check .
|
||||
docker compose exec api mypy .
|
||||
cd apps/web && npm run lint
|
||||
|
||||
# Type checking
|
||||
typecheck:
|
||||
docker compose exec api mypy .
|
||||
cd apps/web && npm run typecheck
|
||||
|
||||
# Build all images
|
||||
build:
|
||||
docker compose build
|
||||
|
||||
# Build specific service
|
||||
build-api:
|
||||
docker compose build api
|
||||
|
||||
build-web:
|
||||
docker compose build web
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
docker compose down -v --remove-orphans
|
||||
docker system prune -f
|
||||
|
||||
# Open shell in API container
|
||||
shell:
|
||||
docker compose exec api /bin/sh
|
||||
|
||||
# Database shell
|
||||
db-shell:
|
||||
docker compose exec postgres psql -U $(POSTGRES_USER) -d $(POSTGRES_DB)
|
||||
|
||||
# Health check
|
||||
health:
|
||||
@echo "Checking service health..."
|
||||
@docker compose ps
|
||||
@docker compose exec api wget -qO- http://localhost:8000/health || echo "API health check failed"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add Makefile
|
||||
git commit -m "feat: add Makefile with common development commands"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Validate Docker Configuration
|
||||
|
||||
**Files:**
|
||||
- Test: `docker-compose.yml`
|
||||
|
||||
- [ ] **Step 1: Validate Docker Compose syntax**
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Expected: Valid YAML output with all services configured
|
||||
|
||||
- [ ] **Step 2: Test build**
|
||||
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
|
||||
Expected: Both API and web images build successfully (may warn about missing source files - that's OK)
|
||||
|
||||
- [ ] **Step 3: Test start/stop**
|
||||
|
||||
```bash
|
||||
make up
|
||||
sleep 10
|
||||
make down
|
||||
```
|
||||
|
||||
Expected: Services start (Postgres and Redis should be healthy), then stop cleanly
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "test: validate Docker infrastructure"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
Run these checks to verify everything works:
|
||||
|
||||
1. **Syntax validation:**
|
||||
```bash
|
||||
docker compose config > /dev/null && echo "Valid"
|
||||
```
|
||||
|
||||
2. **Health checks:**
|
||||
```bash
|
||||
make up
|
||||
docker compose ps
|
||||
```
|
||||
All services should show "healthy" or "running"
|
||||
|
||||
3. **Makefile commands:**
|
||||
```bash
|
||||
make help # Shows usage
|
||||
make build # Builds images
|
||||
make up # Starts services
|
||||
make logs # Shows logs
|
||||
make down # Stops services
|
||||
```
|
||||
|
||||
## Quality Gates
|
||||
|
||||
- [ ] `docker compose config` validates without errors
|
||||
- [ ] All services have health checks defined
|
||||
- [ ] API Dockerfile uses multi-stage build with non-root user
|
||||
- [ ] Web Dockerfile uses multi-stage build with non-root user
|
||||
- [ ] Makefile includes all required commands (up, down, logs, migrate, test, lint)
|
||||
- [ ] .env.example documents all required variables
|
||||
- [ ] Persistent volume for `/data/repos`
|
||||
Reference in New Issue
Block a user