Das Skript brach mit „laesst sich nicht ohne Merge aktualisieren. Bitte von Hand pruefen" ab — und liess den Betreiber damit allein. Genau dieser Fall ist auf dem Server aufgetreten, nachdem hier die Historie neu geschrieben wurde: Alte und neue Commits haben dann keinen gemeinsamen Vorfahren mehr. Jetzt unterscheidet das Skript die beiden Ursachen und nennt den Ausweg. Bei neu geschriebener Historie steht der vollstaendige Befehl da, samt dem Hinweis, dass die .env davon unberuehrt bleibt. Bei eigenen Commits werden sie aufgelistet. Beide Zweige an nachgebauten Repositories geprueft.
186 lines
6.0 KiB
Bash
Executable File
186 lines
6.0 KiB
Bash
Executable File
#!/bin/sh
|
|
# =============================================================================
|
|
# QR-Code-Generator aktualisieren
|
|
#
|
|
# ./update.sh neuen Stand holen und, falls es Änderungen gibt,
|
|
# den Stack neu bauen und starten
|
|
# ./update.sh --force auch ohne neue Commits neu bauen
|
|
# ./update.sh --prune danach verwaiste Docker-Images aufräumen
|
|
# ./update.sh --help diese Hilfe
|
|
# =============================================================================
|
|
set -eu
|
|
|
|
FORCE=0
|
|
PRUNE=0
|
|
|
|
usage() {
|
|
cat <<'HILFE'
|
|
QR-Code-Generator aktualisieren.
|
|
|
|
./update.sh neuen Stand holen und, falls es Änderungen gibt,
|
|
den Stack neu bauen und starten
|
|
./update.sh --force auch ohne neue Commits neu bauen
|
|
./update.sh --prune danach verwaiste Docker-Images aufräumen
|
|
./update.sh --help diese Hilfe
|
|
HILFE
|
|
}
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--force) FORCE=1 ;;
|
|
--prune) PRUNE=1 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*)
|
|
echo "Unbekannte Option: $1" >&2
|
|
echo "Aufruf: ./update.sh [--force] [--prune]" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# Immer im Projektverzeichnis arbeiten, egal von wo das Skript gestartet wird.
|
|
cd "$(dirname "$0")"
|
|
|
|
info() { echo "→ $*"; }
|
|
ok() { echo "✓ $*"; }
|
|
fail() { echo "✗ $*" >&2; exit 1; }
|
|
|
|
# --- Voraussetzungen --------------------------------------------------------
|
|
|
|
command -v git >/dev/null 2>&1 || fail "git ist nicht installiert."
|
|
command -v docker >/dev/null 2>&1 || fail "docker ist nicht installiert."
|
|
docker compose version >/dev/null 2>&1 ||
|
|
fail "docker compose ist nicht verfügbar (Docker Compose v2 wird benötigt)."
|
|
|
|
[ -f .env ] || fail ".env fehlt. Anlegen mit: cp .env.example .env"
|
|
|
|
# Ein "git pull" auf einem verändertem Arbeitsverzeichnis endet im Konflikt.
|
|
if [ -n "$(git status --porcelain)" ]; then
|
|
echo "✗ Es gibt lokale Änderungen im Arbeitsverzeichnis:" >&2
|
|
git status --short >&2
|
|
echo "" >&2
|
|
fail "Bitte zuerst committen oder mit 'git stash' beiseitelegen."
|
|
fi
|
|
|
|
# --- Neuen Stand holen ------------------------------------------------------
|
|
|
|
BEFORE="$(git rev-parse HEAD)"
|
|
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
|
|
|
info "Hole neuen Stand von origin/$BRANCH …"
|
|
git fetch --quiet origin
|
|
|
|
# --ff-only: lieber abbrechen als eine Merge-Situation erzeugen, die auf einem
|
|
# Server niemand auflösen will.
|
|
if ! git merge --ff-only --quiet "origin/$BRANCH" 2>/dev/null; then
|
|
echo "" >&2
|
|
echo "✗ Der lokale Stand lässt sich nicht ohne Merge aktualisieren." >&2
|
|
echo "" >&2
|
|
|
|
if git merge-base --is-ancestor HEAD "origin/$BRANCH" 2>/dev/null; then
|
|
# Kann eigentlich nicht sein — dann wäre das Vorspulen geglückt.
|
|
echo " Grund: unklar. Bitte 'git status' und 'git log --oneline -5' ansehen." >&2
|
|
elif ! git merge-base HEAD "origin/$BRANCH" >/dev/null 2>&1; then
|
|
# Kein gemeinsamer Vorfahre: Die Historie wurde neu geschrieben, etwa
|
|
# durch einen Force-Push. Der Inhalt ist derselbe, die Commits haben
|
|
# nur neue Kennungen.
|
|
cat >&2 <<'HINWEIS'
|
|
Grund: Der Verlauf auf dem Server wurde neu geschrieben (Force-Push).
|
|
Alte und neue Commits haben keinen gemeinsamen Vorfahren mehr.
|
|
|
|
Der lokale Stand lässt sich verwerfen und durch den vom Server ersetzen:
|
|
|
|
git fetch origin
|
|
git reset --hard origin/main
|
|
./update.sh --force
|
|
|
|
Das betrifft nur versionierte Dateien. Die .env und alles, was nicht im
|
|
Repository liegt, bleibt unberührt.
|
|
HINWEIS
|
|
else
|
|
echo " Grund: Hier liegen eigene Commits, die es auf dem Server nicht gibt:" >&2
|
|
git --no-pager log --oneline "origin/$BRANCH..HEAD" | sed 's/^/ /' >&2
|
|
echo "" >&2
|
|
echo " Entweder pushen — oder mit 'git reset --hard origin/$BRANCH' verwerfen." >&2
|
|
fi
|
|
echo "" >&2
|
|
exit 1
|
|
fi
|
|
|
|
AFTER="$(git rev-parse HEAD)"
|
|
|
|
if [ "$BEFORE" = "$AFTER" ]; then
|
|
ok "Bereits auf dem neuesten Stand ($(git rev-parse --short HEAD))."
|
|
else
|
|
ok "Aktualisiert: $(git rev-parse --short "$BEFORE") → $(git rev-parse --short "$AFTER")"
|
|
echo ""
|
|
git --no-pager log --oneline "$BEFORE..$AFTER"
|
|
echo ""
|
|
fi
|
|
|
|
# --- Stack neu bauen --------------------------------------------------------
|
|
|
|
RUNNING="$(docker compose ps --status running --quiet 2>/dev/null | wc -l | tr -d ' ')"
|
|
|
|
if [ "$BEFORE" = "$AFTER" ] && [ "$FORCE" -eq 0 ] && [ "$RUNNING" -gt 0 ]; then
|
|
ok "Der Stack läuft und es gibt nichts Neues — kein Neubau nötig."
|
|
echo " (Mit ./update.sh --force lässt sich ein Neubau erzwingen.)"
|
|
exit 0
|
|
fi
|
|
|
|
info "Baue und starte den Stack neu — das dauert einige Minuten …"
|
|
docker compose up -d --build
|
|
|
|
# --- Warten, bis die Anwendung antwortet ------------------------------------
|
|
|
|
# Nur die Ziffern behalten — so stören weder Anführungszeichen noch ein
|
|
# Wagenrücklauf aus einer unter Windows bearbeiteten .env.
|
|
APP_PORT="$(grep -m1 '^APP_PORT=' .env | cut -d= -f2 | tr -cd '0-9')"
|
|
[ -n "$APP_PORT" ] || APP_PORT=3000
|
|
|
|
probe() {
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl --silent --fail --max-time 5 --output /dev/null "http://localhost:$APP_PORT/"
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
wget --quiet --timeout=5 --tries=1 --spider "http://localhost:$APP_PORT/"
|
|
else
|
|
return 0 # Kein Prüfwerkzeug vorhanden — dann eben ohne Prüfung.
|
|
fi
|
|
}
|
|
|
|
info "Warte darauf, dass die Anwendung antwortet …"
|
|
ATTEMPT=0
|
|
until probe; do
|
|
ATTEMPT=$((ATTEMPT + 1))
|
|
if [ "$ATTEMPT" -ge 60 ]; then
|
|
echo ""
|
|
docker compose logs --tail 40 app >&2
|
|
fail "Die Anwendung antwortet nach 2 Minuten nicht. Protokoll siehe oben."
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
ok "Die Anwendung antwortet auf http://localhost:$APP_PORT"
|
|
|
|
# --- Ergebnis ---------------------------------------------------------------
|
|
|
|
echo ""
|
|
info "Datenbankmigrationen beim Start:"
|
|
docker compose logs app 2>/dev/null |
|
|
grep -E "migration|Migration|pending" |
|
|
tail -n 5 |
|
|
sed 's/^/ /'
|
|
|
|
echo ""
|
|
docker compose ps
|
|
|
|
if [ "$PRUNE" -eq 1 ]; then
|
|
echo ""
|
|
info "Räume verwaiste Docker-Images auf …"
|
|
docker image prune --force
|
|
fi
|
|
|
|
echo ""
|
|
ok "Fertig — Version $(git rev-parse --short HEAD)"
|