From 10a37180dedac74fa280687c18835339f155c210 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 29 Oct 2026 15:48:17 +0100 Subject: [PATCH] Refactor deployment scripts to improve Node.js version handling and PM2 process management - Updated `ensure_node_version` function in `deploy-production.sh` and `deploy-test.sh` to use a common script for Node.js version validation. - Simplified PM2 instance restart logic by introducing `deploy_restart_pm2` function in a new `deploy-runtime.sh` script. - Replaced direct PM2 commands with the new function to handle instance restarts and HTTP readiness checks. - Modified PM2 configuration files to set the script directly instead of using `node` as the interpreter, allowing for better compatibility with older Node.js versions. - Added tests for the new deployment runtime functions in `deploy-runtime.spec.ts` to ensure correct behavior during deployment. - Updated `package-lock.json` to include new dependencies and ensure compatibility with the latest changes. --- deploy-production.sh | 48 +-- deploy-test.sh | 63 +--- harheimertc.config.cjs | 5 +- harheimertc.test.config.cjs | 5 +- package-lock.json | 569 ++++++++++++++++++++++++++++++++++- scripts/deploy-runtime.sh | 45 +++ tests/deploy-runtime.spec.ts | 58 ++++ 7 files changed, 693 insertions(+), 100 deletions(-) create mode 100644 scripts/deploy-runtime.sh create mode 100644 tests/deploy-runtime.spec.ts diff --git a/deploy-production.sh b/deploy-production.sh index 60ebdcc..15b80ac 100755 --- a/deploy-production.sh +++ b/deploy-production.sh @@ -137,13 +137,8 @@ ensure_node_version() { local node_version node_version="$(node -p 'process.versions.node')" - if ! node -e 'const [major, minor] = process.versions.node.split(".").map(Number); process.exit(major > 22 || (major === 22 && minor >= 12) ? 0 : 1)' >/dev/null 2>&1; then - echo "ERROR: Node.js >= 22.12.0 wird benötigt, aktuell ist $node_version aktiv." - echo "Bitte Node 22 installieren/aktivieren, z.B.:" - echo " nvm install 22" - echo " nvm alias default 22" - exit 1 - fi + # Dieselbe Laufzeitprüfung wie beim Build und im Nitro-Server. + node "$SCRIPT_DIR/scripts/require-safe-node.cjs" echo " Node.js $node_version" } @@ -544,29 +539,11 @@ if ! command -v pm2 &> /dev/null; then exit 1 fi -# Funktion zum Starten/Neustarten einer PM2-Instanz +# Gemeinsamer Start mit der ausgewählten Node-Laufzeit und HTTP-Prüfung. +# shellcheck source=scripts/deploy-runtime.sh +source "$SCRIPT_DIR/scripts/deploy-runtime.sh" restart_pm2_instance() { - local instance_name=$1 - if ! pm2 describe "$instance_name" &> /dev/null; then - echo " WARNING: PM2-Prozess '$instance_name' existiert nicht." - echo " Versuche, den Prozess zu starten..." - if pm2 start harheimertc.config.cjs --only "$instance_name" --update-env; then - echo " ✓ PM2-Prozess '$instance_name' gestartet" - return 0 - else - echo " ERROR: Konnte PM2-Prozess '$instance_name' nicht starten." - return 1 - fi - else - echo " Restarting $instance_name with --update-env..." - if pm2 restart "$instance_name" --update-env; then - echo " ✓ PM2-Prozess '$instance_name' neu gestartet" - return 0 - else - echo " ERROR: PM2-Restart für '$instance_name' fehlgeschlagen!" - return 1 - fi - fi + deploy_restart_pm2 "$1" "$SCRIPT_DIR/harheimertc.config.cjs" } # Starte/Neustarte die Produktionsinstanz @@ -576,23 +553,16 @@ if ! restart_pm2_instance "harheimertc"; then INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1)) fi -# Prüfe, ob der Prozess läuft -sleep 2 -echo "" -echo " Checking PM2 instances status..." - -if pm2 describe harheimertc | grep -q "online"; then - echo " ✓ PM2-Prozess 'harheimertc' läuft (online)" -else - echo " WARNING: PM2-Prozess 'harheimertc' ist nicht online. Prüfe Logs: pm2 logs harheimertc" +if ! deploy_wait_for_http "harheimertc" "http://127.0.0.1:3100/"; then INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1)) fi if [ "$INSTANCE_ERRORS" -gt 0 ]; then echo "" - echo "WARNING: Einige PM2-Instanzen haben Probleme. Bitte manuell prüfen:" + echo "ERROR: Produktionsinstanz konnte nicht erfolgreich gestartet werden. Bitte manuell prüfen:" echo " pm2 status" echo " pm2 logs harheimertc" + exit 1 fi echo "" diff --git a/deploy-test.sh b/deploy-test.sh index bd7f3df..cf84c4f 100755 --- a/deploy-test.sh +++ b/deploy-test.sh @@ -150,13 +150,8 @@ ensure_node_version() { local node_version node_version="$(node -p 'process.versions.node')" - if ! node -e 'const [major, minor] = process.versions.node.split(".").map(Number); process.exit(major > 22 || (major === 22 && minor >= 12) ? 0 : 1)' >/dev/null 2>&1; then - echo "ERROR: Node.js >= 22.12.0 wird benötigt, aktuell ist $node_version aktiv." - echo "Bitte Node 22 installieren/aktivieren, z.B.:" - echo " nvm install 22" - echo " nvm alias default 22" - exit 1 - fi + # Dieselbe Laufzeitprüfung wie beim Build und im Nitro-Server. + node "$SCRIPT_DIR/scripts/require-safe-node.cjs" echo " Node.js $node_version" } @@ -545,39 +540,11 @@ if ! command -v pm2 &> /dev/null; then exit 1 fi -# Funktion zum Starten/Neustarten einer PM2-Instanz +# Gemeinsamer Start mit der ausgewählten Node-Laufzeit und HTTP-Prüfung. +# shellcheck source=scripts/deploy-runtime.sh +source "$SCRIPT_DIR/scripts/deploy-runtime.sh" restart_pm2_instance() { - local instance_name=$1 - local config_file="$SCRIPT_DIR/harheimertc.test.config.cjs" - - # Prüfe, ob die Konfigurationsdatei existiert - if [ ! -f "$config_file" ]; then - echo " ERROR: PM2-Konfigurationsdatei nicht gefunden: $config_file" - return 1 - fi - - if ! pm2 describe "$instance_name" &> /dev/null; then - echo " WARNING: PM2-Prozess '$instance_name' existiert nicht." - echo " Versuche, den Prozess zu starten..." - echo " Verwende Konfiguration: $config_file" - if pm2 start "$config_file" --update-env; then - echo " ✓ PM2-Prozess '$instance_name' gestartet" - return 0 - else - echo " ERROR: Konnte PM2-Prozess '$instance_name' nicht starten." - echo " Bitte manuell prüfen: pm2 logs $instance_name" - return 1 - fi - else - echo " Restarting $instance_name with --update-env..." - if pm2 restart "$instance_name" --update-env; then - echo " ✓ PM2-Prozess '$instance_name' neu gestartet" - return 0 - else - echo " ERROR: PM2-Restart für '$instance_name' fehlgeschlagen!" - return 1 - fi - fi + deploy_restart_pm2 "$1" "$SCRIPT_DIR/harheimertc.test.config.cjs" } # Starte/Neustarte Test-Instanz @@ -589,23 +556,7 @@ if ! restart_pm2_instance "harheimertc.test"; then exit 1 fi -# Prüfe, ob der Prozess läuft -sleep 2 -echo "" -echo " Checking PM2 instance status..." - -if pm2 describe harheimertc.test | grep -q "online"; then - echo " ✓ PM2-Prozess 'harheimertc.test' läuft (online)" -else - echo " ERROR: PM2-Prozess 'harheimertc.test' ist nicht online. Prüfe Logs: pm2 logs harheimertc.test" - exit 1 -fi - -if ! curl --fail --silent --show-error --max-time 15 http://127.0.0.1:3102/ >/dev/null; then - echo "ERROR: Test-Instanz antwortet nicht auf Port 3102." - exit 1 -fi -echo " ✓ HTTP-Healthcheck auf Port 3102 erfolgreich" +deploy_wait_for_http "harheimertc.test" "http://127.0.0.1:3102/" echo "" echo "=== Test-Instanz Deployment completed successfully! ===" diff --git a/harheimertc.config.cjs b/harheimertc.config.cjs index b3dbd48..13c5be4 100755 --- a/harheimertc.config.cjs +++ b/harheimertc.config.cjs @@ -49,8 +49,9 @@ module.exports = { { name: 'harheimertc', // Nuxt 4 production build: direkt den Node-Server starten (kein "preview mode") - script: 'node', - args: '.output/server/index.mjs', + script: '.output/server/index.mjs', + // Der PM2-Daemon kann noch unter einer älteren Node-Version laufen. + interpreter: process.env.DEPLOY_NODE_BINARY || process.execPath, cwd: '/var/www/harheimertc', instances: 1, autorestart: true, diff --git a/harheimertc.test.config.cjs b/harheimertc.test.config.cjs index 193ade6..cbe5678 100755 --- a/harheimertc.test.config.cjs +++ b/harheimertc.test.config.cjs @@ -51,8 +51,9 @@ module.exports = { { name: 'harheimertc.test', // Nuxt 4 production build: direkt den Node-Server starten (kein "preview mode") - script: 'node', - args: '.output/server/index.mjs', + script: '.output/server/index.mjs', + // Der PM2-Daemon kann noch unter einer älteren Node-Version laufen. + interpreter: process.env.DEPLOY_NODE_BINARY || process.execPath, cwd: '/var/www/harheimertc.test', instances: 1, autorestart: true, diff --git a/package-lock.json b/package-lock.json index ffc18d9..dd52295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -701,8 +701,266 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -715,6 +973,150 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "devOptional": true, @@ -1559,6 +1961,8 @@ }, "node_modules/@napi-rs/canvas": { "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.80.tgz", + "integrity": "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==", "license": "MIT", "workspaces": [ "e2e/*" @@ -1579,13 +1983,63 @@ "@napi-rs/canvas-win32-x64-msvc": "0.1.80" } }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "node_modules/@napi-rs/canvas-android-arm64": { "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz", + "integrity": "sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz", + "integrity": "sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz", + "integrity": "sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz", + "integrity": "sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, "os": [ "linux" ], @@ -1593,6 +2047,117 @@ "node": ">= 10" } }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz", + "integrity": "sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz", + "integrity": "sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz", + "integrity": "sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz", + "integrity": "sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz", + "integrity": "sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz", + "integrity": "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@napi-rs/lzma-linux-x64-gnu": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", @@ -6047,6 +6612,8 @@ }, "node_modules/esbuild": { "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "hasInstallScript": true, "license": "MIT", "bin": { diff --git a/scripts/deploy-runtime.sh b/scripts/deploy-runtime.sh new file mode 100644 index 0000000..4d75e89 --- /dev/null +++ b/scripts/deploy-runtime.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Sourced by the test and production deployment scripts. + +deploy_restart_pm2() { + local instance_name="$1" + local config_file="$2" + local node_binary + + if [ ! -f "$config_file" ]; then + echo "ERROR: PM2-Konfigurationsdatei nicht gefunden: $config_file" + return 1 + fi + + node_binary="$(node -p 'process.execPath')" || return 1 + echo " Starting/restarting $instance_name using Node: $node_binary" + # PM2 retains the old script path even when restarting via a config file. + # Recreate only this app so legacy `script: node` entries migrate as well. + if pm2 describe "$instance_name" >/dev/null 2>&1; then + pm2 delete "$instance_name" || return 1 + fi + DEPLOY_NODE_BINARY="$node_binary" pm2 start "$config_file" \ + --only "$instance_name" --update-env +} + +deploy_wait_for_http() { + local instance_name="$1" + local url="$2" + local attempt + + echo " Waiting for $instance_name: $url" + for ((attempt = 1; attempt <= 30; attempt++)); do + if curl --fail --silent --max-time 2 "$url" >/dev/null; then + echo " ✓ HTTP-Healthcheck erfolgreich: $url" + return 0 + fi + if [ "$attempt" -lt 30 ]; then + sleep 2 + fi + done + + echo "ERROR: $instance_name antwortet nicht erfolgreich auf $url" + pm2 describe "$instance_name" || true + pm2 logs "$instance_name" --nostream --lines 30 || true + return 1 +} diff --git a/tests/deploy-runtime.spec.ts b/tests/deploy-runtime.spec.ts new file mode 100644 index 0000000..7e6dcbb --- /dev/null +++ b/tests/deploy-runtime.spec.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from 'vitest' +import { spawnSync } from 'node:child_process' +import path from 'node:path' + +function runHelper(script: string) { + return spawnSync('bash', ['-c', 'set -euo pipefail; source "$DEPLOY_HELPER"; ' + script], { + encoding: 'utf8', + env: { + ...process.env, + DEPLOY_HELPER: path.resolve('scripts/deploy-runtime.sh'), + DEPLOY_CONFIG: path.resolve('harheimertc.test.config.cjs') + } + }) +} + +describe('Deployment runtime', () => { + it('reloads the selected app configuration with an absolute Node runtime', () => { + const result = runHelper(` + pm2() { + if [ "$1" = describe ]; then return 0; fi + printf 'runtime=%s\\n' "\${DEPLOY_NODE_BINARY:-}" + printf '%s\\n' "$@" + } + deploy_restart_pm2 harheimertc.test "$DEPLOY_CONFIG" + `) + expect(result.status).toBe(0) + expect(result.stdout).toContain('runtime=/') + expect(result.stdout).toContain('delete\nharheimertc.test') + expect(result.stdout).toContain('start\n' + path.resolve('harheimertc.test.config.cjs')) + expect(result.stdout).toContain('--only\nharheimertc.test\n--update-env') + }) + + it('waits for HTTP readiness after initial connection failures', () => { + const result = runHelper(` + attempts=0 + curl() { attempts=$((attempts + 1)); [ "$attempts" -ge 3 ]; } + sleep() { :; } + pm2() { echo 'Unexpected diagnostics'; return 1; } + deploy_wait_for_http harheimertc.test http://127.0.0.1:3102/ + [ "$attempts" -eq 3 ] + `) + expect(result.status).toBe(0) + expect(result.stdout).toContain('HTTP-Healthcheck erfolgreich') + expect(result.stdout).not.toContain('Unexpected diagnostics') + }) + + it('fails with PM2 diagnostics when the server never becomes ready', () => { + const result = runHelper(` + curl() { return 1; } + sleep() { :; } + pm2() { printf '%s\\n' "$*"; } + deploy_wait_for_http harheimertc.test http://127.0.0.1:3102/ + `) + expect(result.status).toBe(1) + expect(result.stdout).toContain('describe harheimertc.test') + expect(result.stdout).toContain('logs harheimertc.test --nostream --lines 30') + }) +})