← back to Norma Platform
deploy hygiene (TK-10370): build-then-restart-then-health-gate reload + silence lockfile-root warning
f2f2df6086485779b8e308fe7aad0cacdeb05173 · 2026-08-08 08:30:14 -0700 · Steve Abrams
- scripts/reload.sh (npm run reload): builds FIRST (broken build never reaches
live), restarts pm2 norma-email, then health-gates :7400 and fails loud if it
doesn't answer — closes the TK-10293 gap where a stale .next after a dep
change crash-looped with no deploy-time signal.
- next.config.ts: outputFileTracingRoot=import.meta.dirname to pin the tracing
root and silence the stray-lockfile "inferred workspace root" warning.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M next.config.tsM package.jsonA scripts/reload.sh
Diff
commit f2f2df6086485779b8e308fe7aad0cacdeb05173
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Aug 8 08:30:14 2026 -0700
deploy hygiene (TK-10370): build-then-restart-then-health-gate reload + silence lockfile-root warning
- scripts/reload.sh (npm run reload): builds FIRST (broken build never reaches
live), restarts pm2 norma-email, then health-gates :7400 and fails loud if it
doesn't answer — closes the TK-10293 gap where a stale .next after a dep
change crash-looped with no deploy-time signal.
- next.config.ts: outputFileTracingRoot=import.meta.dirname to pin the tracing
root and silence the stray-lockfile "inferred workspace root" warning.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
next.config.ts | 5 +++++
package.json | 3 ++-
scripts/reload.sh | 43 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/next.config.ts b/next.config.ts
index 7cc5a16..a39050d 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,6 +1,11 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
+ // Pin the file-tracing root to THIS repo. A stray lockfile at
+ // ~/Projects/package-lock.json made Next infer the wrong workspace root and
+ // emit a "multiple lockfiles" warning at every start (TK-10370). This does
+ // not change build output — it just silences the ambiguity.
+ outputFileTracingRoot: import.meta.dirname,
allowedDevOrigins: ['http://45.61.58.125:7400', 'http://127.0.0.1:7400'],
typescript: {
ignoreBuildErrors: true,
diff --git a/package.json b/package.json
index 70d2b9c..e14eefb 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,8 @@
"test:write": "node tests/api-write-smoke.mjs",
"test:e2e": "playwright test",
"test:crud": "node tests/api-crud-lifecycle.mjs",
- "test:authz": "node tests/api-authz-matrix.mjs"
+ "test:authz": "node tests/api-authz-matrix.mjs",
+ "reload": "bash scripts/reload.sh"
},
"dependencies": {
"@codemirror/lang-html": "^6.4.11",
diff --git a/scripts/reload.sh b/scripts/reload.sh
new file mode 100755
index 0000000..dd819de
--- /dev/null
+++ b/scripts/reload.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# Canonical LOCAL reload for the Norma app (pm2 `norma-email`, :7400).
+#
+# Born from TK-10293: an outage where `npm audit fix` changed node_modules but
+# the on-disk `.next` build was never rebuilt, so `next start` crash-looped and
+# pm2 gave up — with NO deploy-time signal. This script closes that gap:
+#
+# 1. BUILD FIRST — a broken build fails here and NEVER reaches the live
+# process (the old build keeps serving until a good build exists).
+# 2. RESTART — only after a clean build.
+# 3. HEALTH-GATE — poll :7400 until it answers; FAIL LOUD if it doesn't, so
+# a bad reload can't masquerade as success.
+#
+# Usage: npm run reload (or: bash scripts/reload.sh)
+set -euo pipefail
+export PATH="/opt/homebrew/bin:/opt/homebrew/opt/postgresql@14/bin:$PATH"
+cd "$(dirname "${BASH_SOURCE[0]}")/.."
+
+APP="${NORMA_PM2_NAME:-norma-email}"
+PORT="${NORMA_PORT:-7400}"
+URL="http://127.0.0.1:${PORT}/"
+
+echo "[reload] 1/3 building (.next) — a broken build stops here, live app untouched…"
+npm run build
+
+echo "[reload] 2/3 restarting pm2 process '${APP}'…"
+pm2 restart "${APP}"
+
+echo "[reload] 3/3 health-gating ${URL} …"
+code="000"
+for i in $(seq 1 20); do
+ code="$(curl -s -o /dev/null -w '%{http_code}' --max-time 4 "${URL}" 2>/dev/null || echo 000)"
+ [ "${code}" != "000" ] && break
+ sleep 1
+done
+
+if [ "${code}" = "000" ]; then
+ echo "[reload] FATAL: ${APP} did not answer on :${PORT} after 20s — reload FAILED."
+ echo "[reload] Inspect: pm2 logs ${APP} --err --lines 40"
+ exit 1
+fi
+echo "[reload] OK — ${APP} healthy on :${PORT} (HTTP ${code})."
+pm2 save >/dev/null 2>&1 || true
← bc9cfde auto-data-snapshot: 2026-08-07T18:31:41 (2 data files) — pac
·
back to Norma Platform
·
Norma pre-deploy gate: run durable smoke battery before any cbfbf08 →