#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" SSH_USER="${SSH_USER:-syntaxbullet}" VPS_HOST="${VPS_HOST:-srv1.bayerischer-mittelstandspreis.de}" VPS_PORT="${VPS_PORT:-22}" SSH_KEY="${SSH_KEY:-$HOME/.ssh/bmp-vps-deploy}" APP_ROOT="${APP_ROOT:-/var/www/bmp-website}" APP_DOMAIN="${APP_DOMAIN:-srv1.bayerischer-mittelstandspreis.de}" PUBLIC_URL="${PUBLIC_URL:-https://$APP_DOMAIN}" DEPLOY_CADDY="${DEPLOY_CADDY:-0}" RUN_MIGRATIONS="${RUN_MIGRATIONS:-0}" SKIP_VERIFY="${SKIP_VERIFY:-0}" VERIFY_RETRIES="${VERIFY_RETRIES:-30}" VERIFY_RETRY_DELAY_SECONDS="${VERIFY_RETRY_DELAY_SECONDS:-2}" VERIFY_RETRY_MAX_SECONDS="${VERIFY_RETRY_MAX_SECONDS:-60}" VERIFY_REQUEST_TIMEOUT_SECONDS="${VERIFY_REQUEST_TIMEOUT_SECONDS:-20}" SSH_TARGET="$SSH_USER@$VPS_HOST" COMPOSE_FILE="$APP_ROOT/current/deploy/vps/docker-compose.yml" if [[ ! -f "$SSH_KEY" ]]; then echo "Missing SSH key: $SSH_KEY" >&2 exit 1 fi if [[ -n "$(git status --porcelain)" ]]; then echo "Refusing to deploy a dirty worktree." >&2 echo "Commit or stash changes first." >&2 exit 1 fi REVISION="$(git rev-parse --short=12 HEAD)" echo "Deploying $REVISION to $SSH_TARGET:$APP_ROOT" rsync -az --delete \ --exclude '.git/' \ --exclude '.agents/' \ --exclude '.codex/' \ --exclude '.pnpm-store/' \ --exclude 'skills-lock.json' \ --exclude '.env' \ --include '.env.example' \ --exclude '.env.*' \ --exclude '.next/' \ --exclude 'node_modules/' \ --exclude 'content.db' \ --exclude 'next-env.d.ts' \ --exclude 'public/media/' \ --exclude 'public/robots.txt' \ --exclude 'public/sitemap*.xml' \ --exclude '.DS_Store' \ --exclude 'tsconfig.tsbuildinfo' \ -e "ssh -i $SSH_KEY -p $VPS_PORT" \ ./ "$SSH_TARGET:$APP_ROOT/current/" ssh -i "$SSH_KEY" -p "$VPS_PORT" "$SSH_TARGET" "printf '%s\n' '$REVISION' > '$APP_ROOT/current/.deploy-revision'" ssh -t -i "$SSH_KEY" -p "$VPS_PORT" "$SSH_TARGET" " set -e cd '$APP_ROOT' APP_ROOT='$APP_ROOT' APP_DOMAIN='$APP_DOMAIN' sudo docker compose -f '$COMPOSE_FILE' build app if [ '$RUN_MIGRATIONS' = '1' ]; then backup_dir='$APP_ROOT/backups' backup_file=\"\$backup_dir/content-\$(date +%Y%m%d-%H%M%S)-pre-migrate.db\" sudo install -d -o '$SSH_USER' -g '$SSH_USER' \"\$backup_dir\" restart_app() { APP_ROOT='$APP_ROOT' APP_DOMAIN='$APP_DOMAIN' sudo docker compose -f '$COMPOSE_FILE' up -d app >/dev/null } trap restart_app EXIT APP_ROOT='$APP_ROOT' APP_DOMAIN='$APP_DOMAIN' sudo docker compose -f '$COMPOSE_FILE' stop app >/dev/null cp -p '$APP_ROOT/shared/content.db' \"\$backup_file\" echo \"Created pre-migration backup: \$backup_file\" APP_ROOT='$APP_ROOT' APP_DOMAIN='$APP_DOMAIN' sudo docker compose -f '$COMPOSE_FILE' run --rm --no-deps app pnpm payload migrate restart_app trap - EXIT else APP_ROOT='$APP_ROOT' APP_DOMAIN='$APP_DOMAIN' sudo docker compose -f '$COMPOSE_FILE' up -d app fi if [ '$DEPLOY_CADDY' = '1' ]; then APP_ROOT='$APP_ROOT' APP_DOMAIN='$APP_DOMAIN' sudo docker compose -f '$COMPOSE_FILE' up -d caddy fi sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' " verify_url() { local url="$1" echo "Verifying $url" curl -fsSIL \ --retry "$VERIFY_RETRIES" \ --retry-all-errors \ --retry-delay "$VERIFY_RETRY_DELAY_SECONDS" \ --retry-max-time "$VERIFY_RETRY_MAX_SECONDS" \ --max-time "$VERIFY_REQUEST_TIMEOUT_SECONDS" \ "$url" | sed -n '1,12p' } if [[ "$SKIP_VERIFY" != "1" ]]; then verify_url "$PUBLIC_URL/" verify_url "$PUBLIC_URL/admin" fi echo "Deploy complete: $REVISION"