Die rote Fahne aus #113 blieb nach dem automatischen Update aus. Code-Pfad und Daten sind in Ordnung (NC-Gantt #333 ist offen und stammt von "tinko"), aber der sync-Container bindet ./api nur als Volume ein. Bei unveraendertem Image und unveraenderter Compose-Konfiguration liess "docker compose up -d" ihn deshalb einfach weiterlaufen. Der --watch-Prozess hatte seine Klassen noch vom alten Stand geladen, uebergab also nie einen author, und countExternalOpenTickets() kam immer auf 0. update.sh erzeugt api und sync deshalb jetzt mit --force-recreate neu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
143 lines
6.6 KiB
Bash
143 lines
6.6 KiB
Bash
#!/bin/bash
|
|
#
|
|
# Holt die neueste Version aus dem konfigurierten Git-Repo und fuehrt die noetigen Updates
|
|
# fuer die Docker-Compose-Entwicklungs-/Laufzeitumgebung durch (siehe README.md/Visualization.md
|
|
# Abschnitt "Entwicklung nur mit Docker"): PHP-Abhaengigkeiten aktualisieren, Images neu bauen,
|
|
# Dienste neu starten, Testsuiten laufen lassen. SQLite-Migrationen brauchen keinen eigenen
|
|
# Schritt - api/src/Db/Database::migrate() wendet sie bei jedem API-Start automatisch an.
|
|
#
|
|
# Konfiguration ueber Umgebungsvariablen (Defaults passen fuer den ueblichen Fall):
|
|
# UPDATE_REMOTE - Git-Remote, aus dem geholt wird (Default: origin)
|
|
# UPDATE_BRANCH - Branch, der aktualisiert wird (Default: aktuell ausgecheckter Branch)
|
|
# UPDATE_RUN_TESTS - "0" setzen, um die Testsuiten nach dem Update zu ueberspringen (Default: 1)
|
|
# UPDATE_ALLOW_DIRTY - "1" setzen, um auch bei lokalen Aenderungen an versionierten Dateien
|
|
# weiterzumachen (Default: 0, d.h. kontrollierter Abbruch)
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
|
cd "$REPO_DIR"
|
|
|
|
log() {
|
|
printf '[update.sh] %s\n' "$1"
|
|
}
|
|
|
|
fail() {
|
|
printf '[update.sh] Fehler: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
command -v git >/dev/null 2>&1 || fail "git ist nicht installiert."
|
|
command -v docker >/dev/null 2>&1 || fail "docker ist nicht installiert."
|
|
git -C "$REPO_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1 \
|
|
|| fail "$REPO_DIR ist kein Git-Repository."
|
|
|
|
UPDATE_REMOTE="${UPDATE_REMOTE:-origin}"
|
|
UPDATE_BRANCH="${UPDATE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
|
|
UPDATE_RUN_TESTS="${UPDATE_RUN_TESTS:-1}"
|
|
UPDATE_ALLOW_DIRTY="${UPDATE_ALLOW_DIRTY:-0}"
|
|
|
|
# Unversionierte Dateien blockieren das Update bewusst NICHT: neben dem Repo liegen auf einem
|
|
# Produktionsserver Laufzeit-/Konfigurationsdateien (hosts.conf, monitor-api.conf, ...), die
|
|
# beim ff-only-Pull ohnehin unangetastet bleiben - stuende eine davon einem neuen Commit im
|
|
# Weg, bricht git selbst kontrolliert ab. Blockierend sind nur Aenderungen an versionierten
|
|
# Dateien, weil der Pull die verlieren koennte.
|
|
DIRTY_TRACKED="$(git status --porcelain --untracked-files=no)"
|
|
if [ -n "$DIRTY_TRACKED" ]; then
|
|
printf '%s\n' "$DIRTY_TRACKED" >&2
|
|
if [ "$UPDATE_ALLOW_DIRTY" = "1" ]; then
|
|
log "Warnung: lokale Aenderungen an versionierten Dateien (siehe oben), UPDATE_ALLOW_DIRTY=1 - fahre trotzdem fort."
|
|
else
|
|
fail "Lokale Aenderungen an versionierten Dateien (siehe oben) - bitte erst committen/stashen (oder UPDATE_ALLOW_DIRTY=1 setzen). Breche ab, um nichts zu ueberschreiben."
|
|
fi
|
|
fi
|
|
|
|
UNTRACKED="$(git ls-files --others --exclude-standard)"
|
|
if [ -n "$UNTRACKED" ]; then
|
|
log "Hinweis: unversionierte Dateien im Arbeitsbaum, sie bleiben unveraendert:"
|
|
printf '%s\n' "$UNTRACKED" | sed 's/^/ /'
|
|
fi
|
|
|
|
log "Hole Aenderungen von ${UPDATE_REMOTE}/${UPDATE_BRANCH}..."
|
|
git fetch "$UPDATE_REMOTE" "$UPDATE_BRANCH"
|
|
|
|
# --ff-only statt reset --hard: bricht kontrolliert ab, falls der lokale Branch abweicht
|
|
# (z. B. eigene, noch nicht gepushte Commits), statt Historie zu ueberschreiben.
|
|
git checkout "$UPDATE_BRANCH"
|
|
git pull --ff-only "$UPDATE_REMOTE" "$UPDATE_BRANCH"
|
|
|
|
DOCKER_COMPOSE="docker compose"
|
|
if ! docker compose version >/dev/null 2>&1; then
|
|
command -v docker-compose >/dev/null 2>&1 || fail "Weder 'docker compose' noch 'docker-compose' verfuegbar."
|
|
DOCKER_COMPOSE="docker-compose"
|
|
fi
|
|
|
|
log "Aktualisiere PHP-Abhaengigkeiten (composer install)..."
|
|
$DOCKER_COMPOSE run --rm api composer install --no-interaction --no-progress
|
|
|
|
log "Baue Docker-Images neu (falls Dockerfile/Abhaengigkeiten sich geaendert haben)..."
|
|
$DOCKER_COMPOSE build
|
|
|
|
# Tests laufen bewusst VOR dem Neustart der Dienste, in einem temporaeren Container
|
|
# (docker compose run --rm), der die produktiven api/web/sync-Container nicht anfasst. Liefe
|
|
# "up -d" zuerst, wuerden fehlschlagende Tests das Skript zwar per "set -e" abbrechen, die
|
|
# Produktion liefe aber bereits mit dem fehlerhaften Stand weiter - und deploy-webhook.py
|
|
# meldet einen solchen Abbruch nirgends aktiv, nur still im Log. Bei einem Fehlschlag hier
|
|
# bleiben die alten Container unveraendert im Einsatz.
|
|
if [ "$UPDATE_RUN_TESTS" = "1" ]; then
|
|
log "Fuehre PHPUnit-Tests (API) aus..."
|
|
$DOCKER_COMPOSE run --rm api composer test
|
|
|
|
log "Fuehre Frontend-Tests aus..."
|
|
$DOCKER_COMPOSE run --rm frontend
|
|
else
|
|
log "UPDATE_RUN_TESTS=0 gesetzt - ueberspringe Testsuiten."
|
|
fi
|
|
|
|
log "Starte Dienste neu (api, web, sync)..."
|
|
# --force-recreate fuer api und sync: beide mounten ./api nur als Volume, ein reines "up -d"
|
|
# laesst die Container daher unveraendert weiterlaufen, sobald Image und Compose-Konfiguration
|
|
# gleich bleiben. Der sync-Dienst ist ein einziger langlebiger PHP-Prozess (--watch), der seine
|
|
# Klassen einmal geladen hat und neuen Code sonst nie sieht - so schrieb er nach dem Update zu
|
|
# Issue #113 weiterhin keinen Ticket-Ersteller und die rote Fahne am PO blieb aus (Issue #115).
|
|
$DOCKER_COMPOSE up -d --force-recreate api sync
|
|
$DOCKER_COMPOSE up -d web
|
|
|
|
# Meldet das Update als Event (POST /api/v1/events/system, Admin-Key aus der .env). Die API ist
|
|
# direkt nach "up -d" evtl. noch nicht bereit, daher mehrere Versuche; ein Fehlschlag bricht das
|
|
# (bereits erfolgreiche) Update nicht ab.
|
|
report_update_event() {
|
|
command -v curl >/dev/null 2>&1 || { log "Hinweis: curl fehlt, Update-Event wird nicht gemeldet."; return 0; }
|
|
local env_file="$REPO_DIR/.env" admin_key api_base
|
|
env_value() {
|
|
[ -f "$env_file" ] || return 0
|
|
sed -n "s/^[[:space:]]*$1=//p" "$env_file" | tail -n1 \
|
|
| sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'\$/\1/"
|
|
}
|
|
admin_key="${MONITOR_ADMIN_API_KEY:-$(env_value MONITOR_ADMIN_API_KEY)}"
|
|
api_base="${MONITOR_API_BASE:-$(env_value MONITOR_API_BASE)}"
|
|
api_base="${api_base:-http://localhost:8091}"
|
|
api_base="${api_base%/}"
|
|
api_base="${api_base%/api/v1}"
|
|
if [ -z "$admin_key" ]; then
|
|
log "Hinweis: MONITOR_ADMIN_API_KEY fehlt, Update-Event wird nicht gemeldet."
|
|
return 0
|
|
fi
|
|
|
|
local payload i
|
|
payload="{\"name\":\"Monitor\",\"message\":\"Update eingespielt ($(git rev-parse --short HEAD))\",\"severity\":\"warning\"}"
|
|
for i in 1 2 3 4 5 6 7 8 9 10; do
|
|
if curl -sf --max-time 10 -X POST "$api_base/api/v1/events/system" \
|
|
-H "Content-Type: application/json" -H "X-Admin-Key: $admin_key" \
|
|
-d "$payload" >/dev/null 2>&1; then
|
|
log "Update-Event gemeldet."
|
|
return 0
|
|
fi
|
|
sleep 3
|
|
done
|
|
log "Warnung: Update-Event konnte nicht gemeldet werden (API unter $api_base nicht erreichbar oder Key ungueltig)."
|
|
}
|
|
|
|
report_update_event
|
|
|
|
log "Update abgeschlossen (${UPDATE_BRANCH} @ $(git rev-parse --short HEAD))."
|