From 162c56d0d3f94708602bb68cc475845cfbecc2e7 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Mon, 7 Sep 2026 22:33:04 +0200 Subject: [PATCH] feat: fehlgeschlagener Build reisst die laufende App nicht mehr mit next build lief direkt auf .next -- also auf dem Verzeichnis, aus dem die laufende Instanz bedient wird. Brach der Build ab, fand PM2 server.js nicht mehr (MODULE_NOT_FOUND) und geriet in eine Neustartschleife mit ueber 790 Versuchen. Am 07.09. war die Seite dadurch rund eine Stunde unbrauchbar. Neuer Ablauf: 1. Build nach .next-build, .next bleibt unangetastet 2. statische Dateien und .env in den neuen Baum 3. Smoke-Test: der neue Build wird auf Port 3999 gestartet und muss auf /auth/login antworten -- ein Build kann durchlaufen und trotzdem nicht starten 4. erst danach umschalten, alter Stand bleibt als .next-previous liegen 5. PM2 neu starten Scheitert etwas davon, raeumt der ERR-Trap .next-build weg und die laufende App bleibt unveraendert. Die Meldung "die laufende App bleibt unberuehrt" stimmt damit endlich -- vorher war sie schlicht falsch. Geprueft wurde vorher, dass der umbenannte Build ueberhaupt startet: der standalone-Ausgabebaum traegt den distDir-Namen intern, server.js findet seine Dateien aber relativ zu sich selbst. Testweise gestartet, Login-Seite und JS-Dateien lieferten 200. Beide Faelle durchgespielt: Abbruch liess .next samt Markierung unberuehrt und raeumte .next-build weg; Erfolg schaltete um, legte .next-previous an und bestand den Smoke-Test. v1.120.0 Co-Authored-By: Claude Opus 5 --- .gitignore | 4 ++ CHANGELOG.md | 25 ++++++++++++ TODO.md | 29 ++++++++++++- next.config.js | 4 ++ package-lock.json | 4 +- package.json | 2 +- scripts/update-app.sh | 95 +++++++++++++++++++++++++++++++++++-------- 7 files changed, 141 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index f366c8d..ccd4f58 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,7 @@ INFRASTRUCTURE_REQUIREMENTS.md # feste Namen, weshalb .env.production.save durchs Raster fiel .env* !.env.example + +# Build-Arbeitsverzeichnisse von scripts/update-app.sh +.next-build/ +.next-previous/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 475601e..27a6b49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ All notable changes to this project are documented in this file. +## [1.120.0] - 2026-09-07 + +### Changed +- **Ein fehlgeschlagener Build reisst die laufende App nicht mehr mit.** `next build` lief + bisher direkt auf `.next` — also auf dem Verzeichnis, aus dem die laufende Instanz bedient + wird. Brach der Build ab, fand PM2 `server.js` nicht mehr (`MODULE_NOT_FOUND`) und geriet + in eine Neustartschleife; am 07.09. war die Seite deshalb rund eine Stunde unbrauchbar. + + Der neue Ablauf in `scripts/update-app.sh`: + 1. Build nach `.next-build` — `.next` bleibt dabei unangetastet + 2. Statische Dateien und `.env` in den neuen Baum kopieren + 3. **Smoke-Test:** Der neue Build wird auf Port 3999 gestartet und muss auf + `/auth/login` antworten. Ein Build kann durchlaufen und trotzdem nicht starten. + 4. Erst danach umschalten; der alte Stand bleibt als `.next-previous` liegen + 5. PM2 neu starten + + Scheitert irgendetwas davon, wird `.next-build` weggeräumt und die laufende App bleibt + unverändert. Zum Zurückrollen genügt `mv .next-previous .next` plus PM2-Neustart. + +- `next.config.js`: `distDir` kommt jetzt aus `NEXT_DIST_DIR` und fällt auf `.next` zurück. + +Beide Fälle sind praktisch getestet: Ein abgebrochener Lauf liess `.next` samt Markierung +unberührt und räumte `.next-build` weg; ein erfolgreicher schaltete sauber um, legte +`.next-previous` an und bestand den Smoke-Test. + ## [1.119.4] - 2026-09-07 ### Fixed diff --git a/TODO.md b/TODO.md index 359b017..b829d47 100644 --- a/TODO.md +++ b/TODO.md @@ -286,6 +286,31 @@ Umgestellt auf `dataSources.query()`; die Suche filtert jetzt auf `data_source` --- +## Block K — Deploy-Sicherheit ✅ + +### K1. Build zerstört nicht mehr die laufende App ✅ *(v1.120.0)* +- [x] Build läuft nach `.next-build`, das laufende `.next` bleibt unberührt +- [x] Smoke-Test vor dem Umschalten — der neue Build muss einmal geantwortet haben +- [x] Umschalten erst danach, alter Stand bleibt als `.next-previous` liegen +- [x] Beide Fälle getestet: Fehlschlag lässt `.next` unangetastet, Erfolg schaltet sauber um + +**Warum:** `next build` lief direkt auf `.next` — also auf dem Verzeichnis, aus dem die +laufende App bedient wird. Scheiterte der Build, war nicht nur das Update weg, sondern die +laufende Instanz gleich mit: PM2 fand `server.js` nicht mehr +(`MODULE_NOT_FOUND`) und geriet in eine Neustartschleife mit über 790 Versuchen. +Am 07.09. war die Seite dadurch rund eine Stunde unbrauchbar. + +**Zurückrollen** (falls ein Deploy doch mal Mist ausliefert): + +```bash +pm2 stop my-weekly-todo +cd /root/My-Weekly-ToDo-List +rm -rf .next && mv .next-previous .next +pm2 restart my-weekly-todo +``` + +--- + ## Block J — Build-Speicher auf dem Container ### J1. Container-RAM erhöhen *(empfohlen)* @@ -316,7 +341,7 @@ Deploy-Wegs, aber die sauberste Trennung. ## Block I — UI-Wünsche -### I1. Aktuellen Tag auch im Spaltenkopf hervorheben *(erledigt, bitte ansehen)* +### I1. Aktuellen Tag auch im Spaltenkopf hervorheben ✅ *(vom Nutzer bestätigt)* - [x] Hervorhebungsfarbe zusätzlich hinter Wochentagsname und Datum gelegt **Wunsch:** Der laufende Tag soll deutlicher auffallen. Aktuell ist nur der Spaltenkörper @@ -331,7 +356,7 @@ offenbar mit eigenem Hintergrund. Der Wochentagsname hat bereits eine eigene Reg **Zu klären:** Der Kopf ist vermutlich `sticky` und braucht deshalb einen deckenden Hintergrund — die Farbe muss also gesetzt, nicht nur geerbt werden. -### I2. Symbole in Kalenderterminen nach oben rechts *(erledigt, bitte ansehen)* +### I2. Symbole in Kalenderterminen nach oben rechts ✅ *(vom Nutzer bestätigt, Freiraum reicht)* - [x] `.event-icons-row` nach oben rechts verschoben, Titel bekommt rechts Freiraum **Wunsch:** Die kleinen Symbole (Wiederholung, Anbieter) sitzen unten rechts im Termin und diff --git a/next.config.js b/next.config.js index c73a0a4..13b9fcd 100644 --- a/next.config.js +++ b/next.config.js @@ -3,6 +3,10 @@ const { version } = require('./package.json'); const nextConfig = { output: 'standalone', + // Ziel des Builds. update-app.sh baut nach .next-build, damit das laufende .next + // waehrend des Builds unberuehrt bleibt, und schaltet erst nach erfolgreichem + // Smoke-Test um. Ohne das zerstoert ein fehlgeschlagener Build die laufende App. + distDir: process.env.NEXT_DIST_DIR || '.next', eslint: { // Aus: Der Build meldet ESLint-Fehler. Warnungen blockieren ihn nicht, nur Errors. // diff --git a/package-lock.json b/package-lock.json index 89c891e..52e714e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "my-weekly-todo-list", - "version": "1.119.4", + "version": "1.120.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "my-weekly-todo-list", - "version": "1.119.4", + "version": "1.120.0", "license": "MIT", "dependencies": { "@auth/prisma-adapter": "^2.11.1", diff --git a/package.json b/package.json index 0127346..202257f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.119.4", + "version": "1.120.0", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/scripts/update-app.sh b/scripts/update-app.sh index ed65b1b..59da367 100755 --- a/scripts/update-app.sh +++ b/scripts/update-app.sh @@ -34,7 +34,10 @@ main() { log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; } - trap 'log "!!! Update FAILED at line $LINENO (exit $?) — the running build is untouched"' ERR + # Bei einem Abbruch den halbfertigen Build wegraeumen. Der Hinweis "die laufende App + # bleibt unberuehrt" stimmt seit v1.120.0 tatsaechlich -- vorher wurde direkt auf .next + # gebaut, ein Fehlschlag riss die laufende Instanz mit. + trap 'log "!!! Update FAILED at line $LINENO (exit $?) — die laufende App bleibt unberuehrt"; rm -rf "${APP_DIR:-.}/.next-build"' ERR # Fail fast instead of hanging on a credential prompt when run without a terminal # (systemd has no tty). The host authenticates via a read-only SSH deploy key. @@ -95,24 +98,82 @@ main() { log "--- Regenerating Prisma client ---" npx prisma generate - # Der Standard-Heap von Node richtet sich nach dem Arbeitsspeicher und lag auf diesem - # Container bei rund 1,5 GB -- zu wenig fuer `next build`. Ein erster Versuch mit 2048 - # scheiterte ebenfalls: der Build starb punktgenau bei 2045 MB. Gemessener Bedarf sind - # rund 2,6 GB, deshalb 4 GB. Der Container hat 6 GB, das laesst genug Luft fuer den - # laufenden PM2-Prozess. - log "--- Building application ---" - NODE_OPTIONS="--max-old-space-size=4096" npm run build + # ── Build in ein Nebenverzeichnis ──────────────────────────────────────────────── + # + # Frueher lief `next build` direkt auf .next -- also auf dem Verzeichnis, aus dem die + # laufende App bedient wird. Scheiterte der Build, war nicht nur das Update weg, sondern + # die laufende Instanz gleich mit: PM2 fand server.js nicht mehr und geriet in eine + # Neustartschleife. Genau das ist am 07.09. passiert, die Seite war rund eine Stunde + # unbrauchbar. + # + # Jetzt entsteht der neue Build unter .next-build. Das laufende .next wird dabei nicht + # angefasst. Erst wenn der neue Build gebaut UND probeweise gestartet werden konnte, + # wird umgeschaltet. + # + # Nodes Standard-Heap lag hier bei rund 1,5 GB und reichte nicht; ein Versuch mit 2048 + # scheiterte punktgenau bei 2045 MB. Gemessener Bedarf sind rund 2,6 GB. + local BUILD_DIR=".next-build" + rm -rf "$BUILD_DIR" - # next.config.js sets output: 'standalone', and Next.js does NOT copy public/ or - # .next/static/ into .next/standalone — that is a documented manual step. Skipping it - # ships new server code whose HTML references new chunk hashes while the old chunks sit - # in the standalone tree: they 404, React never hydrates, and every page hangs on its - # server-rendered shell ("Loading session...") while the API still answers 200. + log "--- Building application (nach $BUILD_DIR) ---" + NEXT_DIST_DIR="$BUILD_DIR" NODE_OPTIONS="--max-old-space-size=4096" npm run build + + # next.config.js setzt output: 'standalone', und Next.js kopiert public/ und static/ + # NICHT selbst dorthin -- ein dokumentierter manueller Schritt. Wird er ausgelassen, + # verweist das neue HTML auf Chunks, die im standalone-Baum fehlen: sie liefern 404, + # React hydriert nie, und jede Seite haengt auf "Loading session...", waehrend die API + # weiter 200 antwortet. + # + # Der innere Ordner traegt den Namen des distDir -- deshalb .next-build/static und nicht + # .next/static. Beim Umschalten wandert er unveraendert mit; server.js findet ihn + # relativ zu sich selbst (geprueft). log "--- Copying static assets into standalone output ---" - cp -r public .next/standalone/ - mkdir -p .next/standalone/.next/static - cp -r .next/static .next/standalone/.next/ - cp .env .next/standalone/ 2>/dev/null || true + cp -r public "$BUILD_DIR/standalone/" + mkdir -p "$BUILD_DIR/standalone/$BUILD_DIR/static" + cp -r "$BUILD_DIR/static/." "$BUILD_DIR/standalone/$BUILD_DIR/static/" + cp .env "$BUILD_DIR/standalone/" 2>/dev/null || true + + # ── Smoke-Test auf einem freien Port ───────────────────────────────────────────── + # + # Ein Build kann durchlaufen und trotzdem nicht starten. Bevor der neue Stand live + # geht, muss er einmal wirklich geantwortet haben. + log "--- Smoke-Test des neuen Builds ---" + local SMOKE_PORT=3999 + ( cd "$BUILD_DIR/standalone" && PORT=$SMOKE_PORT node server.js > /tmp/smoke-test.log 2>&1 ) & + local SMOKE_PID=$! + local SMOKE_OK=0 + for _ in $(seq 1 20); do + sleep 1 + if curl -sf -o /dev/null --max-time 3 "http://127.0.0.1:$SMOKE_PORT/auth/login"; then + SMOKE_OK=1 + break + fi + done + kill "$SMOKE_PID" 2>/dev/null || true + wait "$SMOKE_PID" 2>/dev/null || true + + if [ "$SMOKE_OK" -ne 1 ]; then + log "!!! Smoke-Test fehlgeschlagen -- der neue Build antwortet nicht." + log " Die laufende App bleibt unveraendert. Ausgabe:" + tail -20 /tmp/smoke-test.log | sed 's/^/ /' + rm -rf "$BUILD_DIR" + return 1 + fi + log " Smoke-Test bestanden." + + # ── Umschalten ─────────────────────────────────────────────────────────────────── + # + # Ab hier ist der neue Build erwiesenermassen lauffaehig. Der alte bleibt als + # .next-previous liegen; zum Zurueckrollen genuegt: + # pm2 stop my-weekly-todo + # rm -rf .next && mv .next-previous .next + # pm2 restart my-weekly-todo + log "--- Umschalten auf den neuen Build ---" + rm -rf .next-previous + if [ -d .next ]; then + mv .next .next-previous + fi + mv "$BUILD_DIR" .next # At boot this script runs before pm2-root.service, so the PM2 daemon is not up yet and # there is nothing to restart — pm2-root.service resurrects the app right after. Probing