feat: Deploy-Scripts für VServer (#8)
- scripts/deploy.sh: Code-Sync + Docker-Build + Restart + Health-Check - Optionen: --build-only, --skip-frontend, --full - Nutzt tar+scp (kein rsync auf VServer) - Automatischer Health-Check nach Deploy - scripts/deploy-db.sh: DB-Upload mit Migrationen - Optionen: --dry-run, --migrate-only, --skip-migrate - Schema-Validierung vor Upload - Backup + Container-Stop + Upload + Migrationen (FTS5, Strang, Fristen) - docker-compose.yml: Obsoletes 'version' entfernt Closes #8
This commit is contained in:
parent
abcb0ff8a2
commit
0ef5910eb4
@ -1,78 +1,148 @@
|
||||
#!/bin/bash
|
||||
# Deploy DB zum VServer
|
||||
# Usage: ./scripts/deploy-db.sh [--dry-run]
|
||||
set -e
|
||||
# Usage: ./scripts/deploy-db.sh [--dry-run] [--migrate-only] [--skip-migrate]
|
||||
#
|
||||
# Modes:
|
||||
# (default) Upload DB + run migrations + restart container
|
||||
# --dry-run Nur anzeigen, nichts ändern
|
||||
# --migrate-only Nur Migrationen auf Remote-DB ausführen (kein Upload)
|
||||
# --skip-migrate DB hochladen, aber keine Migrationen
|
||||
#
|
||||
# Migrationen: FTS5, Strang, Fristen
|
||||
|
||||
LOCAL_DB="data/tracker_remote.db"
|
||||
set -euo pipefail
|
||||
|
||||
LOCAL_DB="data/tracker.db"
|
||||
REMOTE_DB="/opt/antragstracker/data/tracker.db"
|
||||
REMOTE_HOST="vserver"
|
||||
CONTAINER="antragstracker-hagen"
|
||||
DRY_RUN=false
|
||||
MIGRATE_ONLY=false
|
||||
SKIP_MIGRATE=false
|
||||
|
||||
if [ "$1" = "--dry-run" ]; then
|
||||
DRY_RUN=true
|
||||
echo "🔍 DRY RUN — keine Änderungen"
|
||||
echo ""
|
||||
fi
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--migrate-only) MIGRATE_ONLY=true ;;
|
||||
--skip-migrate) SKIP_MIGRATE=true ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--dry-run] [--migrate-only] [--skip-migrate]"
|
||||
exit 0 ;;
|
||||
*) echo "Unknown arg: $arg"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "=== DB Deploy ==="
|
||||
echo "Lokale DB: $(du -sh $LOCAL_DB | cut -f1)"
|
||||
|
||||
# 0. Schema-Validierung
|
||||
echo "0. Schema prüfen..."
|
||||
EXPECTED_TABLES="vorlagen beratungen ketten ketten_glieder ki_bewertungen parteien antragsteller gremien orte referenzen"
|
||||
MISSING=""
|
||||
for tbl in $EXPECTED_TABLES; do
|
||||
# --- Schema-Validierung (nur bei Upload) ---
|
||||
if [ "$MIGRATE_ONLY" = false ]; then
|
||||
# Fallback zu tracker_remote.db wenn tracker.db nicht existiert
|
||||
if [ ! -f "$LOCAL_DB" ]; then
|
||||
if [ -f "data/tracker_remote.db" ]; then
|
||||
LOCAL_DB="data/tracker_remote.db"
|
||||
echo " → Nutze data/tracker_remote.db"
|
||||
else
|
||||
echo "❌ Keine lokale DB gefunden (data/tracker.db oder data/tracker_remote.db)"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Lokale DB: $(du -sh $LOCAL_DB | cut -f1)"
|
||||
|
||||
echo "Schema prüfen..."
|
||||
EXPECTED_TABLES="vorlagen beratungen ketten ketten_glieder ki_bewertungen parteien antragsteller gremien orte referenzen"
|
||||
MISSING=""
|
||||
for tbl in $EXPECTED_TABLES; do
|
||||
EXISTS=$(sqlite3 "$LOCAL_DB" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='$tbl'" 2>/dev/null)
|
||||
if [ "$EXISTS" != "1" ]; then
|
||||
MISSING="$MISSING $tbl"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ -n "$MISSING" ]; then
|
||||
echo "❌ FEHLER: Fehlende Tabellen:$MISSING"
|
||||
if [ -n "$MISSING" ]; then
|
||||
echo "❌ Fehlende Tabellen:$MISSING"
|
||||
exit 1
|
||||
fi
|
||||
echo " ✓ Alle ${#EXPECTED_TABLES[@]} Tabellen vorhanden"
|
||||
fi
|
||||
echo " ✓ Alle Tabellen vorhanden"
|
||||
|
||||
# Stats
|
||||
echo ""
|
||||
echo " Vorlagen: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM vorlagen')"
|
||||
echo " Ketten: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM ketten')"
|
||||
echo " KI-Bew.: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM ki_bewertungen')"
|
||||
echo " Matches: $(sqlite3 "$LOCAL_DB" "SELECT COUNT(*) FROM ki_bewertungen WHERE typ='umsetzung_match'")"
|
||||
echo " Orte: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM orte WHERE lat IS NOT NULL')"
|
||||
echo ""
|
||||
|
||||
# Flush WAL journal
|
||||
echo " Flushing WAL..."
|
||||
sqlite3 "$LOCAL_DB" "PRAGMA wal_checkpoint(TRUNCATE)" 2>/dev/null
|
||||
echo ""
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "🔍 Würde hochladen: $(du -sh $LOCAL_DB | cut -f1) nach $REMOTE_HOST:$REMOTE_DB"
|
||||
echo "🔍 Prod-DB Größe: $(ssh $REMOTE_HOST "du -sh $REMOTE_DB 2>/dev/null | cut -f1" || echo "N/A")"
|
||||
# Stats
|
||||
echo ""
|
||||
echo "DRY RUN fertig. Ohne --dry-run ausführen zum Deployen."
|
||||
echo " Vorlagen: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM vorlagen')"
|
||||
echo " Ketten: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM ketten')"
|
||||
echo " KI-Bew.: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM ki_bewertungen')"
|
||||
echo " Orte: $(sqlite3 "$LOCAL_DB" 'SELECT COUNT(*) FROM orte WHERE lat IS NOT NULL')"
|
||||
echo ""
|
||||
|
||||
# Flush WAL journal
|
||||
echo " WAL flushen..."
|
||||
sqlite3 "$LOCAL_DB" "PRAGMA wal_checkpoint(TRUNCATE)" 2>/dev/null
|
||||
fi
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo ""
|
||||
echo "🔍 DRY RUN"
|
||||
if [ "$MIGRATE_ONLY" = false ]; then
|
||||
echo " Würde hochladen: $(du -sh $LOCAL_DB | cut -f1) nach $REMOTE_HOST:$REMOTE_DB"
|
||||
echo " Prod-DB: $(ssh $REMOTE_HOST "du -sh $REMOTE_DB 2>/dev/null | cut -f1" || echo "N/A")"
|
||||
fi
|
||||
if [ "$SKIP_MIGRATE" = false ]; then
|
||||
echo " Migrationen: FTS5, Strang, Fristen"
|
||||
fi
|
||||
echo ""
|
||||
echo "DRY RUN fertig."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 1. Backup auf VServer
|
||||
BACKUP_NAME="${REMOTE_DB}.bak-$(date +%Y%m%d-%H%M)"
|
||||
echo "1. Backup Prod-DB..."
|
||||
ssh $REMOTE_HOST "cp $REMOTE_DB $BACKUP_NAME"
|
||||
echo " → $BACKUP_NAME"
|
||||
# --- Upload ---
|
||||
if [ "$MIGRATE_ONLY" = false ]; then
|
||||
# Backup auf VServer
|
||||
BACKUP_NAME="${REMOTE_DB}.bak-$(date +%Y%m%d-%H%M)"
|
||||
echo "1. Backup Prod-DB..."
|
||||
ssh $REMOTE_HOST "cp $REMOTE_DB $BACKUP_NAME 2>/dev/null || echo ' (keine bestehende DB)'"
|
||||
echo " → $BACKUP_NAME"
|
||||
|
||||
# 2. Upload
|
||||
echo "2. Upload..."
|
||||
scp "$LOCAL_DB" "$REMOTE_HOST:$REMOTE_DB"
|
||||
# Container stoppen für sicheren DB-Write
|
||||
echo "2. Container stoppen..."
|
||||
ssh $REMOTE_HOST "cd /opt/antragstracker && docker compose stop antragstracker" 2>&1 | grep -v "level=warning" || true
|
||||
|
||||
# 3. Restart
|
||||
echo "3. Restart Backend..."
|
||||
ssh $REMOTE_HOST 'cd /opt/antragstracker && docker compose restart antragstracker' 2>&1 | grep -v "level=warning"
|
||||
echo "3. Upload..."
|
||||
scp "$LOCAL_DB" "$REMOTE_HOST:$REMOTE_DB"
|
||||
echo " ✓ Upload fertig"
|
||||
fi
|
||||
|
||||
# --- Migrationen ---
|
||||
if [ "$SKIP_MIGRATE" = false ]; then
|
||||
echo "4. Migrationen ausführen..."
|
||||
|
||||
# Migrationen im Docker-Container auf dem VServer
|
||||
ssh $REMOTE_HOST "cd /opt/antragstracker && docker compose run --rm --no-deps antragstracker python scripts/migrate_fts5.py /app/data/tracker.db" 2>&1 | grep -v "level=warning" || true
|
||||
echo " ✓ FTS5"
|
||||
|
||||
ssh $REMOTE_HOST "cd /opt/antragstracker && docker compose run --rm --no-deps antragstracker python scripts/migrate_strang.py" 2>&1 | grep -v "level=warning" || true
|
||||
echo " ✓ Strang"
|
||||
|
||||
ssh $REMOTE_HOST "cd /opt/antragstracker && docker compose run --rm --no-deps antragstracker python scripts/migrate_fristen.py" 2>&1 | grep -v "level=warning" || true
|
||||
echo " ✓ Fristen"
|
||||
fi
|
||||
|
||||
# --- Container starten ---
|
||||
echo "5. Container starten..."
|
||||
ssh $REMOTE_HOST "cd /opt/antragstracker && docker compose up -d" 2>&1 | grep -v "level=warning" || true
|
||||
|
||||
# --- Health-Check ---
|
||||
sleep 5
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://antraege.toppyr.de/api/health" 2>/dev/null || echo "000")
|
||||
if [ "$HTTP_STATUS" = "200" ]; then
|
||||
echo " ✓ Health-Check OK"
|
||||
else
|
||||
echo " ⚠️ Health-Check: HTTP $HTTP_STATUS"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ Deploy fertig"
|
||||
echo "Backup: $BACKUP_NAME"
|
||||
echo "Rollback: ssh $REMOTE_HOST 'cp $BACKUP_NAME $REMOTE_DB && cd /opt/antragstracker && docker compose restart antragstracker'"
|
||||
echo "✅ DB-Deploy fertig"
|
||||
if [ "$MIGRATE_ONLY" = false ]; then
|
||||
echo "Backup: $BACKUP_NAME"
|
||||
echo "Rollback: ssh $REMOTE_HOST 'cp $BACKUP_NAME $REMOTE_DB && cd /opt/antragstracker && docker compose restart antragstracker'"
|
||||
fi
|
||||
|
||||
113
scripts/deploy.sh
Executable file
113
scripts/deploy.sh
Executable file
@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
# Deploy Antragstracker auf VServer
|
||||
# Usage: ./scripts/deploy.sh [--build-only] [--full] [--skip-frontend]
|
||||
#
|
||||
# Modes:
|
||||
# (default) Build + deploy code, restart container
|
||||
# --build-only Build Docker image on server, don't restart
|
||||
# --full Full deploy: frontend build + code sync + docker rebuild + restart
|
||||
# --skip-frontend Skip local frontend build (use existing build/)
|
||||
#
|
||||
# Voraussetzung: ssh vserver funktioniert
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VSERVER="vserver"
|
||||
REMOTE_DIR="/opt/antragstracker"
|
||||
CONTAINER="antragstracker-hagen"
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
# Parse args
|
||||
BUILD_ONLY=false
|
||||
SKIP_FRONTEND=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--build-only) BUILD_ONLY=true ;;
|
||||
--full) ;; # default behavior
|
||||
--skip-frontend) SKIP_FRONTEND=true ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--build-only] [--full] [--skip-frontend]"
|
||||
exit 0 ;;
|
||||
*) echo "Unknown arg: $arg"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 1. Frontend bauen (lokal)
|
||||
if [ "$SKIP_FRONTEND" = false ]; then
|
||||
echo "🔨 Frontend bauen..."
|
||||
cd frontend && npm run build && cd ..
|
||||
echo " ✓ Frontend build fertig ($(du -sh frontend/build | cut -f1))"
|
||||
else
|
||||
echo "⏭️ Frontend-Build übersprungen"
|
||||
fi
|
||||
|
||||
# 2. Code zum VServer synchronisieren
|
||||
echo "📦 Code synchronisieren..."
|
||||
tar czf /tmp/antragstracker-deploy.tar.gz \
|
||||
--exclude='.git' \
|
||||
--exclude='.venv' \
|
||||
--exclude='venv' \
|
||||
--exclude='node_modules' \
|
||||
--exclude='frontend/.svelte-kit' \
|
||||
--exclude='data' \
|
||||
--exclude='*.log' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='.DS_Store' \
|
||||
--exclude='.pytest_cache' \
|
||||
--exclude='tracker.db*' \
|
||||
--exclude='antraege.db' \
|
||||
--exclude='.github' \
|
||||
--exclude='logs' \
|
||||
-C "$(dirname "$PROJECT_DIR")" "$(basename "$PROJECT_DIR")"
|
||||
|
||||
scp /tmp/antragstracker-deploy.tar.gz "$VSERVER:/tmp/"
|
||||
ssh "$VSERVER" "cd /opt && tar xzf /tmp/antragstracker-deploy.tar.gz && rm /tmp/antragstracker-deploy.tar.gz"
|
||||
rm -f /tmp/antragstracker-deploy.tar.gz
|
||||
echo " ✓ Code synchronisiert"
|
||||
|
||||
# 3. Docker Image bauen (auf VServer)
|
||||
echo "🐳 Docker Image bauen auf VServer..."
|
||||
ssh "$VSERVER" "cd $REMOTE_DIR && docker compose build --no-cache"
|
||||
echo " ✓ Image gebaut"
|
||||
|
||||
if [ "$BUILD_ONLY" = true ]; then
|
||||
echo "⏹️ Build-only Mode — Container wird nicht neu gestartet"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 4. Container neu starten
|
||||
echo "🔄 Container neu starten..."
|
||||
ssh "$VSERVER" "cd $REMOTE_DIR && docker compose up -d"
|
||||
echo " ✓ Container gestartet"
|
||||
|
||||
# 5. Warten + Health-Check
|
||||
echo "⏳ Warte auf Startup (5s)..."
|
||||
sleep 5
|
||||
|
||||
echo "🏥 Health-Check..."
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://antraege.toppyr.de/api/health" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "$HTTP_STATUS" = "200" ]; then
|
||||
echo " ✓ Health-Check OK (HTTP $HTTP_STATUS)"
|
||||
elif [ "$HTTP_STATUS" = "000" ]; then
|
||||
sleep 5
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://antraege.toppyr.de/api/health" 2>/dev/null || echo "000")
|
||||
if [ "$HTTP_STATUS" = "200" ]; then
|
||||
echo " ✓ Health-Check OK (HTTP $HTTP_STATUS, 2. Versuch)"
|
||||
else
|
||||
echo " ⚠️ Health-Check fehlgeschlagen (HTTP $HTTP_STATUS)"
|
||||
echo " Logs prüfen: ssh $VSERVER 'docker logs --tail 20 $CONTAINER'"
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ Health-Check: HTTP $HTTP_STATUS"
|
||||
echo " Logs prüfen: ssh $VSERVER 'docker logs --tail 20 $CONTAINER'"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ Deploy fertig!"
|
||||
echo " URL: https://antraege.toppyr.de"
|
||||
echo " Logs: ssh $VSERVER 'docker logs --tail 50 $CONTAINER'"
|
||||
Loading…
Reference in New Issue
Block a user