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 <noreply@anthropic.com>
This commit is contained in:
parent
bea9723a6f
commit
162c56d0d3
4
.gitignore
vendored
4
.gitignore
vendored
@ -60,3 +60,7 @@ INFRASTRUCTURE_REQUIREMENTS.md
|
|||||||
# feste Namen, weshalb .env.production.save durchs Raster fiel
|
# feste Namen, weshalb .env.production.save durchs Raster fiel
|
||||||
.env*
|
.env*
|
||||||
!.env.example
|
!.env.example
|
||||||
|
|
||||||
|
# Build-Arbeitsverzeichnisse von scripts/update-app.sh
|
||||||
|
.next-build/
|
||||||
|
.next-previous/
|
||||||
|
|||||||
25
CHANGELOG.md
25
CHANGELOG.md
@ -2,6 +2,31 @@
|
|||||||
|
|
||||||
All notable changes to this project are documented in this file.
|
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
|
## [1.119.4] - 2026-09-07
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
29
TODO.md
29
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
|
## Block J — Build-Speicher auf dem Container
|
||||||
|
|
||||||
### J1. Container-RAM erhöhen *(empfohlen)*
|
### J1. Container-RAM erhöhen *(empfohlen)*
|
||||||
@ -316,7 +341,7 @@ Deploy-Wegs, aber die sauberste Trennung.
|
|||||||
|
|
||||||
## Block I — UI-Wünsche
|
## 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
|
- [x] Hervorhebungsfarbe zusätzlich hinter Wochentagsname und Datum gelegt
|
||||||
|
|
||||||
**Wunsch:** Der laufende Tag soll deutlicher auffallen. Aktuell ist nur der Spaltenkörper
|
**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
|
**Zu klären:** Der Kopf ist vermutlich `sticky` und braucht deshalb einen deckenden
|
||||||
Hintergrund — die Farbe muss also gesetzt, nicht nur geerbt werden.
|
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
|
- [x] `.event-icons-row` nach oben rechts verschoben, Titel bekommt rechts Freiraum
|
||||||
|
|
||||||
**Wunsch:** Die kleinen Symbole (Wiederholung, Anbieter) sitzen unten rechts im Termin und
|
**Wunsch:** Die kleinen Symbole (Wiederholung, Anbieter) sitzen unten rechts im Termin und
|
||||||
|
|||||||
@ -3,6 +3,10 @@ const { version } = require('./package.json');
|
|||||||
|
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: 'standalone',
|
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: {
|
eslint: {
|
||||||
// Aus: Der Build meldet ESLint-Fehler. Warnungen blockieren ihn nicht, nur Errors.
|
// Aus: Der Build meldet ESLint-Fehler. Warnungen blockieren ihn nicht, nur Errors.
|
||||||
//
|
//
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"name": "my-weekly-todo-list",
|
||||||
"version": "1.119.4",
|
"version": "1.120.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "my-weekly-todo-list",
|
"name": "my-weekly-todo-list",
|
||||||
"version": "1.119.4",
|
"version": "1.120.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/prisma-adapter": "^2.11.1",
|
"@auth/prisma-adapter": "^2.11.1",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"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",
|
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -34,7 +34,10 @@ main() {
|
|||||||
|
|
||||||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }
|
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
|
# 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.
|
# (systemd has no tty). The host authenticates via a read-only SSH deploy key.
|
||||||
@ -95,24 +98,82 @@ main() {
|
|||||||
log "--- Regenerating Prisma client ---"
|
log "--- Regenerating Prisma client ---"
|
||||||
npx prisma generate
|
npx prisma generate
|
||||||
|
|
||||||
# Der Standard-Heap von Node richtet sich nach dem Arbeitsspeicher und lag auf diesem
|
# ── Build in ein Nebenverzeichnis ────────────────────────────────────────────────
|
||||||
# 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
|
# Frueher lief `next build` direkt auf .next -- also auf dem Verzeichnis, aus dem die
|
||||||
# rund 2,6 GB, deshalb 4 GB. Der Container hat 6 GB, das laesst genug Luft fuer den
|
# laufende App bedient wird. Scheiterte der Build, war nicht nur das Update weg, sondern
|
||||||
# laufenden PM2-Prozess.
|
# die laufende Instanz gleich mit: PM2 fand server.js nicht mehr und geriet in eine
|
||||||
log "--- Building application ---"
|
# Neustartschleife. Genau das ist am 07.09. passiert, die Seite war rund eine Stunde
|
||||||
NODE_OPTIONS="--max-old-space-size=4096" npm run build
|
# 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
|
log "--- Building application (nach $BUILD_DIR) ---"
|
||||||
# .next/static/ into .next/standalone — that is a documented manual step. Skipping it
|
NEXT_DIST_DIR="$BUILD_DIR" NODE_OPTIONS="--max-old-space-size=4096" npm run build
|
||||||
# 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
|
# next.config.js setzt output: 'standalone', und Next.js kopiert public/ und static/
|
||||||
# server-rendered shell ("Loading session...") while the API still answers 200.
|
# 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 ---"
|
log "--- Copying static assets into standalone output ---"
|
||||||
cp -r public .next/standalone/
|
cp -r public "$BUILD_DIR/standalone/"
|
||||||
mkdir -p .next/standalone/.next/static
|
mkdir -p "$BUILD_DIR/standalone/$BUILD_DIR/static"
|
||||||
cp -r .next/static .next/standalone/.next/
|
cp -r "$BUILD_DIR/static/." "$BUILD_DIR/standalone/$BUILD_DIR/static/"
|
||||||
cp .env .next/standalone/ 2>/dev/null || true
|
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
|
# 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
|
# there is nothing to restart — pm2-root.service resurrects the app right after. Probing
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user