fix: Build-Erkennung an den gebauten Commit koppeln, Smoke-Server sauber beenden
Some checks failed
Playwright Tests / test (push) Has been cancelled
Test and SonarQube / quality (push) Has been cancelled

Zwei Fehler im neuen Deploy-Weg, beide bei einem Testlauf gefunden.

1. Das Skript uebersprang den Build, wenn vorher von Hand git pull lief.
   Die Entscheidung haengte an HEAD vs. origin/main -- nach einem manuellen
   Pull sind beide gleich, also "Nothing to do", obwohl der vorhandene Build
   aus einem aelteren Commit stammte. Genau das ist beim Deploy von v1.120.0
   passiert: gepullt wurde, gebaut nicht, ausgeliefert blieb 1.119.4.
   Massgeblich ist jetzt .next/BUILD_COMMIT.

   Mein Rat, vor dem Skript git pull auszufuehren, hat den Fall ueberhaupt
   erst ausgeloest -- das Skript pullt selbst.

2. Der Smoke-Test-Server ueberlebte das Beenden. ( ... ) & startet eine
   Subshell, $! ist deren PID; node lief als Waise weiter und hielt Port
   3999 belegt. Jeder weitere Deploy waere daran gescheitert. exec ersetzt
   die Subshell, kill greift, kill -9 fasst nach, der ERR-Trap raeumt
   ebenfalls auf, und vorab wird geprueft ob der Port frei ist.

Beide Szenarien getestet: alter Build wird erkannt und neu gebaut,
gleicher Stand wird uebersprungen, Port bleibt frei.

v1.120.1

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-09-07 22:41:26 +02:00
parent 162c56d0d3
commit dcbcb77dba
5 changed files with 59 additions and 7 deletions

View File

@ -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

View File

@ -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

4
package-lock.json generated
View File

@ -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",

View File

@ -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": {

View File

@ -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