Release: CI/CD Pipeline & Architecture Updates #177

Merged
jason.woltje merged 173 commits from develop into main 2026-02-01 19:18:48 +00:00
3 changed files with 318 additions and 0 deletions
Showing only changes of commit 4b943fb997 - Show all commits

66
.env.prod.example Normal file
View File

@@ -0,0 +1,66 @@
# ==============================================
# Mosaic Stack Production Environment
# ==============================================
# Copy to .env and configure for production deployment
# ======================
# PostgreSQL Database
# ======================
# CRITICAL: Use a strong, unique password
POSTGRES_USER=mosaic
POSTGRES_PASSWORD=REPLACE_WITH_SECURE_PASSWORD
POSTGRES_DB=mosaic
POSTGRES_SHARED_BUFFERS=256MB
POSTGRES_EFFECTIVE_CACHE_SIZE=1GB
POSTGRES_MAX_CONNECTIONS=100
# ======================
# Valkey Cache
# ======================
VALKEY_MAXMEMORY=256mb
# ======================
# API Configuration
# ======================
API_PORT=3001
API_HOST=0.0.0.0
# ======================
# Web Configuration
# ======================
WEB_PORT=3000
NEXT_PUBLIC_API_URL=https://api.mosaicstack.dev
# ======================
# Authentication (Authentik OIDC)
# ======================
OIDC_ISSUER=https://auth.diversecanvas.com/application/o/mosaic-stack/
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URI=https://api.mosaicstack.dev/auth/callback/authentik
# ======================
# JWT Configuration
# ======================
# CRITICAL: Generate a random secret (openssl rand -base64 32)
JWT_SECRET=REPLACE_WITH_RANDOM_SECRET
JWT_EXPIRATION=24h
# ======================
# Traefik Integration
# ======================
# Set to true if using external Traefik
TRAEFIK_ENABLE=true
TRAEFIK_ENTRYPOINT=websecure
TRAEFIK_TLS_ENABLED=true
TRAEFIK_DOCKER_NETWORK=traefik-public
TRAEFIK_CERTRESOLVER=letsencrypt
# Domain configuration
MOSAIC_API_DOMAIN=api.mosaicstack.dev
MOSAIC_WEB_DOMAIN=app.mosaicstack.dev
# ======================
# Optional: Ollama
# ======================
# OLLAMA_ENDPOINT=http://ollama.diversecanvas.com:11434

View File

@@ -78,3 +78,76 @@ steps:
- typecheck # Only block on critical checks
- security-audit
- prisma-generate
# ======================
# Docker Build & Push (main/develop only)
# ======================
# Requires secrets: harbor_username, harbor_password
docker-build-api:
image: woodpeckerci/plugin-docker-buildx
settings:
registry: reg.diversecanvas.com
repo: reg.diversecanvas.com/mosaic/api
dockerfile: apps/api/Dockerfile
context: .
platforms:
- linux/amd64
tags:
- "${CI_COMMIT_SHA:0:8}"
- latest
username:
from_secret: harbor_username
password:
from_secret: harbor_password
when:
- branch: [main, develop]
event: push
depends_on:
- build
docker-build-web:
image: woodpeckerci/plugin-docker-buildx
settings:
registry: reg.diversecanvas.com
repo: reg.diversecanvas.com/mosaic/web
dockerfile: apps/web/Dockerfile
context: .
platforms:
- linux/amd64
build_args:
- NEXT_PUBLIC_API_URL=https://api.mosaicstack.dev
tags:
- "${CI_COMMIT_SHA:0:8}"
- latest
username:
from_secret: harbor_username
password:
from_secret: harbor_password
when:
- branch: [main, develop]
event: push
depends_on:
- build
docker-build-postgres:
image: woodpeckerci/plugin-docker-buildx
settings:
registry: reg.diversecanvas.com
repo: reg.diversecanvas.com/mosaic/postgres
dockerfile: docker/postgres/Dockerfile
context: docker/postgres
platforms:
- linux/amd64
tags:
- "${CI_COMMIT_SHA:0:8}"
- latest
username:
from_secret: harbor_username
password:
from_secret: harbor_password
when:
- branch: [main, develop]
event: push
depends_on:
- build

