Der Deploy starb mit "Ineffective mark-compacts near heap limit". Nodes Standard-Heap richtet sich nach dem Arbeitsspeicher und liegt auf dem Container bei rund 1,5 GB -- zu wenig fuer next build. update-app.sh setzt jetzt --max-old-space-size=2048. Ich hatte zuerst ESLint im Build verdaechtigt (mein ignoreDuringBuilds: false aus v1.117.4) und die Aenderung zurueckgenommen. Eine Messung widerlegte das: Spitzenspeicher mit ESLint 2.590 MB, ohne 2.595 MB -- kein Unterschied. Die Ruecknahme ist damit hinfaellig, ESLint bleibt im Build. Der Build braucht rund 2,6 GB. Bei 3 GB Container-RAM bleibt das knapp; NODE_OPTIONS ist die Sofortmassnahme, mehr RAM fuer LXC 202 die eigentliche Loesung. Steht als Block J in TODO.md, zusammen mit der Alternative, den Build auf den Spark zu verlagern. v1.119.3 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
133 lines
5.6 KiB
Bash
Executable File
133 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Deploy script for the production host (WebApp-Testing-live).
|
|
#
|
|
# The app runs there as a PM2 process ("my-weekly-todo") from /root/My-Weekly-ToDo-List.
|
|
# It is NOT run under Docker — deploy.sh and docker-compose.yml in this repo describe an
|
|
# older Portainer setup that is not what serves todo.martin-bierschenk.de.
|
|
#
|
|
# This is the single update path. It is used both ways:
|
|
# - by hand: /root/My-Weekly-ToDo-List/scripts/update-app.sh [--force]
|
|
# - at boot: my-weekly-todo-update.service, before pm2-root.service starts the app
|
|
#
|
|
# There used to be a second, near-identical boot-update.sh. The two drifted (the npm ci fix
|
|
# from 1.114.1 only ever landed in this one), so boot-update.sh is now a shim that calls
|
|
# this script. Keep it that way: one script, one set of fixes.
|
|
#
|
|
# The whole body is wrapped in a function so bash parses it in one go: this script lives
|
|
# inside the very checkout it updates, and `git pull` rewriting it mid-run would otherwise
|
|
# make bash resume at a stale file offset.
|
|
|
|
main() {
|
|
# pipefail matters because everything below is written through a `tee` redirect: without
|
|
# it the shell sees tee's exit status (always 0) and a failed build looks like success.
|
|
set -euo pipefail
|
|
|
|
local APP_DIR="/root/My-Weekly-ToDo-List"
|
|
local PM2_APP="my-weekly-todo"
|
|
local LOG_FILE="/var/log/my-weekly-todo-update.log"
|
|
local LOCK_FILE="/var/lock/my-weekly-todo-update.lock"
|
|
local FORCE="${1:-}"
|
|
|
|
# One redirect for the whole run: goes to the log and, when run by hand, to the console.
|
|
exec > >(tee -a "$LOG_FILE") 2>&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
|
|
|
|
# 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.
|
|
export GIT_TERMINAL_PROMPT=0
|
|
|
|
# Never let a boot run and a manual run install into the same tree at once: a half-written
|
|
# node_modules is what leaves npm unable to clean up afterwards (ENOTEMPTY on rmdir).
|
|
exec 9>"$LOCK_FILE"
|
|
if ! flock -n 9; then
|
|
log "Another update is already running — nothing to do."
|
|
return 0
|
|
fi
|
|
|
|
cd "$APP_DIR"
|
|
|
|
log "=== Update started ==="
|
|
|
|
log "--- Ensuring main branch ---"
|
|
git checkout main
|
|
|
|
log "--- Fetching from git ---"
|
|
git fetch origin main
|
|
|
|
local LOCAL REMOTE
|
|
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.
|
|
local NEEDS_BUILD=0
|
|
[ "$LOCAL" != "$REMOTE" ] && NEEDS_BUILD=1
|
|
[ ! -f "$APP_DIR/.next/standalone/server.js" ] && 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 "=== Update finished (no changes) ==="
|
|
return 0
|
|
fi
|
|
|
|
if [ "$LOCAL" != "$REMOTE" ]; then
|
|
log "--- Pulling latest changes ($LOCAL -> $REMOTE) ---"
|
|
git pull origin main
|
|
|
|
# npm ci, not npm install: it installs exactly what package-lock.json specifies and
|
|
# never rewrites it. npm install rewrites the lockfile, which leaves the working tree
|
|
# dirty and makes the *next* git pull abort. npm ci clears node_modules itself; if
|
|
# that ever fails with ENOTEMPTY, remove the directory by hand and rerun.
|
|
log "--- Installing dependencies ---"
|
|
npm ci
|
|
fi
|
|
|
|
log "--- Updating database schema (safe migrate) ---"
|
|
npx prisma migrate deploy || {
|
|
log "migrate deploy failed, falling back to db push..."
|
|
npx prisma db push --accept-data-loss
|
|
}
|
|
|
|
log "--- Regenerating Prisma client ---"
|
|
npx prisma generate
|
|
|
|
# Der Standard-Heap von Node richtet sich nach dem Arbeitsspeicher und landet auf
|
|
# diesem Container bei rund 1,5 GB. Das reichte fuer `next build` nicht mehr, der Build
|
|
# starb mit "JavaScript heap out of memory". 2 GB lassen genug Luft fuer den Rest.
|
|
log "--- Building application ---"
|
|
NODE_OPTIONS="--max-old-space-size=2048" npm run build
|
|
|
|
# next.config.js sets output: 'standalone', and Next.js does NOT copy public/ or
|
|
# .next/static/ into .next/standalone — that is a documented manual step. Skipping it
|
|
# 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
|
|
# server-rendered shell ("Loading session...") while the API still answers 200.
|
|
log "--- Copying static assets into standalone output ---"
|
|
cp -r public .next/standalone/
|
|
mkdir -p .next/standalone/.next/static
|
|
cp -r .next/static .next/standalone/.next/
|
|
cp .env .next/standalone/ 2>/dev/null || true
|
|
|
|
# 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
|
|
# one started from this unit would race the real service.
|
|
local PM2_PID_FILE="${HOME:-/root}/.pm2/pm2.pid"
|
|
if [ -f "$PM2_PID_FILE" ] && kill -0 "$(cat "$PM2_PID_FILE")" 2>/dev/null; then
|
|
log "--- Restarting PM2 process ---"
|
|
pm2 restart "$PM2_APP" --update-env
|
|
pm2 save
|
|
else
|
|
log "PM2 daemon is not running — leaving the start to pm2-root.service."
|
|
fi
|
|
|
|
log "=== Update Complete ==="
|
|
log "Deployed version: $(node -p "require('$APP_DIR/package.json').version")"
|
|
}
|
|
|
|
main "$@"
|