feat(infra): add sveltekit-base image and build-app script for Mac Mini

- Add docker/Dockerfile.sveltekit-base: pre-built base with all 34 shared
  packages (mirrors nestjs-base pattern), eliminates redundant COPY/build
  steps from individual web Dockerfiles
- Add scripts/mac-mini/build-app.sh: stops monitoring stack before build
  to free RAM, auto-restarts on exit (trap cleanup)
- Migrate todo web Dockerfile to use sveltekit-base:local (47 COPY lines
  → 2, 4 build steps → 0)
- Update CD workflow to build sveltekit-base when deploying web apps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-26 12:17:48 +01:00
parent c681a5d66a
commit cdfbfcd13e
4 changed files with 233 additions and 61 deletions

View file

@ -243,12 +243,29 @@ jobs:
done done
fi fi
NEEDS_WEB_BASE=false
if [ "$DEPLOY_ALL" == "true" ]; then
NEEDS_WEB_BASE=true
else
for svc in $SERVICES; do
case "$svc" in *-web) NEEDS_WEB_BASE=true; break ;; esac
done
fi
if [ "$NEEDS_BASE" == "true" ]; then if [ "$NEEDS_BASE" == "true" ]; then
echo "=== Building shared NestJS base image ===" echo "=== Building shared NestJS base image ==="
docker build -f docker/Dockerfile.nestjs-base -t nestjs-base:local . 2>&1 | tail -5 docker build -f docker/Dockerfile.nestjs-base -t nestjs-base:local . 2>&1 | tail -5
echo "Base image built" echo "NestJS base image built"
else else
echo "No backends to deploy, skipping base image" echo "No backends to deploy, skipping NestJS base image"
fi
if [ "$NEEDS_WEB_BASE" == "true" ]; then
echo "=== Building shared SvelteKit base image ==="
docker build -f docker/Dockerfile.sveltekit-base -t sveltekit-base:local . 2>&1 | tail -5
echo "SvelteKit base image built"
else
echo "No web apps to deploy, skipping SvelteKit base image"
fi fi
- name: Build and deploy services - name: Build and deploy services

View file

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# Build stage # Build stage - inherits pre-built shared packages from sveltekit-base
FROM node:20-alpine AS builder FROM sveltekit-base:local AS builder
# Build arguments for SvelteKit static env vars # Build arguments for SvelteKit static env vars
ARG PUBLIC_BACKEND_URL=http://todo-backend:3018 ARG PUBLIC_BACKEND_URL=http://todo-backend:3018
@ -10,66 +10,13 @@ ARG PUBLIC_MANA_CORE_AUTH_URL=http://mana-core-auth:3001
ENV PUBLIC_BACKEND_URL=$PUBLIC_BACKEND_URL ENV PUBLIC_BACKEND_URL=$PUBLIC_BACKEND_URL
ENV PUBLIC_MANA_CORE_AUTH_URL=$PUBLIC_MANA_CORE_AUTH_URL ENV PUBLIC_MANA_CORE_AUTH_URL=$PUBLIC_MANA_CORE_AUTH_URL
# Install pnpm # Copy app-specific packages
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 ./
# --- AUTO-GENERATED COPY STATEMENTS (do not edit manually) ---
COPY patches/ ./patches/
COPY packages/shared-api-client ./packages/shared-api-client
COPY packages/shared-app-onboarding ./packages/shared-app-onboarding
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-error-tracking ./packages/shared-error-tracking
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-help-content ./packages/shared-help-content
COPY packages/shared-help-types ./packages/shared-help-types
COPY packages/shared-help-ui ./packages/shared-help-ui
COPY packages/shared-i18n ./packages/shared-i18n
COPY packages/shared-icons ./packages/shared-icons
COPY packages/shared-profile-ui ./packages/shared-profile-ui
COPY packages/shared-pwa ./packages/shared-pwa
COPY packages/shared-splitscreen ./packages/shared-splitscreen
COPY packages/shared-stores ./packages/shared-stores
COPY packages/shared-subscription-ui ./packages/shared-subscription-ui
COPY packages/shared-tags ./packages/shared-tags
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-types ./packages/shared-types
COPY packages/shared-ui ./packages/shared-ui
COPY packages/shared-utils ./packages/shared-utils
COPY packages/shared-vite-config ./packages/shared-vite-config
COPY packages/spiral-db ./packages/spiral-db
COPY apps/todo/packages/shared ./apps/todo/packages/shared COPY apps/todo/packages/shared ./apps/todo/packages/shared
# --- END AUTO-GENERATED ---
COPY apps/todo/apps/web ./apps/todo/apps/web COPY apps/todo/apps/web ./apps/todo/apps/web
# Install dependencies # Install app-specific dependencies
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile --ignore-scripts
# Build shared packages that need building
WORKDIR /app/packages/shared-vite-config
RUN pnpm build
WORKDIR /app/packages/shared-auth
RUN pnpm build || true
WORKDIR /app/packages/shared-error-tracking
RUN pnpm build
WORKDIR /app/packages/shared-pwa
RUN pnpm build
# Build the web app # Build the web app
WORKDIR /app/apps/todo/apps/web WORKDIR /app/apps/todo/apps/web

