Refactor deployment scripts to improve Node.js version handling and PM2 process management
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 7m36s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m55s

- 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.
This commit is contained in:
Torsten Schulz (local)
2026-10-29 15:48:17 +01:00
parent 4ecc2cc90c
commit 10a37180de
7 changed files with 693 additions and 100 deletions

View File

@@ -137,13 +137,8 @@ ensure_node_version() {
local node_version local node_version
node_version="$(node -p 'process.versions.node')" 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 # Dieselbe Laufzeitprüfung wie beim Build und im Nitro-Server.
echo "ERROR: Node.js >= 22.12.0 wird benötigt, aktuell ist $node_version aktiv." node "$SCRIPT_DIR/scripts/require-safe-node.cjs"
echo "Bitte Node 22 installieren/aktivieren, z.B.:"
echo " nvm install 22"
echo " nvm alias default 22"
exit 1
fi
echo " Node.js $node_version" echo " Node.js $node_version"
} }
@@ -544,29 +539,11 @@ if ! command -v pm2 &> /dev/null; then
exit 1 exit 1
fi 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() { restart_pm2_instance() {
local instance_name=$1 deploy_restart_pm2 "$1" "$SCRIPT_DIR/harheimertc.config.cjs"
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
} }
# Starte/Neustarte die Produktionsinstanz # Starte/Neustarte die Produktionsinstanz
@@ -576,23 +553,16 @@ if ! restart_pm2_instance "harheimertc"; then
INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1)) INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1))
fi fi
# Prüfe, ob der Prozess läuft if ! deploy_wait_for_http "harheimertc" "http://127.0.0.1:3100/"; then
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"
INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1)) INSTANCE_ERRORS=$((INSTANCE_ERRORS + 1))
fi fi
if [ "$INSTANCE_ERRORS" -gt 0 ]; then if [ "$INSTANCE_ERRORS" -gt 0 ]; then
echo "" 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 status"
echo " pm2 logs harheimertc" echo " pm2 logs harheimertc"
exit 1
fi fi
echo "" echo ""

View File

@@ -150,13 +150,8 @@ ensure_node_version() {
local node_version local node_version
node_version="$(node -p 'process.versions.node')" 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 # Dieselbe Laufzeitprüfung wie beim Build und im Nitro-Server.
echo "ERROR: Node.js >= 22.12.0 wird benötigt, aktuell ist $node_version aktiv." node "$SCRIPT_DIR/scripts/require-safe-node.cjs"
echo "Bitte Node 22 installieren/aktivieren, z.B.:"
echo " nvm install 22"
echo " nvm alias default 22"
exit 1
fi
echo " Node.js $node_version" echo " Node.js $node_version"
} }
@@ -545,39 +540,11 @@ if ! command -v pm2 &> /dev/null; then
exit 1 exit 1
fi 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() { restart_pm2_instance() {
local instance_name=$1 deploy_restart_pm2 "$1" "$SCRIPT_DIR/harheimertc.test.config.cjs"
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
} }
# Starte/Neustarte Test-Instanz # Starte/Neustarte Test-Instanz
@@ -589,23 +556,7 @@ if ! restart_pm2_instance "harheimertc.test"; then
exit 1 exit 1
fi fi
# Prüfe, ob der Prozess läuft deploy_wait_for_http "harheimertc.test" "http://127.0.0.1:3102/"
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"
echo "" echo ""
echo "=== Test-Instanz Deployment completed successfully! ===" echo "=== Test-Instanz Deployment completed successfully! ==="

View File

@@ -49,8 +49,9 @@ module.exports = {
{ {
name: 'harheimertc', name: 'harheimertc',
// Nuxt 4 production build: direkt den Node-Server starten (kein "preview mode") // Nuxt 4 production build: direkt den Node-Server starten (kein "preview mode")
script: 'node', script: '.output/server/index.mjs',
args: '.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', cwd: '/var/www/harheimertc',
instances: 1, instances: 1,
autorestart: true, autorestart: true,

View File

@@ -51,8 +51,9 @@ module.exports = {
{ {
name: 'harheimertc.test', name: 'harheimertc.test',
// Nuxt 4 production build: direkt den Node-Server starten (kein "preview mode") // Nuxt 4 production build: direkt den Node-Server starten (kein "preview mode")
script: 'node', script: '.output/server/index.mjs',
args: '.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', cwd: '/var/www/harheimertc.test',
instances: 1, instances: 1,
autorestart: true, autorestart: true,

569
package-lock.json generated
View File

@@ -701,8 +701,266 @@
"tslib": "^2.4.0" "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": { "node_modules/@esbuild/linux-x64": {
"version": "0.28.1", "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": [ "cpu": [
"x64" "x64"
], ],
@@ -715,6 +973,150 @@
"node": ">=18" "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": { "node_modules/@eslint-community/eslint-utils": {
"version": "4.9.1", "version": "4.9.1",
"devOptional": true, "devOptional": true,
@@ -1559,6 +1961,8 @@
}, },
"node_modules/@napi-rs/canvas": { "node_modules/@napi-rs/canvas": {
"version": "0.1.80", "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", "license": "MIT",
"workspaces": [ "workspaces": [
"e2e/*" "e2e/*"
@@ -1579,13 +1983,63 @@
"@napi-rs/canvas-win32-x64-msvc": "0.1.80" "@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", "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": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT", "license": "MIT",
"optional": true, "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": [ "os": [
"linux" "linux"
], ],
@@ -1593,6 +2047,117 @@
"node": ">= 10" "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": { "node_modules/@napi-rs/lzma-linux-x64-gnu": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", "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": { "node_modules/esbuild": {
"version": "0.28.1", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {

45
scripts/deploy-runtime.sh Normal file
View File

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

View File

@@ -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')
})
})