🚀 ci(picture): add picture app to CI/CD deployment pipeline

- Add picture-backend and picture-web to CI Docker build matrix
- Add picture services to staging deployment workflow
- Add picture-backend to production deployment workflow
- Create Dockerfile and docker-entrypoint.sh for picture-web
- Fix picture-backend Dockerfile port (3003→3006) and health endpoint
- Add picture routes to Caddyfile.staging
- Add REPLICATE_API_TOKEN and MANA_CORE_SERVICE_KEY env vars
This commit is contained in:
Wuesteon 2025-12-17 18:47:42 +01:00
parent 74654e652a
commit dcdc15f154
8 changed files with 285 additions and 2 deletions

View file

@ -22,6 +22,7 @@ on:
- manadeck-backend
- nutriphi-backend
- news-api
- picture-backend
environment:
description: 'Deployment environment'
required: true
@ -269,7 +270,7 @@ jobs:
if [ "$SERVICE" == "all" ]; then
# Rolling update for all services
for service in mana-core-auth maerchenzauber-backend chat-backend manadeck-backend nutriphi-backend news-api; do
for service in mana-core-auth maerchenzauber-backend chat-backend manadeck-backend nutriphi-backend news-api picture-backend; do
echo "Deploying \$service..."
docker compose up -d --no-deps --scale \$service=2 \$service
sleep 10

View file

@ -31,6 +31,8 @@ on:
- calendar-web
- clock-backend
- clock-web
- picture-backend
- picture-web
workflow_call:
permissions:
@ -138,6 +140,12 @@ jobs:
S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}
MANACORE_STORAGE_PUBLIC_URL=${{ secrets.MANACORE_STORAGE_PUBLIC_URL }}
# Replicate API (for Picture app AI image generation)
REPLICATE_API_TOKEN=${{ secrets.REPLICATE_API_TOKEN }}
# Mana Core Service Key (for credit system)
MANA_CORE_SERVICE_KEY=${{ secrets.MANA_CORE_SERVICE_KEY }}
# Environment
NODE_ENV=staging
EOF
@ -217,6 +225,9 @@ jobs:
# Create clock database (for clock-backend service)
docker compose exec -T postgres psql -U postgres -c "CREATE DATABASE clock;" 2>/dev/null || echo "clock database already exists"
# Create picture database (for picture-backend service)
docker compose exec -T postgres psql -U postgres -c "CREATE DATABASE picture;" 2>/dev/null || echo "picture database already exists"
echo "✅ Databases ready"
EOF
@ -349,6 +360,8 @@ jobs:
check_health calendar-web http://localhost:5186/health || exit 1
check_health clock-backend http://localhost:3017/api/v1/health || exit 1
check_health clock-web http://localhost:5187/health || exit 1
check_health picture-backend http://localhost:3006/api/v1/health || exit 1
check_health picture-web http://localhost:5175/health || exit 1
echo ""
echo "✅ All health checks passed!"

View file

@ -93,6 +93,8 @@ jobs:
- { name: 'calendar-web', path: 'apps/calendar/apps/web', port: '5186' }
- { name: 'clock-backend', path: 'apps/clock/apps/backend', port: '3017' }
- { name: 'clock-web', path: 'apps/clock/apps/web', port: '5187' }
- { name: 'picture-backend', path: 'apps/picture/apps/backend', port: '3006' }
- { name: 'picture-web', path: 'apps/picture/apps/web', port: '5175' }
fail-fast: false
steps:
- name: Checkout code

View file

