Files
harheimertc/deploy-production.sh
Torsten Schulz (local) 3577831149
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 48s
Enhance deployment script to handle symlinks and ensure proper data restoration from backup
This commit updates the deploy-production.sh script to check if the server/data directory is a symlink and removes it if necessary, ensuring that data is copied to a real directory. It also modifies the data restoration process to follow symlinks when copying from the backup, improving the reliability of data recovery during deployment.
2026-01-17 18:54:05 +01:00

447 lines
15 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# Immer im Repo-Verzeichnis arbeiten (wichtig für Backup/Restore mit relativen Pfaden)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Deployment Script für Harheimer TC Website
# Sichert Produktivdaten vor dem Build und stellt sie danach wieder her
echo "=== Harheimer TC Deployment ==="
echo ""
echo "Working directory: $(pwd)"
echo ""
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "ERROR: Dieses Script muss im Git-Repository ausgeführt werden (kein .git gefunden)."
exit 1
fi
# Optional (empfohlen): Persistente Daten außerhalb des Git-Repos halten und per Symlink einbinden.
# Das verhindert zuverlässig, dass Git jemals Produktivdaten überschreibt.
DEPLOY_HOME="${DEPLOY_HOME:-${HOME:-/tmp}}"
DATA_ROOT="${DATA_ROOT:-$DEPLOY_HOME/harheimertc-data}"
BACKUP_ROOT="${BACKUP_ROOT:-$DEPLOY_HOME/harheimertc-backups}"
mkdir -p "$DATA_ROOT" "$BACKUP_ROOT"
ensure_symlink_dir() {
local src="$1" # z.B. server/data
local target="$2" # z.B. /var/lib/harheimertc/server-data
mkdir -p "$(dirname "$src")"
mkdir -p "$target"
if [ -L "$src" ]; then
return 0
fi
if [ -d "$src" ]; then
echo " Moving $src -> $target (first-time migration)"
# Merge existing content into target
cp -a "$src/." "$target/" || true
rm -rf "$src"
fi
ln -s "$target" "$src"
echo " Linked $src -> $target"
}
has_tracked_files_under() {
local prefix="$1" # e.g. public/data
# If any file is tracked under this path, symlinking the directory will break git operations
git ls-files "$prefix" | head -n 1 | grep -q .
}
echo "0. Ensuring persistent data directories (recommended)..."
# IMPORTANT: Only symlink server/data if it's not tracked by git.
if has_tracked_files_under "server/data"; then
echo " Skipping symlink for server/data (tracked files detected in git)."
echo " Recommendation: remove server/data/** from git history and keep them only as production data."
else
ensure_symlink_dir "server/data" "$DATA_ROOT/server-data"
fi
# IMPORTANT: Only symlink public/data if it's not tracked by git.
# Otherwise git will error with "path is beyond a symbolic link".
if has_tracked_files_under "public/data"; then
echo " Skipping symlink for public/data (tracked files detected in git)."
echo " Recommendation: remove public/data/*.csv from git history and keep them only as production data."
else
ensure_symlink_dir "public/data" "$DATA_ROOT/public-data"
fi
ensure_symlink_dir "public/uploads" "$DATA_ROOT/public-uploads"
echo ""
# 1. BACKUP: Laufende Produktivdaten VOR allen Git-Operationen sichern
echo "1. Backing up current production data (pre-git)..."
# Human readable timestamp (lokal)
BACKUP_TS="$(date +"%Y-%m-%d_%H-%M-%S")"
BACKUP_DIR="$BACKUP_ROOT/backup_$BACKUP_TS"
mkdir -p "$BACKUP_DIR"
echo " Backup directory: $BACKUP_DIR"
# Backup server data (JSON) und CSVs immer vom Dateisystem, nicht aus 'stash'
if [ -d server/data ]; then
cp -a server/data "$BACKUP_DIR/server-data"
echo " Backed up server/data -> $BACKUP_DIR/server-data"
else
echo "ERROR: server/data existiert nicht. Abbruch, damit wir keine Repo-Defaults ausrollen."
exit 1
fi
if ls public/data/*.csv >/dev/null 2>&1; then
mkdir -p "$BACKUP_DIR/public-data"
cp -a public/data/*.csv "$BACKUP_DIR/public-data/"
echo " Backed up public/data/*.csv -> $BACKUP_DIR/public-data/"
else
echo " No public CSVs to backup (public/data/*.csv not found)"
fi
# 2. Handle local changes and Git Pull
echo "2. Handling local changes and pulling latest from git..."
# Check if there are merge conflicts first
if [ -n "$(git status --porcelain | grep '^UU\|^AA\|^DD')" ]; then
echo " Resolving existing merge conflicts..."
git reset --hard HEAD
fi
# Ensure git operations can run even if we have untracked local artifacts (e.g. backups/)
# We avoid `git stash` (breaks with symlinked tracked paths). Instead, hard-reset and clean safely.
echo " Resetting working tree to HEAD (production data will be restored from backup)..."
git reset --hard HEAD
echo " Cleaning untracked files (excluding data dirs/backups/.env)..."
git clean -fd \
-e server/data \
-e public/data \
-e public/uploads \
-e backups \
-e .env || true
# Pull latest changes
echo " Pulling latest changes..."
git pull
# Reset any accidental changes from stash restore (should be none now)
git reset --hard HEAD >/dev/null 2>&1
# WICHTIG: Entferne public/data Dateien aus Git-Index, falls sie getrackt sind
# (Sie sollten in .gitignore sein, aber falls sie historisch getrackt wurden)
if git ls-files --error-unmatch public/data/*.csv >/dev/null 2>&1; then
echo " WARNING: public/data/*.csv Dateien sind im Git getrackt!"
echo " Entferne sie aus dem Git-Index (Dateien bleiben erhalten)..."
git rm --cached public/data/*.csv 2>/dev/null || true
fi
# 3. Install dependencies
echo ""
echo "3. Installing dependencies..."
npm install
# 4. Remove old build (but keep data!)
echo ""
echo "4. Cleaning build artifacts..."
# Sicherstellen, dass .output vollständig gelöscht wird
if [ -d ".output" ]; then
echo " Removing .output directory..."
rm -rf .output
# Prüfen, ob wirklich gelöscht wurde
if [ -d ".output" ]; then
echo "WARNING: .output konnte nicht vollständig gelöscht werden. Versuche erneut..."
sleep 2
rm -rf .output
if [ -d ".output" ]; then
echo "ERROR: .output konnte auch nach erneutem Versuch nicht gelöscht werden!"
echo "Bitte manuell prüfen und löschen: rm -rf .output"
exit 1
fi
fi
echo " ✓ .output gelöscht"
fi
# Auch .nuxt Cache löschen für sauberen Build
if [ -d ".nuxt" ]; then
echo " Removing .nuxt cache..."
rm -rf .nuxt
echo " ✓ .nuxt gelöscht"
fi
# Prüfe, ob node_modules vorhanden ist (für npm run build)
if [ ! -d "node_modules" ]; then
echo ""
echo "WARNING: node_modules fehlt. Installiere Dependencies..."
npm install
fi
# 5. Build
echo ""
echo "5. Building application..."
echo " Running: npm run build"
echo " (This may take a few minutes...)"
# Clean-Build: Stelle sicher, dass node_modules aktuell ist
echo " Checking dependencies..."
if [ ! -f "node_modules/.package-lock.json" ] && [ ! -f "package-lock.json" ]; then
echo " WARNING: package-lock.json fehlt. Führe npm install aus..."
npm install
fi
# Build mit expliziter Fehlerbehandlung und Output-Capture
BUILD_OUTPUT=$(npm run build 2>&1)
BUILD_EXIT_CODE=$?
# Zeige Build-Output
echo "$BUILD_OUTPUT"
if [ "$BUILD_EXIT_CODE" -ne 0 ]; then
echo ""
echo "ERROR: Build fehlgeschlagen mit Exit-Code $BUILD_EXIT_CODE"
echo "Bitte prüfen Sie die Build-Ausgabe oben auf Fehler."
exit 1
fi
# Prüfe auf Warnungen im Build-Output, die auf Probleme hinweisen
if echo "$BUILD_OUTPUT" | grep -qi "error\|failed\|missing"; then
echo ""
echo "WARNING: Build-Output enthält möglicherweise Fehler oder Warnungen."
echo "Bitte prüfen Sie die Ausgabe oben."
fi
# Prüfe, ob der Build erfolgreich war - mehrere Checks
echo ""
echo " Verifying build output..."
BUILD_FAILED=0
# Check 1: _nuxt Verzeichnis
if [ ! -d ".output/public/_nuxt" ]; then
echo "ERROR: .output/public/_nuxt Verzeichnis fehlt!"
BUILD_FAILED=1
else
NUXT_FILES=$(find .output/public/_nuxt -type f 2>/dev/null | wc -l)
echo " ✓ .output/public/_nuxt vorhanden ($NUXT_FILES Dateien)"
if [ "$NUXT_FILES" -eq 0 ]; then
echo "ERROR: .output/public/_nuxt ist leer!"
BUILD_FAILED=1
else
# Prüfe, ob wichtige Dateien vorhanden sind
JS_FILES=$(find .output/public/_nuxt -name "*.js" 2>/dev/null | wc -l)
CSS_FILES=$(find .output/public/_nuxt -name "*.css" 2>/dev/null | wc -l)
echo " ✓ JS-Dateien: $JS_FILES, CSS-Dateien: $CSS_FILES"
if [ "$JS_FILES" -eq 0 ]; then
echo "ERROR: Keine JS-Dateien in .output/public/_nuxt gefunden!"
BUILD_FAILED=1
fi
fi
fi
# Check 1b: _nuxt/builds Verzeichnis (für Meta-Dateien)
if [ -d ".output/public/_nuxt/builds" ]; then
BUILD_META_FILES=$(find .output/public/_nuxt/builds -type f 2>/dev/null | wc -l)
echo " ✓ .output/public/_nuxt/builds vorhanden ($BUILD_META_FILES Meta-Dateien)"
else
echo "WARNING: .output/public/_nuxt/builds fehlt (kann in manchen Nuxt-Versionen normal sein)"
fi
# Check 2: Server index.mjs
if [ ! -f ".output/server/index.mjs" ]; then
echo "ERROR: .output/server/index.mjs fehlt!"
BUILD_FAILED=1
else
echo " ✓ .output/server/index.mjs vorhanden"
fi
# Check 3: Public Verzeichnis
if [ ! -d ".output/public" ]; then
echo "ERROR: .output/public Verzeichnis fehlt!"
BUILD_FAILED=1
else
echo " ✓ .output/public vorhanden"
fi
# Check 4: Server Verzeichnis
if [ ! -d ".output/server" ]; then
echo "ERROR: .output/server Verzeichnis fehlt!"
BUILD_FAILED=1
else
echo " ✓ .output/server vorhanden"
fi
if [ "$BUILD_FAILED" -eq 1 ]; then
echo ""
echo "ERROR: Build-Verifikation fehlgeschlagen!"
echo "Bitte führen Sie manuell aus:"
echo " rm -rf .output .nuxt"
echo " npm run build"
exit 1
fi
echo " ✓ Build erfolgreich verifiziert"
# 6. Restore Production Data (überschreibe Repo-Defaults mit Backup)
echo ""
echo "6. Restoring production data..."
# Stelle server/data vollständig wieder her (inkl. config.json, users.json, news.json, sessions.json, members.json, membership-applications)
if [ ! -d "$BACKUP_DIR/server-data" ]; then
echo "ERROR: Backup-Verzeichnis $BACKUP_DIR/server-data fehlt. Abbruch."
exit 1
fi
# WICHTIG: Prüfe, ob server/data ein Symlink ist (z.B. auf Backup-Verzeichnis)
# Falls ja, entferne den Symlink, damit wir in das echte Verzeichnis kopieren können
if [ -L "server/data" ]; then
echo " WARNING: server/data ist ein Symlink. Entferne Symlink..."
rm "server/data"
fi
# Stelle sicher, dass server/data ein echtes Verzeichnis ist
if [ ! -d "server/data" ]; then
mkdir -p server/data
fi
# Kopiere Daten vom Backup (verwende -L, um Symlinks zu folgen, falls nötig)
cp -aL "$BACKUP_DIR/server-data/." server/data/
echo " Restored server/data from backup ($BACKUP_DIR/server-data)."
# Stelle alle CSVs wieder her
if ls "$BACKUP_DIR/public-data"/*.csv >/dev/null 2>&1; then
mkdir -p public/data
# WICHTIG: Überschreibe auch Dateien, die aus dem Git-Repository kommen
# Verwende cp mit -f (force) um sicherzustellen, dass Backup-Dateien Vorrang haben
for csv_file in "$BACKUP_DIR/public-data"/*.csv; do
filename=$(basename "$csv_file")
# Überschreibe explizit, auch wenn Datei bereits existiert
cp -f "$csv_file" "public/data/$filename"
# Stelle sicher, dass die Datei wirklich überschrieben wurde
if [ -f "public/data/$filename" ]; then
# Prüfe, ob die Datei wirklich vom Backup kommt (Größenvergleich)
backup_size=$(stat -f%z "$csv_file" 2>/dev/null || stat -c%s "$csv_file" 2>/dev/null || echo "0")
restored_size=$(stat -f%z "public/data/$filename" 2>/dev/null || stat -c%s "public/data/$filename" 2>/dev/null || echo "0")
if [ "$backup_size" = "$restored_size" ] && [ "$backup_size" != "0" ]; then
echo " ✓ Restored public/data/$filename from backup ($backup_size bytes)"
else
echo " ⚠ WARNING: public/data/$filename Größe stimmt nicht überein (Backup: $backup_size, Restored: $restored_size)"
fi
else
echo " ❌ ERROR: Konnte public/data/$filename nicht wiederherstellen!"
fi
done
echo " ✓ All public/data/*.csv files restored from backup ($BACKUP_DIR/public-data)."
# Zusätzliche Sicherheit: Entferne public/data Dateien aus Git-Index, falls sie getrackt sind
# (nach dem Restore, damit sie nicht beim nächsten git reset überschrieben werden)
if git ls-files --error-unmatch public/data/*.csv >/dev/null 2>&1; then
echo " WARNING: public/data/*.csv Dateien sind noch im Git getrackt!"
echo " Entferne sie aus dem Git-Index (Dateien bleiben erhalten)..."
git rm --cached public/data/*.csv 2>/dev/null || true
echo " ✓ public/data/*.csv aus Git-Index entfernt"
fi
else
echo " No public CSVs to restore"
fi
# Sanity Check: users.json muss existieren und darf nicht leer sein
if [ ! -s server/data/users.json ]; then
echo "ERROR: server/data/users.json fehlt oder ist leer nach Restore. Abbruch."
exit 1
fi
# 7. Cleanup (Backups werden bewusst behalten)
echo ""
echo "7. Keeping backups in $BACKUP_ROOT (no git stash used)."
# 8. Restart PM2
echo ""
echo "8. Restarting PM2..."
# Prüfe, ob PM2 installiert ist
if ! command -v pm2 &> /dev/null; then
echo "ERROR: PM2 ist nicht installiert oder nicht im PATH!"
echo "Bitte installieren Sie PM2: npm install -g pm2"
exit 1
fi
# Funktion zum Starten/Neustarten einer PM2-Instanz
restart_pm2_instance() {
local instance_name=$1
if ! pm2 describe "$instance_name" &> /dev/null; then
echo " WARNING: PM2-Prozess '$instance_name' existiert nicht."
echo " Versuche, den Prozess zu starten..."
if pm2 start harheimertc.config.cjs --only "$instance_name" --update-env; then
echo " ✓ PM2-Prozess '$instance_name' gestartet"
return 0
else
echo " ERROR: Konnte PM2-Prozess '$instance_name' nicht starten."
return 1
fi
else
echo " Restarting $instance_name with --update-env..."
if pm2 restart "$instance_name" --update-env; then
echo " ✓ PM2-Prozess '$instance_name' neu gestartet"
return 0
else
echo " ERROR: PM2-Restart für '$instance_name' fehlgeschlagen!"
return 1
fi
fi
}
# Starte/Neustarte beide Instanzen
INSTANCE_ERRORS=0
if ! restart_pm2_instance "harheimertc"; then
INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1))
fi
if ! restart_pm2_instance "harheimertc-3102"; then
INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1))
fi
# Prüfe, ob beide Prozesse laufen
sleep 2
echo ""
echo " Checking PM2 instances status..."
if pm2 describe harheimertc | grep -q "online"; then
echo " ✓ PM2-Prozess 'harheimertc' läuft (online)"
else
echo " WARNING: PM2-Prozess 'harheimertc' ist nicht online. Prüfe Logs: pm2 logs harheimertc"
INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1))
fi
if pm2 describe harheimertc-3102 | grep -q "online"; then
echo " ✓ PM2-Prozess 'harheimertc-3102' läuft (online)"
else
echo " WARNING: PM2-Prozess 'harheimertc-3102' ist nicht online. Prüfe Logs: pm2 logs harheimertc-3102"
INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1))
fi
if [ "$INSTANCE_ERRORS" -gt 0 ]; then
echo ""
echo "WARNING: Einige PM2-Instanzen haben Probleme. Bitte manuell prüfen:"
echo " pm2 status"
echo " pm2 logs harheimertc"
echo " pm2 logs harheimertc-3102"
fi
echo ""
echo "=== Deployment completed successfully! ==="
echo "The application is now running with the latest code and your production data preserved."
echo ""
echo "Useful commands:"
echo " pm2 logs harheimertc # View logs (Port 3100)"
echo " pm2 logs harheimertc-3102 # View logs (Port 3102)"
echo " pm2 status # View status"
echo " pm2 restart harheimertc # Restart instance on port 3100"
echo " pm2 restart harheimertc-3102 # Restart instance on port 3102"
echo " pm2 restart all # Restart all instances"