Files
lernbar/update.sh
Florian WildeandClaude Opus 5 e856349cd4 Baustein 1: Grundgeruest, Datenbank, Anmeldung, zwei Rollen
Legt das Fundament der Lern-Bar an:

- Next.js 15 (App Router), React 19, TypeScript, ESLint, Vitest
- SQLite ueber Drizzle hinter einer Repository-Schicht
- Eigener Migrationslauf mit Pruefsummen, beim Start angewendet
- Argon2id, serverseitige Sitzungen, HttpOnly-Cookie, SameSite=Lax
- Rollen Ausbilder und Azubi, Rechteregeln als reine Funktionen
- Verwaltung: Azubikonto und Ausbildung anlegen, Passwort, Sperren
- Gestaltung Petrol/Bernstein, helle und dunkle Darstellung
- Dockerfile, docker-compose, Lebenszeichen, update.sh mit Rueckfall
- Sicherung und Wiederherstellung als Shell-Skripte
- 29 Tests fuer Rechte, Zeitrechnung und Migrationslauf

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 00:52:44 +02:00

151 lines
4.8 KiB
Bash
Executable File
Raw Permalink 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
# ---------------------------------------------------------------------------
# Lern-Bar aktualisieren – für den Linux-Server, auf dem der Container läuft.
#
# ./update.sh Quelltext holen, neu bauen, neu starten
# ./update.sh --ohne-git nur neu bauen und neu starten
# ./update.sh --ohne-sicherung Sicherung überspringen (nicht empfohlen)
# ./update.sh --zweig main einen bestimmten Git-Zweig holen
#
# Ablauf: Sicherung → Quelltext holen → Abbild bauen → umschalten →
# Lebenszeichen prüfen. Klappt das Lebenszeichen nicht, wird automatisch auf
# das vorherige Abbild zurückgeschaltet.
# ---------------------------------------------------------------------------
set -euo pipefail
cd "$(dirname "$(readlink -f "$0")")"
DIENST="lernbar"
ABBILD="lernbar:lokal"
ZWEIG=""
MIT_GIT=1
MIT_SICHERUNG=1
WARTEZEIT=90
while [ $# -gt 0 ]; do
case "$1" in
--ohne-git) MIT_GIT=0 ;;
--ohne-sicherung) MIT_SICHERUNG=0 ;;
--zweig)
ZWEIG="${2:-}"
shift
;;
--hilfe | -h)
sed -n '2,18p' "$0"
exit 0
;;
*)
echo "Unbekannte Angabe: $1 (--hilfe zeigt die Möglichkeiten)" >&2
exit 1
;;
esac
shift
done
# --- Docker-Compose-Aufruf ermitteln ---------------------------------------
if docker compose version >/dev/null 2>&1; then
COMPOSE="docker compose"
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE="docker-compose"
else
echo "Weder 'docker compose' noch 'docker-compose' gefunden." >&2
exit 1
fi
meldung() { printf '\n\033[1;36m▸ %s\033[0m\n' "$1"; }
warnung() { printf '\033[1;33m! %s\033[0m\n' "$1"; }
fehler() { printf '\033[1;31m✗ %s\033[0m\n' "$1" >&2; }
fertig() { printf '\033[1;32m✓ %s\033[0m\n' "$1"; }
# --- 1. Sicherung ----------------------------------------------------------
if [ "$MIT_SICHERUNG" -eq 1 ]; then
meldung "Sicherung anlegen"
if $COMPOSE ps --status running --services 2>/dev/null | grep -qx "$DIENST"; then
ARCHIV="$($COMPOSE exec -T "$DIENST" /app/werkzeuge/sicherung.sh /daten/sicherungen | tail -1)"
fertig "Sicherung liegt im Volume unter ${ARCHIV}"
else
warnung "Container läuft nicht – Sicherung übersprungen."
fi
else
warnung "Sicherung auf Wunsch übersprungen."
fi
# --- 2. Quelltext holen ----------------------------------------------------
VORHER_STAND=""
if [ "$MIT_GIT" -eq 1 ]; then
if [ -d .git ]; then
meldung "Quelltext holen"
VORHER_STAND="$(git rev-parse HEAD)"
if [ -n "$(git status --porcelain)" ]; then
fehler "Im Arbeitsverzeichnis liegen Änderungen. Bitte erst aufräumen (git status)."
exit 1
fi
git fetch --prune origin
if [ -n "$ZWEIG" ]; then
git checkout "$ZWEIG"
fi
AKTUELLER_ZWEIG="$(git rev-parse --abbrev-ref HEAD)"
git merge --ff-only "origin/$AKTUELLER_ZWEIG"
fertig "Stand: $(git rev-parse --short HEAD) – $(git log -1 --pretty=%s)"
else
warnung "Kein Git-Repository – Quelltext bleibt, wie er ist."
fi
fi
# --- 3. Vorheriges Abbild sichern ------------------------------------------
ALTE_ID="$(docker image inspect "$ABBILD" --format '{{.Id}}' 2>/dev/null || true)"
if [ -n "$ALTE_ID" ]; then
docker tag "$ALTE_ID" "lernbar:vorher"
fi
# --- 4. Bauen --------------------------------------------------------------
meldung "Abbild bauen"
if ! $COMPOSE build --pull "$DIENST"; then
fehler "Der Bau ist fehlgeschlagen. Es läuft weiterhin die alte Fassung."
exit 1
fi
# --- 5. Umschalten ---------------------------------------------------------
meldung "Neu starten"
$COMPOSE up -d "$DIENST"
# --- 6. Lebenszeichen ------------------------------------------------------
meldung "Auf das Lebenszeichen warten (bis zu ${WARTEZEIT}s)"
GESUND=0
for _ in $(seq 1 "$WARTEZEIT"); do
ZUSTAND="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}kein-test{{end}}' "$DIENST" 2>/dev/null || echo unbekannt)"
case "$ZUSTAND" in
healthy | kein-test)
GESUND=1
break
;;
unhealthy)
break
;;
esac
sleep 1
done
if [ "$GESUND" -ne 1 ]; then
fehler "Die neue Fassung meldet sich nicht gesund. Ich schalte zurück."
docker logs --tail 60 "$DIENST" || true
if [ -n "$ALTE_ID" ]; then
docker tag "lernbar:vorher" "$ABBILD"
$COMPOSE up -d --no-build "$DIENST"
warnung "Zurückgeschaltet auf das vorherige Abbild."
fi
if [ -n "$VORHER_STAND" ]; then
warnung "Quelltext steht auf einem neueren Stand. Zurück mit: git reset --hard $VORHER_STAND"
fi
exit 1
fi
# --- 7. Aufräumen ----------------------------------------------------------
meldung "Aufräumen"
docker image prune -f --filter "label=org.opencontainers.image.title=lernbar" >/dev/null 2>&1 || true
docker image rm "lernbar:vorher" >/dev/null 2>&1 || true
fertig "Lern-Bar läuft in der neuen Fassung."
$COMPOSE ps "$DIENST"