From 39098cd201f481ed361de637c359f54bb5c5f453 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Wed, 2 Sep 2026 00:11:22 +0200 Subject: [PATCH] chore: Update-Skript erkennt Container-Zustand, Merkposten ergaenzt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Das Update-Skript prüfte allein http://127.0.0.1:APP_PORT/api/health. Hinter einem Reverse Proxy ist der App-Port oft gar nicht auf dem Host veröffentlicht — dann meldete das Skript nach zwei Minuten einen Fehlschlag, obwohl das Update sauber durchgelaufen war. Schlägt die Abfrage vom Host fehl, zählt jetzt der HEALTHCHECK des Containers. Den bringt das Image schon mit; er fragt /api/health von innen ab und ist damit unabhängig von der Portveröffentlichung. Im Fehlerfall nennt das Skript zusätzlich den Container-Zustand und ordnet ihn ein: kein Container (Start fehlgeschlagen), startet noch (Migrationen dauern), läuft aber ungesund (Fehler in der Anwendung), oder ein älteres Image ohne Healthcheck. Ohne diese Auskunft sah ein nicht veröffentlichter Port genauso aus wie eine abgestürzte Anwendung. container_zustand() gibt bewusst immer 0 zurück — unter set -e dürfte die Diagnose nicht selbst abbrechen, bevor der Weg zurück ausgegeben ist. Dazu drei Merkposten in CLAUDE.md, die diese Sitzung Zeit gekostet haben: Klickwege müssen gegen einen Produktionsbau geprüft werden (im Entwicklungsmodus hydratisiert unter headless Chromium nichts, weil der HMR-Socket scheitert — ohne jede Fehlermeldung), vor npm start den Port prüfen, und dass scripts/shot.mjs sich nicht anmeldet. --- CLAUDE.md | 15 +++++++++++ update.sh | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index dde85d8..c1ec571 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,6 +96,9 @@ Statistiken, Benachrichtigungen, REST-API, PWA. Oberflächensprache **Deutsch**. - **Dev-Umgebung**: Postgres auf `localhost:5439` (`docker-compose.dev.yml`), Dev-Server auf **Port 3011** (3000 und 3010 sind von anderen Projekten belegt). - **Screenshots**: `node scripts/shot.mjs [w] [h] [theme]`. + Achtung: das Skript meldet sich **nicht** an — bei geschützten Seiten entsteht + kommentarlos ein Bild der Anmeldemaske. Für angemeldete Ansichten den + Login-Weg aus `scripts/screenshots.mjs` nachbauen. - Next hängt selbst einen Block an diese Datei an — der bleibt drin. ## Fortschritt @@ -119,6 +122,18 @@ Statistiken, Benachrichtigungen, REST-API, PWA. Oberflächensprache **Deutsch**. das ASCII-Schlusszeichen beendet den String. In JSX-Text und Kommentaren ok. - Heredocs im Bash-Tool sind bei langen TSX-Dateien unzuverlässig — dann das Write-Werkzeug benutzen. +- **Klickwege niemals gegen `npm run dev` prüfen.** Unter headless Chromium + scheitert der HMR-Socket (`ERR_INVALID_HTTP_RESPONSE`), dadurch hydratisiert + **keine** Seite und kein Knopf reagiert — ohne Seitenfehler, ohne + fehlgeschlagene Anfrage, es passiert einfach nichts. Sieht aus wie ein Fehler + im eigenen Code, ist aber die Umgebung. Stattdessen + `npm run build` und `PORT=3012 npm start`, dann dagegen klicken. + Formulare täuschen zusätzlich: die Anmeldung funktioniert auch ohne + JavaScript, wirkt also intakt. +- Vor `npm start` prüfen, ob der Port noch von einem früheren Lauf belegt ist — + sonst startet der neue Server gar nicht und man testet den alten Bau. +- Testdaten nach Klickproben wieder herstellen: die Proben schreiben in die + geseedete Entwicklungsdatenbank. diff --git a/update.sh b/update.sh index 5bc2eb4..5a48fbe 100755 --- a/update.sh +++ b/update.sh @@ -39,6 +39,10 @@ Ablauf: Datenbank sichern → Änderungen holen → Image bauen → neu starten warten, bis die Instanz wieder antwortet. Die Datenbankmigrationen spielt der Container beim Start selbst ein. +Geprüft wird zuerst http://127.0.0.1:APP_PORT/api/health. Ist der Port gar +nicht auf dem Host veröffentlicht — üblich hinter einem Reverse Proxy —, +zählt stattdessen der Healthcheck des Containers. + Optionen: --ohne-backup Keine Sicherung anlegen (nicht empfohlen) --ohne-pull Nichts aus dem Repository holen, nur neu bauen und starten @@ -196,31 +200,95 @@ gut "Container laufen." schritt "Auf die Instanz warten" info "Die Datenbankmigrationen spielt der Container beim Start selbst ein." +# Der Container meldet seinen Zustand selbst — der HEALTHCHECK im Image fragt +# /api/health von innen ab. Das ist der verlässlichere Weg: hinter einem +# Reverse Proxy ist der App-Port oft gar nicht auf dem Host veröffentlicht, +# und dann sagt ein fehlschlagendes curl nichts über die Instanz aus. +# Gibt immer 0 zurück: die Funktion läuft auch im Fehlerpfad, und unter +# `set -e` dürfte eine Auskunft über den Zustand nicht selbst das Skript +# abbrechen, bevor der Weg zurück ausgegeben wurde. +container_zustand() { + local id + id=$($COMPOSE ps -q app 2>/dev/null || true) + if [ -z "$id" ]; then + printf 'kein-container' + return 0 + fi + # Ohne HEALTHCHECK im Image ist .State.Health leer — dann bleibt der + # Laufzustand (running/exited) als grobe Auskunft. + docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}ohne-healthcheck:{{.State.Status}}{{end}}' \ + "$id" 2>/dev/null || printf 'unbekannt' + return 0 +} + GESUND=0 +UEBER_CONTAINER=0 for _ in $(seq 1 60); do if curl -fsS "http://127.0.0.1:${PORT}/api/health" >/dev/null 2>&1; then GESUND=1 break fi + if [ "$(container_zustand)" = "healthy" ]; then + GESUND=1 + UEBER_CONTAINER=1 + break + fi sleep 2 done if [ "$GESUND" -eq 1 ]; then - ANTWORT=$(curl -fsS "http://127.0.0.1:${PORT}/api/health" 2>/dev/null || true) - gut "Flimmer antwortet auf Port ${PORT}." - leise "$ANTWORT" + if [ "$UEBER_CONTAINER" -eq 1 ]; then + gut "Der Container meldet sich gesund." + leise "Auf Port ${PORT} war nichts zu erreichen — das ist normal, wenn die App" + leise "nur über einen Reverse Proxy erreichbar ist und den Port nicht veröffentlicht." + else + ANTWORT=$(curl -fsS "http://127.0.0.1:${PORT}/api/health" 2>/dev/null || true) + gut "Flimmer antwortet auf Port ${PORT}." + leise "$ANTWORT" + fi printf '\n%s✓ Fertig — Version %s%s\n' "$GUT" "$NEUE_VERSION" "$AUS" if [ "$VORHER_VERSION" != "$NEUE_VERSION" ]; then info "Was sich geändert hat, steht in CHANGELOG.md." fi - [ -n "$SICHERUNG" ] && leise "Sicherung: $SICHERUNG" + if [ -n "$SICHERUNG" ]; then + leise "Sicherung: $SICHERUNG" + fi exit 0 fi # --- Fehlschlag: konkreter Weg zurück ---------------------------------------- printf '\n%s✗ Die Instanz antwortet nach zwei Minuten nicht.%s\n\n' "$FEHLER" "$AUS" >&2 + +# Der Container-Zustand grenzt ein, wo es klemmt — ohne ihn sähe ein nicht +# veröffentlichter Port genauso aus wie eine abgestürzte App. +ZUSTAND=$(container_zustand) +case "$ZUSTAND" in + kein-container) + info "Es läuft gar kein app-Container. Der Start ist offenbar fehlgeschlagen." + ;; + starting) + info "Der Container startet noch. Möglicherweise dauern die Migrationen länger —" + info "sieh im Log nach und gib ihm etwas Zeit." + ;; + unhealthy) + info "Der Container läuft, meldet sich aber als nicht gesund. Der Fehler liegt" + info "also in der Anwendung, nicht am Port." + ;; + ohne-healthcheck:running) + info "Der Container läuft, das Image bringt aber keinen Healthcheck mit (ältere" + info "Fassung). Ob die App wirklich antwortet, verrät nur das Log." + ;; + ohne-healthcheck:*) + info "Der Container ist nicht gelaufen: ${ZUSTAND#ohne-healthcheck:}" + ;; + *) + info "Container-Zustand: ${ZUSTAND}" + ;; +esac +printf '\n' + info "Log ansehen:" printf ' %s logs --tail 50 app\n\n' "$COMPOSE"