@ -0,0 +1,95 @@
# Build stage
FROM node:20-alpine AS builder
# Build arguments for SvelteKit static env vars
ARG PUBLIC_BACKEND_URL=http://picture-backend:3006
ARG PUBLIC_MANA_CORE_AUTH_URL=http://mana-core-auth:3001
# Set as environment variables for build
ENV PUBLIC_BACKEND_URL=$PUBLIC_BACKEND_URL
ENV PUBLIC_MANA_CORE_AUTH_URL=$PUBLIC_MANA_CORE_AUTH_URL
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
# Copy root workspace files
COPY pnpm-workspace.yaml ./
COPY package.json ./
COPY pnpm-lock.yaml ./
# Copy shared packages needed by picture web
COPY packages/better-auth-types ./packages/better-auth-types
COPY packages/shared-auth ./packages/shared-auth
COPY packages/shared-auth-ui ./packages/shared-auth-ui
COPY packages/shared-branding ./packages/shared-branding
COPY packages/shared-feedback-service ./packages/shared-feedback-service
COPY packages/shared-feedback-types ./packages/shared-feedback-types
COPY packages/shared-feedback-ui ./packages/shared-feedback-ui
COPY packages/shared-i18n ./packages/shared-i18n
COPY packages/shared-icons ./packages/shared-icons
COPY packages/shared-tailwind ./packages/shared-tailwind
COPY packages/shared-theme ./packages/shared-theme
COPY packages/shared-theme-ui ./packages/shared-theme-ui
COPY packages/shared-subscription-types ./packages/shared-subscription-types
COPY packages/shared-subscription-ui ./packages/shared-subscription-ui
COPY packages/shared-profile-ui ./packages/shared-profile-ui
COPY packages/shared-ui ./packages/shared-ui
COPY packages/shared-utils ./packages/shared-utils
# Copy picture packages
COPY apps/picture/packages ./apps/picture/packages
COPY apps/picture/apps/web ./apps/picture/apps/web
# Install dependencies
RUN pnpm install --frozen-lockfile
# Build shared packages that need building
WORKDIR /app/packages/better-auth-types
RUN pnpm build || true
WORKDIR /app/packages/shared-auth
RUN pnpm build || true
# Build the web app
WORKDIR /app/apps/picture/apps/web
RUN pnpm build
# Production stage
FROM node:20-alpine AS production
# Keep same directory structure as builder so pnpm symlinks resolve correctly
WORKDIR /app/apps/picture/apps/web
# Copy the pnpm store that symlinks point to (at /app/node_modules/.pnpm)
COPY --from=builder /app/node_modules/.pnpm /app/node_modules/.pnpm
# Copy the app's node_modules (contains symlinks to the pnpm store)
COPY --from=builder /app/apps/picture/apps/web/node_modules ./node_modules
# Copy built application
COPY --from=builder /app/apps/picture/apps/web/build ./build
COPY --from=builder /app/apps/picture/apps/web/package.json ./
# Copy entrypoint script for runtime config generation
COPY apps/picture/apps/web/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Expose port
EXPOSE 5175
# Set environment variables
ENV NODE_ENV=production
ENV PORT=5175
ENV HOST=0.0.0.0
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:5175/health || exit 1
# Use entrypoint to generate runtime config
ENTRYPOINT ["docker-entrypoint.sh"]
# Run the app
CMD ["node", "build"]

View file

@ -0,0 +1,31 @@
#!/bin/sh
set -e
echo "Generating runtime configuration..."
# Environment variables with development defaults
BACKEND_URL=${BACKEND_URL:-"http://localhost:3006"}
AUTH_URL=${AUTH_URL:-"http://localhost:3001"}
echo "Config values:"
echo " BACKEND_URL: $BACKEND_URL"
echo " AUTH_URL: $AUTH_URL"
# Generate config.json from environment variables
cat > /app/apps/picture/apps/web/build/client/config.json <<EOF
{
"BACKEND_URL": "${BACKEND_URL}",
"AUTH_URL": "${AUTH_URL}"
}
EOF
echo "Configuration generated at /app/apps/picture/apps/web/build/client/config.json"
cat /app/apps/picture/apps/web/build/client/config.json
# Remove pre-compressed versions (SvelteKit serves these instead of the raw file)
rm -f /app/apps/picture/apps/web/build/client/config.json.br
rm -f /app/apps/picture/apps/web/build/client/config.json.gz
echo "Removed stale pre-compressed config files"
echo "Starting Picture web app..."
exec "$@"

View file

@ -1,5 +1,10 @@
version: '3.9'
# To add more services:
# 1. Add service block below
# 2. Add to cd-production.yml workflow_dispatch options
# 3. Add Caddy routes to production Caddyfile
services:
# ============================================
# Backend Services (Production)
@ -235,6 +240,53 @@ services:
cpus: '0.5'
memory: 256M
picture-backend:
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/picture-backend:${PICTURE_VERSION:-latest}
container_name: picture-backend-prod
restart: always
depends_on:
mana-core-auth:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3006
DATABASE_URL: ${PICTURE_DATABASE_URL}
MANA_CORE_AUTH_URL: http://mana-core-auth:3001
# Replicate API for AI image generation
REPLICATE_API_TOKEN: ${REPLICATE_API_TOKEN}
# S3/MinIO storage
S3_ENDPOINT: ${S3_ENDPOINT}
S3_REGION: ${S3_REGION}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
MANACORE_STORAGE_PUBLIC_URL: ${MANACORE_STORAGE_PUBLIC_URL}
# Credit system
MANA_CORE_SERVICE_KEY: ${MANA_CORE_SERVICE_KEY}
APP_ID: picture
ports:
- "127.0.0.1:3007:3006"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3006/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- manacore-prod
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '1'
memory: 512M
# ============================================
# Monitoring (Optional but recommended)
# ============================================

