90 lines
3.0 KiB
Bash
Executable File
90 lines
3.0 KiB
Bash
Executable File
#!/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}"
|
|
ALLOW_DIRTY="${ALLOW_DIRTY:-0}"
|
|
DEPLOY_CADDY="${DEPLOY_CADDY:-0}"
|
|
RUN_MIGRATIONS="${RUN_MIGRATIONS:-0}"
|
|
SKIP_VERIFY="${SKIP_VERIFY:-0}"
|
|
|
|
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 [[ "$ALLOW_DIRTY" != "1" && -n "$(git status --porcelain)" ]]; then
|
|
echo "Refusing to deploy a dirty worktree." >&2
|
|
echo "Commit or stash changes first, or run with ALLOW_DIRTY=1 for an intentional ad-hoc deploy." >&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 '.env' \
|
|
--include '.env.example' \
|
|
--exclude '.env.*' \
|
|
--exclude '.next/' \
|
|
--exclude 'node_modules/' \
|
|
--exclude 'content.db' \
|
|
--exclude 'public/media/' \
|
|
--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 -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}}'
|
|
"
|
|
|
|
if [[ "$SKIP_VERIFY" != "1" ]]; then
|
|
echo "Verifying $PUBLIC_URL"
|
|
curl -fsSIL --max-time 20 "$PUBLIC_URL/" | sed -n '1,12p'
|
|
curl -fsSIL --max-time 20 "$PUBLIC_URL/admin" | sed -n '1,12p'
|
|
fi
|
|
|
|
echo "Deploy complete: $REVISION"
|