Bisher lag das Skript mit Modus 100644 im Repo. Ein chmod +x auf dem Server galt dann als lokale Aenderung, und update.sh brach vor dem Pull mit "Es gibt lokale Aenderungen" ab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
150 lines
4.3 KiB
Bash
Executable File
150 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Bricktrack aktualisieren.
|
|
#
|
|
# ./update.sh Update auf den aktuellen Stand von origin/main
|
|
# ./update.sh --no-pull Nur neu bauen und starten, ohne git pull
|
|
# ./update.sh --no-backup Ohne Sicherung (nicht empfohlen)
|
|
#
|
|
# Ablauf: sichern -> holen -> bauen -> starten -> Healthcheck.
|
|
# Schlaegt der Healthcheck fehl, wird automatisch auf den vorherigen Stand
|
|
# zurueckgerollt und die Sicherung bleibt liegen.
|
|
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
BACKUP_DIR="./backups"
|
|
KEEP_BACKUPS=10
|
|
HEALTH_TIMEOUT=180
|
|
DO_PULL=1
|
|
DO_BACKUP=1
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--no-pull) DO_PULL=0 ;;
|
|
--no-backup) DO_BACKUP=0 ;;
|
|
-h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
*) echo "Unbekannte Option: $arg" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
info() { printf '\033[1;34m[bricktrack]\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33m[bricktrack]\033[0m %s\n' "$*"; }
|
|
error() { printf '\033[1;31m[bricktrack]\033[0m %s\n' "$*" >&2; }
|
|
|
|
# --- Vorbedingungen --------------------------------------------------------
|
|
|
|
if ! docker compose version >/dev/null 2>&1; then
|
|
error "Docker Compose (Plugin) wurde nicht gefunden."
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f .env ]; then
|
|
error "Es gibt keine .env. Lege sie an: cp .env.example .env"
|
|
exit 1
|
|
fi
|
|
|
|
PREVIOUS_COMMIT="$(git rev-parse HEAD 2>/dev/null || echo '')"
|
|
|
|
# --- Sichern ---------------------------------------------------------------
|
|
|
|
BACKUP_FILE=""
|
|
if [ "$DO_BACKUP" -eq 1 ]; then
|
|
mkdir -p "$BACKUP_DIR"
|
|
BACKUP_FILE="$BACKUP_DIR/bricktrack-$(date +%Y%m%d-%H%M%S).tar.gz"
|
|
|
|
info "Sicherung wird angelegt: $BACKUP_FILE"
|
|
# Der Container wird kurz angehalten, damit die SQLite-Datei nicht mitten in
|
|
# einem Schreibvorgang eingepackt wird.
|
|
docker compose stop app >/dev/null 2>&1 || true
|
|
tar czf "$BACKUP_FILE" data .env 2>/dev/null || {
|
|
error "Die Sicherung ist fehlgeschlagen. Abbruch."
|
|
docker compose start app >/dev/null 2>&1 || true
|
|
exit 1
|
|
}
|
|
info "Sicherung fertig ($(du -h "$BACKUP_FILE" | cut -f1))."
|
|
|
|
# Alte Sicherungen aufraeumen, die juengsten bleiben.
|
|
ls -1t "$BACKUP_DIR"/bricktrack-*.tar.gz 2>/dev/null \
|
|
| tail -n +$((KEEP_BACKUPS + 1)) \
|
|
| xargs -r rm --
|
|
fi
|
|
|
|
# --- Holen -----------------------------------------------------------------
|
|
|
|
if [ "$DO_PULL" -eq 1 ]; then
|
|
if [ -n "$(git status --porcelain)" ]; then
|
|
error "Es gibt lokale Aenderungen im Arbeitsverzeichnis:"
|
|
git status --short >&2
|
|
error "Bitte erst aufraeumen oder mit --no-pull starten."
|
|
exit 1
|
|
fi
|
|
|
|
info "Neue Version wird geholt …"
|
|
git pull --ff-only
|
|
NEW_COMMIT="$(git rev-parse HEAD)"
|
|
|
|
if [ "$NEW_COMMIT" = "$PREVIOUS_COMMIT" ]; then
|
|
info "Es gibt nichts Neues. Der Container wird trotzdem neu gestartet."
|
|
else
|
|
info "Neuer Stand: $(git log -1 --pretty='%h %s')"
|
|
fi
|
|
fi
|
|
|
|
# --- Bauen und starten -----------------------------------------------------
|
|
|
|
info "Image wird gebaut …"
|
|
docker compose build app
|
|
|
|
info "Container wird gestartet …"
|
|
docker compose up -d app
|
|
|
|
# --- Healthcheck -----------------------------------------------------------
|
|
|
|
info "Warte auf den Healthcheck (max. ${HEALTH_TIMEOUT}s) …"
|
|
elapsed=0
|
|
healthy=0
|
|
|
|
while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do
|
|
status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' bricktrack 2>/dev/null || echo 'missing')"
|
|
|
|
case "$status" in
|
|
healthy) healthy=1; break ;;
|
|
unhealthy) break ;;
|
|
missing) error "Der Container bricktrack laeuft nicht."; break ;;
|
|
esac
|
|
|
|
sleep 3
|
|
elapsed=$((elapsed + 3))
|
|
printf '.'
|
|
done
|
|
printf '\n'
|
|
|
|
if [ "$healthy" -eq 1 ]; then
|
|
info "Bricktrack laeuft."
|
|
docker compose ps app
|
|
exit 0
|
|
fi
|
|
|
|
# --- Rueckabwicklung -------------------------------------------------------
|
|
|
|
error "Die neue Version ist nicht gesund geworden. Die letzten Logzeilen:"
|
|
docker compose logs --tail 50 app >&2
|
|
|
|
if [ "$DO_PULL" -eq 1 ] && [ -n "$PREVIOUS_COMMIT" ]; then
|
|
warn "Rollback auf $PREVIOUS_COMMIT …"
|
|
git reset --hard "$PREVIOUS_COMMIT"
|
|
docker compose build app
|
|
docker compose up -d app
|
|
warn "Der vorherige Stand laeuft wieder."
|
|
fi
|
|
|
|
if [ -n "$BACKUP_FILE" ]; then
|
|
warn "Die Daten liegen unveraendert in $BACKUP_FILE."
|
|
warn "Zurueckspielen falls noetig:"
|
|
warn " docker compose down && tar xzf $BACKUP_FILE && sudo chown -R 1000:1000 ./data && docker compose up -d"
|
|
fi
|
|
|
|
exit 1
|