diff --git a/CHANGELOG.md b/CHANGELOG.md index 27a6b49..ca7375c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ All notable changes to this project are documented in this file. +## [1.120.1] - 2026-09-07 + +### Fixed +- **Das Skript übersprang den Build, wenn vorher von Hand `git pull` ausgeführt wurde.** + Die Entscheidung hing an `HEAD` vs. `origin/main` — nach einem manuellen Pull sind beide + gleich, und das Skript sah keinen Grund zu bauen, obwohl der vorhandene Build aus einem + älteren Commit stammte. Es lieferte weiter den alten Stand aus und meldete dabei + „Nothing to do". Massgeblich ist jetzt `.next/BUILD_COMMIT`: aus welchem Commit der + vorhandene Build tatsächlich stammt. +- **Der Smoke-Test-Server überlebte das Beenden.** `( … ) &` startet eine Subshell, `$!` + ist deren PID — `node` darin lief als Waise weiter und hielt Port 3999 belegt, womit + jeder weitere Deploy am Smoke-Test gescheitert wäre. Mit `exec` ersetzt `node` die + Subshell, `kill` greift, und ein `kill -9` fasst nach. Zusätzlich prüft das Skript vorab, + ob der Port frei ist, und der ERR-Trap beendet den Server ebenfalls. + +Beide Fehler kamen bei einem Testlauf ans Licht, nicht in Produktion. + ## [1.120.0] - 2026-09-07 ### Changed diff --git a/TODO.md b/TODO.md index b829d47..b453f47 100644 --- a/TODO.md +++ b/TODO.md @@ -293,6 +293,11 @@ Umgestellt auf `dataSources.query()`; die Suche filtert jetzt auf `data_source` - [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 +- [x] Build-Erkennung an den gebauten Commit gekoppelt (`.next/BUILD_COMMIT`) statt an + `HEAD` vs. `origin/main` — sonst überspringt das Skript den Build, wenn jemand vorher + von Hand `git pull` ausführt +- [x] Smoke-Test-Server wird zuverlässig beendet (`exec`, sonst überlebt `node` als Waise + und blockiert Port 3999 für alle weiteren Deploys) **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 diff --git a/package-lock.json b/package-lock.json index 52e714e..671e396 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "my-weekly-todo-list", - "version": "1.120.0", + "version": "1.120.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "my-weekly-todo-list", - "version": "1.120.0", + "version": "1.120.1", "license": "MIT", "dependencies": { "@auth/prisma-adapter": "^2.11.1", diff --git a/package.json b/package.json index 202257f..8dd6a32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.120.0", + "version": "1.120.1", "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 59da367..3fe836c 100755 --- a/scripts/update-app.sh +++ b/scripts/update-app.sh @@ -37,7 +37,7 @@ main() { # 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 + trap 'log "!!! Update FAILED at line $LINENO (exit $?) — die laufende App bleibt unberuehrt"; kill "${SMOKE_PID:-0}" 2>/dev/null || true; 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. @@ -65,14 +65,23 @@ main() { LOCAL=$(git rev-parse HEAD) REMOTE=$(git rev-parse origin/main) - # Rebuild when the code moved, when there is no build to serve, or when asked to. + # Gebaut wird, wenn der vorhandene Build nicht zum aktuellen Stand passt. + # + # Frueher wurde HEAD mit origin/main verglichen. Das ging schief, sobald jemand vor dem + # Skript von Hand `git pull` ausfuehrte: Dann waren beide gleich, das Skript sah keinen + # Grund zu bauen -- und lieferte weiter den alten Build aus, obwohl neuer Code dalag. + # Massgeblich ist deshalb, aus welchem Commit der vorhandene Build stammt. + local BUILT_COMMIT="" + [ -f "$APP_DIR/.next/BUILD_COMMIT" ] && BUILT_COMMIT=$(cat "$APP_DIR/.next/BUILD_COMMIT") + local NEEDS_BUILD=0 [ "$LOCAL" != "$REMOTE" ] && NEEDS_BUILD=1 [ ! -f "$APP_DIR/.next/standalone/server.js" ] && NEEDS_BUILD=1 + [ "$BUILT_COMMIT" != "$REMOTE" ] && NEEDS_BUILD=1 [ "$FORCE" = "--force" ] && NEEDS_BUILD=1 if [ "$NEEDS_BUILD" -eq 0 ]; then - log "Already up to date and a build is in place. Nothing to do." + log "Vorhandener Build stammt aus ${BUILT_COMMIT:0:7} und passt zum aktuellen Stand." log "=== Update finished (no changes) ===" return 0 fi @@ -139,7 +148,20 @@ main() { # 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 ) & + + # Steht noch ein Server aus einem abgebrochenen Lauf auf dem Port, wuerde der + # Smoke-Test dessen Antwort messen statt der des neuen Builds. + if (ss -ltn 2>/dev/null || netstat -ltn 2>/dev/null) | grep -q ":$SMOKE_PORT "; then + log "!!! Port $SMOKE_PORT ist belegt -- vermutlich ein Rest aus einem abgebrochenen Lauf." + log " Bitte pruefen: ss -ltnp | grep $SMOKE_PORT" + rm -rf "$BUILD_DIR" + return 1 + fi + + # exec ist hier wesentlich: Ohne das ersetzt node die Subshell NICHT, $! zeigt auf die + # Subshell, und der node-Prozess ueberlebt das spaetere kill als Waise -- Port 3999 + # bliebe belegt und jeder weitere Deploy scheiterte daran. + ( cd "$BUILD_DIR/standalone" && exec env 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 @@ -151,6 +173,11 @@ main() { done kill "$SMOKE_PID" 2>/dev/null || true wait "$SMOKE_PID" 2>/dev/null || true + # Nachfassen, falls der Prozess das TERM ignoriert hat. + if kill -0 "$SMOKE_PID" 2>/dev/null; then + sleep 2 + kill -9 "$SMOKE_PID" 2>/dev/null || true + fi if [ "$SMOKE_OK" -ne 1 ]; then log "!!! Smoke-Test fehlgeschlagen -- der neue Build antwortet nicht." @@ -175,6 +202,9 @@ main() { fi mv "$BUILD_DIR" .next + # Damit der naechste Lauf erkennt, ob der Build noch zum Code passt. + git rev-parse HEAD > .next/BUILD_COMMIT + # 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 # with `pm2 describe` would be wrong here: any pm2 command spawns a daemon, and a stray