186 lines
7.5 KiB
Bash
Executable File
186 lines
7.5 KiB
Bash
Executable File
#!/bin/bash
|
||
# deploy.sh – Rebreak Deploy Script (backend/-Layout, post-cutover)
|
||
#
|
||
# Wird vom Webhook-Listener (scripts/deploy-webhook/server.mjs) aufgerufen.
|
||
# Repo-Root: /srv/rebreak
|
||
# Clone: git@github.com:RaynisDev/rebreak.git
|
||
# Backend: /srv/rebreak/backend (standalone Nitro, package: rebreak-backend)
|
||
#
|
||
# WICHTIG: GitHub Actions ist der primaere Deploy-Weg (deploy-from-artifact.sh).
|
||
# Dieses Script ist Fallback/Legacy-Pfad und wird NICHT ausgefuehrt wenn
|
||
# GH-Actions gerade deployed (.deploy-ga.lock).
|
||
#
|
||
# Ablauf:
|
||
# 1. Git pull (via Deploy-Key)
|
||
# 2. pnpm install --frozen-lockfile (mit hoisted node-linker via .npmrc)
|
||
# 3. cd backend && pnpm --filter rebreak-backend build (Prisma generate + Nitro build)
|
||
# 4. .output → .output-staging (atomisch via tmp)
|
||
# 5. pm2 restart rebreak-staging --update-env
|
||
# 6. pm2 restart rebreak-imap-staging / rebreak-idle-staging (best-effort, falls vorhanden)
|
||
# 7. pm2 restart dns-rebreak-staging / dns-rebreak (best-effort, falls vorhanden)
|
||
#
|
||
# Secrets: via Infisical (INFISICAL_CLIENT_ID/SECRET in /etc/environment) — NICHT hier,
|
||
# sondern in start-staging.sh / ecosystem.config.js zur Laufzeit.
|
||
|
||
set -euo pipefail
|
||
|
||
REPO_ROOT="/srv/rebreak"
|
||
APP_DIR="${REPO_ROOT}/backend"
|
||
NODE_BIN="/root/.nvm/versions/node/v24.11.1/bin/node"
|
||
PNPM_BIN="/root/.nvm/versions/node/v24.11.1/bin/pnpm"
|
||
PM2_BIN="/root/.nvm/versions/node/v24.11.1/bin/pm2"
|
||
GA_LOCK="${REPO_ROOT}/.deploy-ga.lock"
|
||
|
||
log() { echo "[deploy] $(date '+%H:%M:%S') $*"; }
|
||
log_err() { echo "[deploy:err] $(date '+%H:%M:%S') $*" >&2; }
|
||
|
||
log "=== Rebreak Deploy gestartet (backend/-Layout) ==="
|
||
|
||
# 0. Sicherstellen dass PATH stimmt
|
||
export PATH="/root/.nvm/versions/node/v24.11.1/bin:$PATH"
|
||
|
||
# 0a. GH-Actions-Lock pruefen: wenn deploy-from-artifact.sh laeuft, nicht doppeln.
|
||
if [[ -f "$GA_LOCK" ]]; then
|
||
LOCK_PID=$(cat "$GA_LOCK" 2>/dev/null || echo "")
|
||
if [[ -n "$LOCK_PID" ]] && kill -0 "$LOCK_PID" 2>/dev/null; then
|
||
log "GitHub-Actions-Deploy laeuft gerade (PID $LOCK_PID) -- Webhook-Deploy abgebrochen (kein Fehler)"
|
||
exit 0
|
||
else
|
||
log "Staler GA-Lock gefunden (PID $LOCK_PID) -- wird ignoriert"
|
||
rm -f "$GA_LOCK"
|
||
fi
|
||
fi
|
||
|
||
# 1. Git pull via Deploy-Key (SSH ist konfiguriert in /root/.ssh/config)
|
||
log "Step 1: git pull..."
|
||
cd "${REPO_ROOT}"
|
||
git fetch origin main 2>&1
|
||
git reset --hard origin/main 2>&1
|
||
git clean -fd 2>&1
|
||
log "Git updated to $(git rev-parse --short HEAD)"
|
||
|
||
# 2. pnpm install (workspace-root, .npmrc mit node-linker=hoisted ist im Repo)
|
||
log "Step 2: pnpm install --frozen-lockfile..."
|
||
cd "${REPO_ROOT}"
|
||
CI=true "${PNPM_BIN}" install --frozen-lockfile 2>&1 || {
|
||
log_err "frozen-lockfile fehlgeschlagen, fallback ohne frozen..."
|
||
CI=true "${PNPM_BIN}" install --no-frozen-lockfile 2>&1
|
||
}
|
||
log "pnpm install done"
|
||
|
||
# 2.5 Prisma-Migration auto-deploy wenn neue migration-files committed wurden
|
||
# Detect via git diff zwischen .last-deployed-sha und HEAD.
|
||
# Idempotent: prisma migrate deploy skipped already-applied migrations.
|
||
# Failure-Mode: bei Migration-Fehler pm2 NICHT restarten (alter Code/alte DB konsistent).
|
||
# First-deploy-Edge-Case: wenn .last-deployed-sha fehlt → Migration ausführen (sicher
|
||
# weil idempotent).
|
||
log "Step 2.5: Migration-Check..."
|
||
PREV_SHA=$(cat "${REPO_ROOT}/.last-deployed-sha" 2>/dev/null || echo "")
|
||
CUR_SHA=$(git -C "${REPO_ROOT}" rev-parse HEAD)
|
||
|
||
run_migration=false
|
||
if [[ -z "$PREV_SHA" ]]; then
|
||
log "Kein .last-deployed-sha gefunden — first-deploy: Migration sicherheitshalber ausführen"
|
||
run_migration=true
|
||
elif ! git -C "${REPO_ROOT}" diff --quiet "$PREV_SHA"..HEAD -- backend/prisma/migrations/ backend/prisma/schema.prisma; then
|
||
log "Migration-Changes detected zwischen ${PREV_SHA} und ${CUR_SHA}"
|
||
run_migration=true
|
||
else
|
||
log "Keine Migration-Changes seit ${PREV_SHA} — skip migrate deploy"
|
||
fi
|
||
|
||
if $run_migration; then
|
||
log "Running prisma migrate deploy..."
|
||
cd "${APP_DIR}"
|
||
|
||
# Infisical-Wrapper für DATABASE_URL (analog start-staging.sh)
|
||
source /etc/environment
|
||
if [[ -z "${INFISICAL_CLIENT_ID:-}" || -z "${INFISICAL_CLIENT_SECRET:-}" ]]; then
|
||
log_err "INFISICAL_CLIENT_ID / INFISICAL_CLIENT_SECRET fehlt — kann Migration nicht ausführen"
|
||
exit 1
|
||
fi
|
||
|
||
INFISICAL_TOKEN=$(infisical login \
|
||
--method=universal-auth \
|
||
--client-id="${INFISICAL_CLIENT_ID}" \
|
||
--client-secret="${INFISICAL_CLIENT_SECRET}" \
|
||
--silent --plain 2>/dev/null)
|
||
|
||
if [[ -z "$INFISICAL_TOKEN" ]]; then
|
||
log_err "Infisical login fehlgeschlagen — Migration abgebrochen"
|
||
exit 1
|
||
fi
|
||
|
||
# DATABASE_URL injecten via infisical run; Aliasing analog start-staging.sh
|
||
# (Infisical-Secret heißt evtl. NUXT_DATABASE_URL, prisma erwartet DATABASE_URL).
|
||
infisical run \
|
||
--projectId="${INFISICAL_PROJECT_ID:-14b11b35-ef59-4b8a-a16b-398f0cc3ad93}" \
|
||
--env=staging \
|
||
--token="$INFISICAL_TOKEN" \
|
||
-- bash -c '
|
||
set -e
|
||
export DATABASE_URL="${DATABASE_URL:-${NUXT_DATABASE_URL:-}}"
|
||
if [[ -z "$DATABASE_URL" ]]; then
|
||
echo "[deploy:err] DATABASE_URL nicht in Infisical-staging — Migration abgebrochen" >&2
|
||
exit 1
|
||
fi
|
||
"'"${PNPM_BIN}"'" prisma migrate deploy --schema prisma/schema.prisma
|
||
' 2>&1 || {
|
||
log_err "Migration-Deploy fehlgeschlagen — pm2-restart ABGEBROCHEN (Daten-Konsistenz-Schutz)"
|
||
exit 1
|
||
}
|
||
|
||
log "Migration done"
|
||
fi
|
||
|
||
# 3. Build backend (Nitro standalone) — Prisma generate ist Teil des build-scripts
|
||
# RAM-constraint (3.7GB VPS): pm2-Services VOR build stoppen, sonst OOM (exit 134).
|
||
# Heap auf 2.5GB erhöhen — sonst hängt nitro build bei 1.5GB mark-compact-loop.
|
||
log "Step 3a: pm2 stop staging services (RAM für build freigeben)..."
|
||
"${PM2_BIN}" stop rebreak-staging rebreak-admin-staging rebreak-idle-staging 2>/dev/null || true
|
||
sleep 2
|
||
|
||
log "Step 3b: pnpm --filter rebreak-backend build..."
|
||
cd "${APP_DIR}"
|
||
NODE_OPTIONS="--max-old-space-size=2560" CI=true "${PNPM_BIN}" --filter rebreak-backend build 2>&1
|
||
log "Build done"
|
||
|
||
# 4. Atomisches Deploy: .output → .output-staging (relativ zu backend/)
|
||
log "Step 4: Atomisches Deploy .output → .output-staging..."
|
||
cd "${APP_DIR}"
|
||
if [ -d ".output" ]; then
|
||
rm -rf .output-staging-new
|
||
cp -r .output .output-staging-new
|
||
rm -rf .output-staging
|
||
mv .output-staging-new .output-staging
|
||
log ".output-staging aktualisiert"
|
||
else
|
||
log_err "FEHLER: .output Verzeichnis nicht gefunden nach Build!"
|
||
exit 1
|
||
fi
|
||
|
||
# 5. pm2 start/restart staging-services (--update-env zieht neue Infisical-secrets)
|
||
log "Step 5: pm2 start rebreak-staging + rebreak-admin-staging + rebreak-idle-staging..."
|
||
"${PM2_BIN}" start rebreak-staging --update-env 2>/dev/null || \
|
||
"${PM2_BIN}" start "${REPO_ROOT}/ecosystem.config.js" --only rebreak-staging
|
||
"${PM2_BIN}" start rebreak-admin-staging --update-env 2>/dev/null || true
|
||
"${PM2_BIN}" start rebreak-idle-staging --update-env 2>/dev/null || true
|
||
log "staging-services started"
|
||
|
||
# 6. IMAP + DNS Services (optional – kein Fehler wenn nicht vorhanden, Mo's Scope)
|
||
log "Step 6: Optional services restart..."
|
||
"${PM2_BIN}" restart rebreak-imap-staging 2>/dev/null || true
|
||
"${PM2_BIN}" restart dns-rebreak-staging 2>/dev/null || \
|
||
"${PM2_BIN}" start "${REPO_ROOT}/ecosystem.config.js" --only dns-rebreak-staging 2>/dev/null || true
|
||
"${PM2_BIN}" restart dns-rebreak 2>/dev/null || \
|
||
"${PM2_BIN}" start "${REPO_ROOT}/ecosystem.config.js" --only dns-rebreak 2>/dev/null || true
|
||
|
||
# 7. pm2 save
|
||
"${PM2_BIN}" save 2>/dev/null || true
|
||
|
||
# 8. Last-deployed-SHA persistieren (für Step 2.5 beim nächsten Deploy)
|
||
echo "${CUR_SHA}" > "${REPO_ROOT}/.last-deployed-sha"
|
||
log "Last-deployed-SHA gespeichert: ${CUR_SHA}"
|
||
|
||
log "=== Deploy erfolgreich: $(git -C ${REPO_ROOT} rev-parse --short HEAD) ==="
|