179
docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,179 @@
# Production Docker Compose - Uses pre-built images from Harbor
#
# Prerequisites:
# - Images built and pushed to reg.diversecanvas.com/mosaic/*
# - .env file configured with production values
#
# Usage:
# docker compose -f docker-compose.prod.yml up -d
#
# For Portainer:
# - Stack → Add Stack → Repository
# - Compose file: docker-compose.prod.yml
services:
# ======================
# PostgreSQL Database
# ======================
postgres:
image: reg.diversecanvas.com/mosaic/postgres:latest
container_name: mosaic-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-mosaic}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB:-mosaic}
POSTGRES_SHARED_BUFFERS: ${POSTGRES_SHARED_BUFFERS:-256MB}
POSTGRES_EFFECTIVE_CACHE_SIZE: ${POSTGRES_EFFECTIVE_CACHE_SIZE:-1GB}
POSTGRES_MAX_CONNECTIONS: ${POSTGRES_MAX_CONNECTIONS:-100}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mosaic} -d ${POSTGRES_DB:-mosaic}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- mosaic-internal
labels:
- "com.mosaic.service=database"
- "com.mosaic.description=PostgreSQL 17 with pgvector"
# ======================
# Valkey Cache
# ======================
valkey:
image: valkey/valkey:8-alpine
container_name: mosaic-valkey
restart: unless-stopped
command:
- valkey-server
- --maxmemory ${VALKEY_MAXMEMORY:-256mb}
- --maxmemory-policy allkeys-lru
- --appendonly yes
volumes:
- valkey_data:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
networks:
- mosaic-internal
labels:
- "com.mosaic.service=cache"
- "com.mosaic.description=Valkey Redis-compatible cache"
# ======================
# Mosaic API
# ======================
api:
image: reg.diversecanvas.com/mosaic/api:latest
container_name: mosaic-api
restart: unless-stopped
environment:
NODE_ENV: production
PORT: ${API_PORT:-3001}
API_HOST: ${API_HOST:-0.0.0.0}
DATABASE_URL: postgresql://${POSTGRES_USER:-mosaic}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-mosaic}
VALKEY_URL: redis://valkey:6379
OIDC_ISSUER: ${OIDC_ISSUER}
OIDC_CLIENT_ID: ${OIDC_CLIENT_ID}
OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET}
OIDC_REDIRECT_URI: ${OIDC_REDIRECT_URI}
JWT_SECRET: ${JWT_SECRET}
JWT_EXPIRATION: ${JWT_EXPIRATION:-24h}
OLLAMA_ENDPOINT: ${OLLAMA_ENDPOINT:-http://ollama:11434}
ports:
- "${API_PORT:-3001}:${API_PORT:-3001}"
depends_on:
postgres:
condition: service_healthy
valkey:
condition: service_healthy
healthcheck:
test:
[
"CMD-SHELL",
'node -e "require(''http'').get(''http://localhost:${API_PORT:-3001}/health'', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"',
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- mosaic-internal
- mosaic-public
labels:
- "com.mosaic.service=api"
- "com.mosaic.description=Mosaic NestJS API"
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.mosaic-api.rule=Host(`${MOSAIC_API_DOMAIN:-api.mosaicstack.dev}`)"
- "traefik.http.routers.mosaic-api.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
- "traefik.http.routers.mosaic-api.tls=${TRAEFIK_TLS_ENABLED:-true}"
- "traefik.http.services.mosaic-api.loadbalancer.server.port=${API_PORT:-3001}"
- "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-mosaic-public}"
- "traefik.http.routers.mosaic-api.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
# ======================
# Mosaic Web
# ======================
web:
image: reg.diversecanvas.com/mosaic/web:latest
container_name: mosaic-web
restart: unless-stopped
environment:
NODE_ENV: production
PORT: ${WEB_PORT:-3000}
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-https://api.mosaicstack.dev}
ports:
- "${WEB_PORT:-3000}:${WEB_PORT:-3000}"
depends_on:
api:
condition: service_healthy
healthcheck:
test:
[
"CMD-SHELL",
'node -e "require(''http'').get(''http://localhost:${WEB_PORT:-3000}'', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"',
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- mosaic-public
labels:
- "com.mosaic.service=web"
- "com.mosaic.description=Mosaic Next.js Web App"
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.mosaic-web.rule=Host(`${MOSAIC_WEB_DOMAIN:-app.mosaicstack.dev}`)"
- "traefik.http.routers.mosaic-web.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
- "traefik.http.routers.mosaic-web.tls=${TRAEFIK_TLS_ENABLED:-true}"
- "traefik.http.services.mosaic-web.loadbalancer.server.port=${WEB_PORT:-3000}"
- "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-mosaic-public}"
- "traefik.http.routers.mosaic-web.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
# ======================
# Volumes
# ======================
volumes:
postgres_data:
name: mosaic-postgres-data
driver: local
valkey_data:
name: mosaic-valkey-data
driver: local
# ======================
# Networks
# ======================
networks:
mosaic-internal:
name: mosaic-internal
driver: bridge
mosaic-public:
name: mosaic-public
driver: bridge