b2in/dev/betrieb/vorschau-auffrischen.sh
Kevin Adametz b5ed756d67 Git-Deploy per Tag, Betriebsüberwachung und Release-Seeder für den Livegang
- dev/betrieb/deploy.sh: Deploy nur per Tag, mit --seed (Seeder im Wartungsmodus)
  und --initial (Vorschau/Live einmalig an Ort und Stelle auf Git umstellen)
- dev/betrieb/vorschau-auffrischen.sh: Live-DB und Live-Medien in die Vorschau (Generalprobe)
- InteriorReleaseSeeder: alle Inhaltsschritte des Relaunchs in einem Lauf, idempotent;
  Displays, Playlists und Mediathek bleiben unberührt
- DisplayInteriorBrandingSeeder überschreibt keinen inzwischen gepflegten Claim mehr
- Monitoring: GlitchTip (Server + Browser), /health mit Token, Scheduler-Herzschlag,
  Alarme per Mail und ntfy, Version in about und /health
- public/build versioniert; .gitignore: macOS-Muster Icon traf resources/views/flux/icon/
- Runbook: dev/betrieb/README.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:56:23 +00:00

175 lines
6.2 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#
# Vorschau mit den aktuellen Live-Daten auffrischen (B2in). Ablauf: dev/betrieb/README.md.
#
# Kopiert die komplette Live-Datenbank und die hochgeladenen Medien (storage/app/public:
# Display-Mediathek, CMS-Mediathek, Videos, QR-Codes) in die Vorschau. Danach steht auf der
# Vorschau genau das, was der Kunde auf Live gepflegt hat – Displays, Playlists, Mediathek –,
# und der Release-Seeder kann darauf zur Probe laufen.
#
# Live wird nur gelesen (mysqldump --single-transaction, rsync als Quelle). Die Vorschau-
# Datenbank wird vollständig ersetzt.
#
# Aufruf (auf dem Server, als App-Benutzer):
#
# bash dev/betrieb/vorschau-auffrischen.sh <live-verzeichnis> <vorschau-verzeichnis> [--seed=<Klasse>]
#
# --seed=<Klasse> nach dem Import (und den Migrationen) diesen Seeder auf der Vorschau ausführen,
# z. B. --seed=InteriorReleaseSeeder für eine erneute Generalprobe.
#
# Die Live-Sicherung bleibt als ~/b2in-live-<zeit>.sql.gz liegen.
set -euo pipefail
live="${1:-}"
preview="${2:-}"
shift 2 || true
seeders=()
for option in "$@"; do
case "$option" in
--seed=*)
seeder="${option#--seed=}"
if [[ ! "$seeder" =~ ^[A-Za-z][A-Za-z0-9]*$ ]]; then
echo "Ungültiger Seeder: $seeder" >&2; exit 2
fi
seeders+=("$seeder") ;;
*) echo "Unbekannte Option: $option" >&2; exit 2 ;;
esac
done
if [[ -z "$live" || -z "$preview" || ! -f "$live/artisan" || ! -f "$preview/artisan" ]]; then
echo "Aufruf: bash $0 <live-verzeichnis> <vorschau-verzeichnis> [--seed=<Klasse>]" >&2
exit 2
fi
live="$(cd "$live" && pwd)"
preview="$(cd "$preview" && pwd)"
if [[ "$live" == "$preview" ]]; then
echo "Live- und Vorschau-Verzeichnis sind dasselbe." >&2
exit 2
fi
step() { printf '\n==> %s\n' "$*"; }
# Wert aus einer .env lesen (ohne Anführungszeichen, ohne Kommentar am Zeilenende bei unquotierten Werten)
env_value() {
local file="$1" key="$2" line value
line="$(grep -E "^${key}=" "$file" | tail -n 1 || true)"
value="${line#*=}"
if [[ "$value" =~ ^\"(.*)\"$ || "$value" =~ ^\'(.*)\'$ ]]; then
value="${BASH_REMATCH[1]}"
else
value="${value%% #*}"
fi
printf '%s' "$value"
}
# MySQL-Zugangsdaten als Datei (chmod 600), damit das Passwort nicht in der Prozessliste steht
credentials() {
local env="$1/.env" file
file="$(mktemp)"
chmod 600 "$file"
{
echo "[client]"
echo "host=$(env_value "$env" DB_HOST)"
echo "port=$(env_value "$env" DB_PORT)"
echo "user=$(env_value "$env" DB_USERNAME)"
echo "password=\"$(env_value "$env" DB_PASSWORD)\""
} > "$file"
printf '%s' "$file"
}
live_db="$(env_value "$live/.env" DB_DATABASE)"
preview_db="$(env_value "$preview/.env" DB_DATABASE)"
live_host="$(env_value "$live/.env" DB_HOST)"
preview_host="$(env_value "$preview/.env" DB_HOST)"
if [[ -z "$live_db" || -z "$preview_db" ]]; then
echo "DB_DATABASE fehlt in einer der beiden .env-Dateien." >&2
exit 1
fi
if [[ "$live_db" == "$preview_db" && "$live_host" == "$preview_host" ]]; then
echo "STOPP: Vorschau und Live zeigen auf dieselbe Datenbank ($live_db). Nichts wurde verändert." >&2
exit 1
fi
if [[ "$(env_value "$preview/.env" APP_ENV)" == "production" ]]; then
echo "STOPP: Die Vorschau läuft mit APP_ENV=production – ist das wirklich die Vorschau? Nichts wurde verändert." >&2
exit 1
fi
mailer="$(env_value "$preview/.env" MAIL_MAILER)"
if [[ "$mailer" != "log" && "$mailer" != "array" ]]; then
echo "Hinweis: MAIL_MAILER der Vorschau ist „$mailer“. Mit den Live-Daten könnte die Vorschau echte Mails verschicken."
echo "Empfohlen: MAIL_MAILER=log in $preview/.env."
fi
echo "Live: $live (Datenbank $live_db)"
echo "Vorschau: $preview (Datenbank $preview_db – wird vollständig ersetzt)"
echo "Medien: $live/storage/app/public/ → $preview/storage/app/public/ (Vorschau-eigene Dateien werden entfernt)"
read -r -p "Weiter? [ja/nein] " answer
if [[ "$answer" != "ja" ]]; then
echo "Abgebrochen, nichts verändert."
exit 1
fi
live_credentials="$(credentials "$live")"
preview_credentials="$(credentials "$preview")"
trap 'rm -f "$live_credentials" "$preview_credentials"' EXIT
dump="$HOME/b2in-live-$(date +%Y-%m-%d-%H%M).sql.gz"
step "Live-Datenbank sichern nach $dump"
mysqldump --defaults-extra-file="$live_credentials" --single-transaction --quick --no-tablespaces \
--routines --triggers --default-character-set=utf8mb4 "$live_db" | gzip > "$dump"
ls -lh "$dump"
step "Vorschau in den Wartungsmodus"
(cd "$preview" && php artisan down --retry=60)
trap 'echo; echo "!! Abgebrochen. Die Vorschau bleibt im Wartungsmodus; Live ist unverändert." >&2; rm -f "$live_credentials" "$preview_credentials"' ERR
step "Vorschau-Datenbank leeren und Live-Stand einspielen"
(cd "$preview" && php artisan db:wipe --force)
gunzip -c "$dump" | mysql --defaults-extra-file="$preview_credentials" --default-character-set=utf8mb4 "$preview_db"
# Sitzungen und Cache von Live gehören nicht in die Vorschau
for table in sessions cache cache_locks jobs; do
if mysql --defaults-extra-file="$preview_credentials" -N -e "SHOW TABLES LIKE '$table'" "$preview_db" | grep -q .; then
mysql --defaults-extra-file="$preview_credentials" -e "TRUNCATE TABLE \`$table\`" "$preview_db"
fi
done
step "Medien übernehmen"
mkdir -p "$preview/storage/app/public"
if [[ "$(realpath "$live/storage/app/public")" == "$(realpath "$preview/storage/app/public")" ]]; then
# Vorschau verlinkt auf die Live-Medien (preview-deployment.md, Schritt 3) – nichts zu kopieren
echo "Die Vorschau nutzt die Live-Medien per Link – übersprungen."
else
rsync -a --delete "$live/storage/app/public/" "$preview/storage/app/public/"
du -sh "$preview/storage/app/public"
fi
step "Migrationen und Seeder"
cd "$preview"
php artisan migrate --force
for seeder in "${seeders[@]}"; do
php artisan db:seed --class="$seeder" --force
done
step "Caches"
[[ -e public/storage ]] || php artisan storage:link
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
step "Vorschau wieder online"
php artisan up
trap - ERR
echo
echo "Vorschau aufgefrischt: Daten und Medien von Live, Stand $(date '+%d.%m.%Y %H:%M')."