View file

@ -0,0 +1,76 @@
# syntax=docker/dockerfile:1
# Shared builder base for all SvelteKit web apps
# Contains pre-installed shared packages and pre-built dependencies
#
# Usage in web Dockerfiles:
# FROM sveltekit-base:local AS builder
# COPY apps/myapp/packages/shared ./apps/myapp/packages/shared
# COPY apps/myapp/apps/web ./apps/myapp/apps/web
# RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile
# WORKDIR /app/apps/myapp/apps/web
# RUN pnpm exec svelte-kit sync
# RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm build
FROM node:20-alpine
# 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 patches/ ./patches/
# Copy ALL shared packages used by SvelteKit web apps
COPY packages/credit-operations ./packages/credit-operations
COPY packages/qr-export ./packages/qr-export
COPY packages/shared-api-client ./packages/shared-api-client
COPY packages/shared-app-onboarding ./packages/shared-app-onboarding
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-config ./packages/shared-config
COPY packages/shared-error-tracking ./packages/shared-error-tracking
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-help-content ./packages/shared-help-content
COPY packages/shared-help-types ./packages/shared-help-types
COPY packages/shared-help-ui ./packages/shared-help-ui
COPY packages/shared-i18n ./packages/shared-i18n
COPY packages/shared-icons ./packages/shared-icons
COPY packages/shared-profile-ui ./packages/shared-profile-ui
COPY packages/shared-pwa ./packages/shared-pwa
COPY packages/shared-splitscreen ./packages/shared-splitscreen
COPY packages/shared-stores ./packages/shared-stores
COPY packages/shared-subscription-types ./packages/shared-subscription-types
COPY packages/shared-subscription-ui ./packages/shared-subscription-ui
COPY packages/shared-tags ./packages/shared-tags
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-types ./packages/shared-types
COPY packages/shared-ui ./packages/shared-ui
COPY packages/shared-utils ./packages/shared-utils
COPY packages/shared-vite-config ./packages/shared-vite-config
COPY packages/spiral-db ./packages/spiral-db
COPY packages/wallpaper-generator ./packages/wallpaper-generator
# Install dependencies (shared packages only - app deps added later)
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile --ignore-scripts
# Build shared packages in dependency order
RUN cd packages/shared-vite-config && pnpm build \
&& cd /app/packages/shared-auth && pnpm build || true \
&& cd /app/packages/shared-error-tracking && pnpm build \
&& cd /app/packages/shared-pwa && pnpm build \
&& cd /app/packages/shared-tags && pnpm build \
&& cd /app/packages/shared-api-client && pnpm build 2>/dev/null || true \
&& cd /app/packages/credit-operations && pnpm build 2>/dev/null || true \
&& cd /app/packages/spiral-db && pnpm build 2>/dev/null || true
WORKDIR /app

132
scripts/mac-mini/build-app.sh Executable file
View file