View file

@ -8,6 +8,7 @@
# 1. Copy the service block from docker-compose.staging.full.yml
# 2. Add corresponding health check in .github/workflows/cd-staging.yml
# 3. Add service to workflow_dispatch options in cd-staging.yml
# 4. Add Caddy routes to docker/caddy/Caddyfile.staging
services:
# ============================================
@ -83,7 +84,7 @@ services:
# Frontend URL for password reset and email verification links
FRONTEND_URL: ${FRONTEND_URL:-https://staging.manacore.ai}
# CORS - Allow all staging web app origins (HTTPS domains + localhost for dev)
CORS_ORIGINS: https://chat.staging.manacore.ai,https://staging.manacore.ai,https://calendar.staging.manacore.ai,https://clock.staging.manacore.ai,https://todo.staging.manacore.ai,http://localhost:3000,http://localhost:5173,http://localhost:5186,http://localhost:5187,http://localhost:5188
CORS_ORIGINS: https://chat.staging.manacore.ai,https://staging.manacore.ai,https://calendar.staging.manacore.ai,https://clock.staging.manacore.ai,https://todo.staging.manacore.ai,https://picture.staging.manacore.ai,http://localhost:3000,http://localhost:5173,http://localhost:5175,http://localhost:5186,http://localhost:5187,http://localhost:5188
ports:
- "3001:3001"
healthcheck:
@ -189,6 +190,7 @@ services:
CALENDAR_API_URL: https://calendar-api.staging.manacore.ai
CLOCK_API_URL: https://clock-api.staging.manacore.ai
CONTACTS_API_URL: https://contacts-api.staging.manacore.ai
PICTURE_API_URL: https://picture-api.staging.manacore.ai
ports:
- "5173:5173"
healthcheck:
@ -411,6 +413,85 @@ services:
max-size: "10m"
max-file: "3"
# ============================================
# Picture App
# ============================================
picture-backend:
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/picture-backend:${PICTURE_VERSION:-latest}
container_name: picture-backend-staging
restart: unless-stopped
depends_on:
mana-core-auth:
condition: service_healthy
postgres:
condition: service_healthy
environment:
NODE_ENV: staging
PORT: 3006
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/picture
DB_HOST: postgres
DB_PORT: 5432
DB_USER: ${POSTGRES_USER:-postgres}
MANA_CORE_AUTH_URL: http://mana-core-auth:3001
CORS_ORIGINS: https://picture.staging.manacore.ai,https://staging.manacore.ai,http://localhost:5175,http://localhost:5173
# Replicate API for AI image generation
REPLICATE_API_TOKEN: ${REPLICATE_API_TOKEN}
# S3/MinIO storage
S3_ENDPOINT: ${S3_ENDPOINT}
S3_REGION: ${S3_REGION}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
MANACORE_STORAGE_PUBLIC_URL: ${MANACORE_STORAGE_PUBLIC_URL}
# Credit system
MANA_CORE_SERVICE_KEY: ${MANA_CORE_SERVICE_KEY}
APP_ID: picture
ports:
- "3006:3006"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3006/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- manacore-network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
picture-web:
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/picture-web:${PICTURE_WEB_VERSION:-latest}
container_name: picture-web-staging
restart: unless-stopped
depends_on:
picture-backend:
condition: service_healthy
environment:
NODE_ENV: staging
PORT: 5175
# Runtime config generation (12-factor pattern)
# These vars are used by docker-entrypoint.sh to generate /config.json
BACKEND_URL: https://picture-api.staging.manacore.ai
AUTH_URL: https://auth.staging.manacore.ai
ports:
- "5175:5175"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5175/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- manacore-network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# ============================================
# Networks
# ============================================

View file

@ -43,3 +43,11 @@ todo.staging.manacore.ai {
todo-api.staging.manacore.ai {
reverse_proxy localhost:3018
}
# Picture
picture.staging.manacore.ai {
reverse_proxy localhost:5175
}
picture-api.staging.manacore.ai {
reverse_proxy localhost:3006
}