From 95496fa0ef22642bab8d055248332535ce1cbdc7 Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 01:12:34 -0500 Subject: [PATCH] fix(healthz): read version from package.json (not npm_package_version) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `process.env.npm_package_version` is only set when the process is launched via an npm script. The repo Dockerfile (line 80) `CMD ["node", "server.js"]` — no npm wrapper — so in production deployments `/healthz` always returned `version: "unknown"`. Operators trying to verify a rolling deploy ("is the new pod at v1.2.3 yet?") got no signal. Read `version` from `package.json` once at module load. The lookup is wrapped in try/catch so a broken install can't take /healthz down — the endpoint is operationally critical and must come up even when the manifest is missing. New test asserts `body.version === require('../../package.json').version` and that the fallback `'unknown'` string never appears on a healthy install. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/controllers/healthcontroller.js | 19 ++++++++++++++++++- tests/api/healthz.test.js | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/controllers/healthcontroller.js b/app/controllers/healthcontroller.js index e1c2903..1068351 100644 --- a/app/controllers/healthcontroller.js +++ b/app/controllers/healthcontroller.js @@ -2,8 +2,25 @@ // Copyright 2026 Aaron K. Clark "use strict"; +const path = require('node:path'); const db = require('../config/db.config.js'); +// Read the version from package.json once at module load. `npm_package_version` +// is only set when the process is started via an npm script — production +// deployments that launch `node server.js` directly (including the Dockerfile +// in this repo, CMD ["node", "server.js"]) get `undefined` there and the +// /healthz response degrades to `version: "unknown"`. Reading the manifest +// gives operators a real version string regardless of how the process started. +let PACKAGE_VERSION = 'unknown'; +try { + PACKAGE_VERSION = require(path.resolve(__dirname, '../../package.json')).version + || PACKAGE_VERSION; +} catch (_err) { + // package.json missing or malformed — keep the 'unknown' fallback rather + // than crashing module load. /healthz is operationally important and + // must come up even on a broken install. +} + /** * GET /healthz * @@ -83,7 +100,7 @@ exports.healthz = async (req, res) => { status: dbOk ? 'ok' : 'degraded', db: dbOk ? 'ok' : 'down', uptime_s: Math.round(process.uptime()), - version: process.env.npm_package_version || 'unknown', + version: PACKAGE_VERSION, elapsed_ms: Math.round(elapsedMs * 100) / 100, migration, }; diff --git a/tests/api/healthz.test.js b/tests/api/healthz.test.js index dd9d60f..82eef21 100644 --- a/tests/api/healthz.test.js +++ b/tests/api/healthz.test.js @@ -78,4 +78,18 @@ describe('GET /healthz', () => { const m = res.body.migration; expect(m === null || typeof m === 'string').toBe(true); }); + + test('`version` is read from package.json, not npm_package_version', async () => { + // Production Dockerfile launches `node server.js` directly — no + // npm wrapper, so process.env.npm_package_version is undefined. + // The controller must read package.json directly so /healthz + // reports a real version regardless of how the process started. + const pkg = require('../../package.json'); + const res = await request(app).get('/healthz'); + expect(res.body.version).toBe(pkg.version); + // Defensive: the version must NEVER fall back to 'unknown' when + // package.json is readable — that fallback path is only for the + // exotic case of a broken install. + expect(res.body.version).not.toBe('unknown'); + }); });