@ -0,0 +1,132 @@
#!/bin/bash
# Build and deploy specific app containers on the Mac Mini
# Automatically frees RAM by stopping monitoring before build
#
# Usage:
# ./scripts/mac-mini/build-app.sh todo-web
# ./scripts/mac-mini/build-app.sh todo-web todo-backend
# ./scripts/mac-mini/build-app.sh --all-web # rebuild all web apps
# ./scripts/mac-mini/build-app.sh --base # rebuild base images only
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
COMPOSE_FILE="$PROJECT_ROOT/docker-compose.macmini.yml"
DOCKER="${DOCKER_CMD:-/usr/local/bin/docker}"
# Monitoring services (compose service names)
MONITORING_SERVICES=(
grafana
umami
victoriametrics
pushgateway
cadvisor
postgres-exporter
redis-exporter
node-exporter
vmalert
alertmanager
alert-notifier
glitchtip
glitchtip-worker
)
# Track if we stopped monitoring
MONITORING_STOPPED=false
cleanup() {
if [ "$MONITORING_STOPPED" = true ]; then
echo ""
echo "=== Restarting monitoring stack ==="
$DOCKER compose -f "$COMPOSE_FILE" start "${MONITORING_SERVICES[@]}" 2>/dev/null || true
echo "Monitoring restored."
fi
}
# Always restart monitoring on exit (success, failure, or interrupt)
trap cleanup EXIT
stop_monitoring() {
echo "=== Stopping monitoring to free RAM ==="
$DOCKER compose -f "$COMPOSE_FILE" stop "${MONITORING_SERVICES[@]}" 2>/dev/null || true
MONITORING_STOPPED=true
# Also prune dangling build cache
$DOCKER builder prune -f 2>/dev/null | tail -1 || true
echo "RAM freed."
echo ""
}
build_base_images() {
echo "=== Building sveltekit-base image ==="
$DOCKER build -f "$PROJECT_ROOT/docker/Dockerfile.sveltekit-base" -t sveltekit-base:local "$PROJECT_ROOT" 2>&1 | tail -5
echo "sveltekit-base:local built."
echo ""
echo "=== Building nestjs-base image ==="
$DOCKER build -f "$PROJECT_ROOT/docker/Dockerfile.nestjs-base" -t nestjs-base:local "$PROJECT_ROOT" 2>&1 | tail -5
echo "nestjs-base:local built."
echo ""
}
build_services() {
local services=("$@")
echo "=== Building: ${services[*]} ==="
$DOCKER compose -f "$COMPOSE_FILE" build --no-cache "${services[@]}"
echo ""
echo "=== Restarting: ${services[*]} ==="
$DOCKER compose -f "$COMPOSE_FILE" up -d --no-deps "${services[@]}"
}
# --- Main ---
if [ $# -eq 0 ]; then
echo "Usage: $0 <service...> | --base | --all-web"
echo ""
echo "Examples:"
echo " $0 todo-web # Build & restart todo web"
echo " $0 todo-web todo-backend # Build & restart both"
echo " $0 --base # Rebuild base images"
echo " $0 --all-web # Rebuild all web apps"
exit 1
fi
cd "$PROJECT_ROOT"
# Pull latest code
echo "=== Pulling latest code ==="
git pull
# Free RAM
stop_monitoring
case "$1" in
--base)
build_base_images
;;
--all-web)
build_base_images
# Find all web services in compose
WEB_SERVICES=$($DOCKER compose -f "$COMPOSE_FILE" config --services 2>/dev/null | grep '\-web$' || true)
if [ -n "$WEB_SERVICES" ]; then
build_services $WEB_SERVICES
else
echo "No web services found."
fi
;;
*)
build_services "$@"
;;
esac
echo ""
echo "=== Build complete ==="
# Show status of built services
for svc in "$@"; do
if [ "$svc" != "--base" ] && [ "$svc" != "--all-web" ]; then
STATUS=$($DOCKER compose -f "$COMPOSE_FILE" ps --format '{{.Name}}\t{{.Status}}' "$svc" 2>/dev/null || echo "$svc: unknown")
echo " $STATUS"
fi
done