diff --git a/CHANGELOG.md b/CHANGELOG.md index 51995c0..788a48c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project are documented in this file. +## [1.115.1] - 2026-09-03 + +### Fixed +- `scripts/boot-update.sh` (the "update on reboot" unit): every command was piped into `tee`, + so `set -e` only ever saw tee's exit status. A failed `npm install` or `npm run build` did + not stop the script and did not fail the systemd unit — it logged "Boot update finished" + and left a half-built tree. Now uses `set -euo pipefail`, logs through a single `exec` + redirect, and traps ERR. +- `scripts/boot-update.sh`: switched from `npm install` to `npm ci`. Same fix update-app.sh + got in 1.114.1 — `npm install` rewrites package-lock.json, leaving the tree dirty so the + *next* `git pull` aborts. +- `scripts/boot-update.sh`: body wrapped in a function so bash parses it in one go. The + script lives inside the checkout it pulls, so `git pull` rewriting it mid-run made bash + resume at a stale file offset. +- `scripts/boot-update.sh`: set `GIT_TERMINAL_PROMPT=0` so a git credential prompt fails + fast instead of hanging the unit at boot, where there is no tty. +- `scripts/my-weekly-todo-update.service`: raised `TimeoutStartSec` from 300 to 1800. + `npm ci` + `prisma generate` + `next build` takes far longer than 5 minutes in the + container; systemd SIGTERMed the oneshot unit mid-install, which left a `node_modules` + tree npm could no longer remove (`ENOTEMPTY: rmdir node_modules/date-fns/isEqual`). +- `package-lock.json`: version resynced with `package.json` (missed in the 1.115.0 commit). + ## [1.115.0] - 2026-09-03 ### Added diff --git a/package-lock.json b/package-lock.json index 03d9eb8..22964bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "my-weekly-todo-list", - "version": "1.114.2", + "version": "1.115.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "my-weekly-todo-list", - "version": "1.114.2", + "version": "1.115.1", "license": "MIT", "dependencies": { "@auth/prisma-adapter": "^2.11.1", diff --git a/package.json b/package.json index 05b21b4..18a42bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.115.0", + "version": "1.115.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/boot-update.sh b/scripts/boot-update.sh index 1d067bd..91a54cc 100755 --- a/scripts/boot-update.sh +++ b/scripts/boot-update.sh @@ -1,66 +1,92 @@ #!/bin/bash -# Boot Update Script: Automatically pull latest main, build, and prepare for PM2 -# This script runs on LXC container boot BEFORE PM2 starts. -# It ensures the app is always up-to-date with the main branch. +# +# Boot Update Script: pull latest main, install, build, and prepare for PM2. +# Runs on LXC container boot BEFORE PM2 starts (my-weekly-todo-update.service). +# +# 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. -set -e +main() { + # pipefail matters here: every command used to be piped into `tee`, so the shell saw + # tee's exit status (always 0). A failed npm install or build did not stop the script + # and did not fail the unit — it just logged "finished" and left a half-built tree. + set -euo pipefail -APP_DIR="/root/My-Weekly-ToDo-List" -LOG_FILE="/var/log/app-boot-update.log" + local APP_DIR="/root/My-Weekly-ToDo-List" + local LOG_FILE="/var/log/app-boot-update.log" -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" + # Send everything to the log once, instead of piping each command through tee. + exec > >(tee -a "$LOG_FILE") 2>&1 + + log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; } + + trap 'log "!!! Boot update FAILED at line $LINENO (exit $?) — not restarting the app"' 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 + + log "=== Boot update started ===" + + cd "$APP_DIR" + + log "Ensuring main branch..." + git checkout main + + log "Fetching from origin..." + git fetch origin main + + local LOCAL REMOTE NEXT_DIR + LOCAL=$(git rev-parse HEAD) + REMOTE=$(git rev-parse origin/main) + NEXT_DIR="$APP_DIR/.next" + + if [ "$LOCAL" = "$REMOTE" ] && [ -d "$NEXT_DIR" ]; then + log "Already up-to-date and .next exists. No rebuild needed." + log "=== Boot update finished (no changes) ===" + return 0 + fi + + if [ "$LOCAL" != "$REMOTE" ]; then + log "Changes detected. Updating from $LOCAL to $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 also clears node_modules + # itself; if that ever fails with ENOTEMPTY, delete the directory by hand and rerun. + log "Installing dependencies..." + npm ci + fi + + if [ ! -d "$NEXT_DIR" ] || [ "$LOCAL" != "$REMOTE" ]; then + 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 + + log "Building application..." + 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 "Loading session..." while the API still answers 200. + log "Preparing standalone directory (static assets and env)..." + 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 + fi + + log "Deployed version: $(node -p "require('$APP_DIR/package.json').version")" + log "=== Boot update finished (verified/rebuilt) ===" } -log "=== Boot update started ===" - -cd "$APP_DIR" - -# Check and ensure we are on main branch -log "Ensuring main branch..." -git checkout main 2>&1 | tee -a "$LOG_FILE" - -# Check if there are remote changes -log "Fetching from origin..." -git fetch origin main 2>&1 | tee -a "$LOG_FILE" - -LOCAL=$(git rev-parse HEAD) -REMOTE=$(git rev-parse origin/main) -NEXT_DIR="$APP_DIR/.next" - -if [ "$LOCAL" = "$REMOTE" ] && [ -d "$NEXT_DIR" ]; then - log "Already up-to-date and .next directory exists. No rebuild needed." - log "=== Boot update finished (no changes) ===" - exit 0 -fi - -if [ "$LOCAL" != "$REMOTE" ]; then - log "Changes detected. Updating from $LOCAL to "$REMOTE"" - log "Pulling latest changes..." - git pull origin main 2>&1 | tee -a "$LOG_FILE" - - log "Installing dependencies..." - npm install 2>&1 | tee -a "$LOG_FILE" -fi - -if [ ! -d "$NEXT_DIR" ] || [ "$LOCAL" != "$REMOTE" ]; then - log "Updating database schema (Safe Migrate)..." - npx prisma migrate deploy 2>&1 | tee -a "$LOG_FILE" || { - log "migrate deploy failed, falling back to db push..." - npx prisma db push --accept-data-loss 2>&1 | tee -a "$LOG_FILE" - } - - log "Regenerating Prisma client..." - npx prisma generate 2>&1 | tee -a "$LOG_FILE" - - log "Building application..." - npm run build 2>&1 | tee -a "$LOG_FILE" - - log "Preparing standalone directory (copying static assets and env)..." - cp -r public .next/standalone/ 2>&1 | tee -a "$LOG_FILE" - mkdir -p .next/standalone/.next/static 2>&1 | tee -a "$LOG_FILE" - cp -r .next/static .next/standalone/.next/ 2>&1 | tee -a "$LOG_FILE" - cp .env .next/standalone/ 2>/dev/null || true 2>&1 | tee -a "$LOG_FILE" -fi - -log "=== Boot update finished (verified/rebuilt) ===" +main "$@" diff --git a/scripts/my-weekly-todo-update.service b/scripts/my-weekly-todo-update.service index 941ba30..19dfcb0 100644 --- a/scripts/my-weekly-todo-update.service +++ b/scripts/my-weekly-todo-update.service @@ -12,7 +12,10 @@ Environment=HOME=/root Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin StandardOutput=journal+console StandardError=journal+console -TimeoutStartSec=300 +# npm ci + prisma generate + next build in this container takes well over the old 300s. +# systemd SIGTERMs a Type=oneshot unit at the timeout, which used to kill npm mid-install +# and leave a node_modules tree that npm could no longer clean up (ENOTEMPTY on rmdir). +TimeoutStartSec=1800 [Install] WantedBy=multi-user.target