#!/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"