Skip to content

Commit a736857

Browse files
committed
Preserve legacy node renderer env parsing
1 parent 007b81f commit a736857

7 files changed

Lines changed: 48 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ
3030

3131
#### Added
3232

33-
- **[Pro]** **Built-in node renderer `/health` and `/ready` probe endpoints**: The node renderer can now register first-class liveness (`GET /health` -> `200` with a status-only body) and readiness (`GET /ready` -> `503` until the answering worker is online and has at least one server bundle compiled, then `200`) endpoints, replacing the hand-rolled `configureFastify` health-check recipe for the common case. The endpoints are off by default and enabled with the new `enableHealthEndpoints` config option (or `RENDERER_ENABLE_HEALTH_ENDPOINTS=true`, `TRUE`, `yes`, `YES`, or `1`); they are unauthenticated like `/info` but expose no runtime version or path details. Includes a new [Health and Readiness Endpoints](docs/oss/building-features/node-renderer/health-checks.md) docs page with working Kubernetes (`tcpSocket` + `exec` with `curl --http2-prior-knowledge` -- the h2c listener cannot be probed with HTTP/1.1 `httpGet`), ECS, and Docker Compose probe examples. The shared node-renderer truthy parser also accepts `1` for other boolean env vars that use it directly, including `RENDERER_SUPPORT_MODULES` and `REPLAY_SERVER_ASYNC_OPERATION_LOGS`. Closes [Issue 3880](https://github.com/shakacode/react_on_rails/issues/3880). [PR 3939](https://github.com/shakacode/react_on_rails/pull/3939) by [justin808](https://github.com/justin808).
33+
- **[Pro]** **Built-in node renderer `/health` and `/ready` probe endpoints**: The node renderer can now register first-class liveness (`GET /health` -> `200` with a status-only body) and readiness (`GET /ready` -> `503` until the answering worker is online and has at least one server bundle compiled, then `200`) endpoints, replacing the hand-rolled `configureFastify` health-check recipe for the common case. The endpoints are off by default and enabled with the new `enableHealthEndpoints` config option (or `RENDERER_ENABLE_HEALTH_ENDPOINTS=true`, `TRUE`, `yes`, `YES`, or `1`); they are unauthenticated like `/info` but expose no runtime version or path details. The `1` alias is scoped to `RENDERER_ENABLE_HEALTH_ENDPOINTS` so existing node-renderer boolean environment flags keep their previous parsing behavior. Includes a new [Health and Readiness Endpoints](docs/oss/building-features/node-renderer/health-checks.md) docs page with working Kubernetes (`tcpSocket` + `exec` with `curl --http2-prior-knowledge` -- the h2c listener cannot be probed with HTTP/1.1 `httpGet`), ECS, and Docker Compose probe examples. Closes [Issue 3880](https://github.com/shakacode/react_on_rails/issues/3880). [PR 3939](https://github.com/shakacode/react_on_rails/pull/3939) by [justin808](https://github.com/justin808).
3434

3535
#### Fixed
3636

docs/oss/building-features/node-renderer/health-checks.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ probes cannot carry it). Keep the renderer on `localhost` or private networking
2525
The endpoints are **off by default**. Enable them with the `enableHealthEndpoints` config option or the
2626
`RENDERER_ENABLE_HEALTH_ENDPOINTS` environment variable (`true`, `TRUE`, `yes`, `YES`, or `1`):
2727

28-
Those string values are parsed by the shared node-renderer `truthy()` helper, so the same accepted values also apply
29-
to other boolean environment variables that use that helper directly, such as `RENDERER_SUPPORT_MODULES` and
30-
`REPLAY_SERVER_ASYNC_OPERATION_LOGS`.
28+
The `1` alias is scoped to `RENDERER_ENABLE_HEALTH_ENDPOINTS`; other node-renderer boolean environment variables keep
29+
their existing parsing behavior.
3130

3231
```js
3332
// renderer/node-renderer.js

llms-full.txt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13282,9 +13282,8 @@ probes cannot carry it). Keep the renderer on `localhost` or private networking
1328213282
The endpoints are **off by default**. Enable them with the `enableHealthEndpoints` config option or the
1328313283
`RENDERER_ENABLE_HEALTH_ENDPOINTS` environment variable (`true`, `TRUE`, `yes`, `YES`, or `1`):
1328413284

13285-
Those string values are parsed by the shared node-renderer `truthy()` helper, so the same accepted values also apply
13286-
to other boolean environment variables that use that helper directly, such as `RENDERER_SUPPORT_MODULES` and
13287-
`REPLAY_SERVER_ASYNC_OPERATION_LOGS`.
13285+
The `1` alias is scoped to `RENDERER_ENABLE_HEALTH_ENDPOINTS`; other node-renderer boolean environment variables keep
13286+
their existing parsing behavior.
1328813287

1328913288
```js
1329013289
// renderer/node-renderer.js
@@ -27270,7 +27269,7 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ
2727027269

2727127270
#### Added
2727227271

27273-
- **[Pro]** **Built-in node renderer `/health` and `/ready` probe endpoints**: The node renderer can now register first-class liveness (`GET /health` -> `200` with a status-only body) and readiness (`GET /ready` -> `503` until the answering worker is online and has at least one server bundle compiled, then `200`) endpoints, replacing the hand-rolled `configureFastify` health-check recipe for the common case. The endpoints are off by default and enabled with the new `enableHealthEndpoints` config option (or `RENDERER_ENABLE_HEALTH_ENDPOINTS=true`, `TRUE`, `yes`, `YES`, or `1`); they are unauthenticated like `/info` but expose no runtime version or path details. Includes a new [Health and Readiness Endpoints](docs/oss/building-features/node-renderer/health-checks.md) docs page with working Kubernetes (`tcpSocket` + `exec` with `curl --http2-prior-knowledge` -- the h2c listener cannot be probed with HTTP/1.1 `httpGet`), ECS, and Docker Compose probe examples. The shared node-renderer truthy parser also accepts `1` for other boolean env vars that use it directly, including `RENDERER_SUPPORT_MODULES` and `REPLAY_SERVER_ASYNC_OPERATION_LOGS`. Closes [Issue 3880](https://github.com/shakacode/react_on_rails/issues/3880). [PR 3939](https://github.com/shakacode/react_on_rails/pull/3939) by [justin808](https://github.com/justin808).
27272+
- **[Pro]** **Built-in node renderer `/health` and `/ready` probe endpoints**: The node renderer can now register first-class liveness (`GET /health` -> `200` with a status-only body) and readiness (`GET /ready` -> `503` until the answering worker is online and has at least one server bundle compiled, then `200`) endpoints, replacing the hand-rolled `configureFastify` health-check recipe for the common case. The endpoints are off by default and enabled with the new `enableHealthEndpoints` config option (or `RENDERER_ENABLE_HEALTH_ENDPOINTS=true`, `TRUE`, `yes`, `YES`, or `1`); they are unauthenticated like `/info` but expose no runtime version or path details. The `1` alias is scoped to `RENDERER_ENABLE_HEALTH_ENDPOINTS` so existing node-renderer boolean environment flags keep their previous parsing behavior. Includes a new [Health and Readiness Endpoints](docs/oss/building-features/node-renderer/health-checks.md) docs page with working Kubernetes (`tcpSocket` + `exec` with `curl --http2-prior-knowledge` -- the h2c listener cannot be probed with HTTP/1.1 `httpGet`), ECS, and Docker Compose probe examples. Closes [Issue 3880](https://github.com/shakacode/react_on_rails/issues/3880). [PR 3939](https://github.com/shakacode/react_on_rails/pull/3939) by [justin808](https://github.com/justin808).
2727427273

2727527274
#### Fixed
2727627275

packages/react-on-rails-pro-node-renderer/src/shared/configBuilder.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ function defaultReplayServerAsyncOperationLogs() {
216216
return env.NODE_ENV?.toLowerCase() === 'development';
217217
}
218218

219+
function truthyHealthEndpointFlag(value: unknown) {
220+
return value === '1' || truthy(value);
221+
}
222+
219223
const defaultConfig: Config = {
220224
// Use env port if we run on Heroku
221225
port: Number(env.RENDERER_PORT) || DEFAULT_PORT,
@@ -268,7 +272,7 @@ const defaultConfig: Config = {
268272
maxVMPoolSize: (env.MAX_VM_POOL_SIZE && parseInt(env.MAX_VM_POOL_SIZE, 10)) || 2,
269273

270274
// Built-in /health and /ready probe endpoints are opt-in.
271-
enableHealthEndpoints: truthy(env.RENDERER_ENABLE_HEALTH_ENDPOINTS),
275+
enableHealthEndpoints: truthyHealthEndpointFlag(env.RENDERER_ENABLE_HEALTH_ENDPOINTS),
272276
};
273277

274278
function envValuesUsed() {
@@ -410,7 +414,7 @@ export function buildConfig(providedUserConfig?: Partial<Config>): Config {
410414
password: env.RENDERER_PASSWORD,
411415
// Re-evaluate env-derived defaults at build time in case env vars are set post-import.
412416
replayServerAsyncOperationLogs: defaultReplayServerAsyncOperationLogs(),
413-
enableHealthEndpoints: truthy(env.RENDERER_ENABLE_HEALTH_ENDPOINTS),
417+
enableHealthEndpoints: truthyHealthEndpointFlag(env.RENDERER_ENABLE_HEALTH_ENDPOINTS),
414418
};
415419
config = { ...runtimeDefaultConfig, ...userConfig };
416420
if (explicitUndefinedPassword) {
@@ -441,7 +445,7 @@ export function buildConfig(providedUserConfig?: Partial<Config>): Config {
441445

442446
config.supportModules = truthy(config.supportModules);
443447
// Coerce in case a user config passes an env-derived string (e.g. "true").
444-
config.enableHealthEndpoints = truthy(config.enableHealthEndpoints);
448+
config.enableHealthEndpoints = truthyHealthEndpointFlag(config.enableHealthEndpoints);
445449

446450
if (config.maxVMPoolSize <= 0 || !Number.isInteger(config.maxVMPoolSize)) {
447451
throw new Error('maxVMPoolSize must be a positive integer');

packages/react-on-rails-pro-node-renderer/src/shared/truthy.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,5 @@
1414
*/
1515

1616
export default function truthy(value: unknown) {
17-
return (
18-
value === true ||
19-
value === '1' ||
20-
value === 'YES' ||
21-
value === 'TRUE' ||
22-
value === 'yes' ||
23-
value === 'true'
24-
);
17+
return value === true || value === 'YES' || value === 'TRUE' || value === 'yes' || value === 'true';
2518
}

packages/react-on-rails-pro-node-renderer/src/worker.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ export default function run(config: Partial<Config>) {
718718
// Safe from a rate-limiting perspective (CodeQL js/missing-rate-limiting):
719719
// this is an internal renderer service not exposed to the internet, returns
720720
// a static status string, and exposes no sensitive runtime data.
721+
// codeql[js/missing-rate-limiting]
721722
// lgtm[js/missing-rate-limiting]
722723
app.get('/health', (_req, res) => {
723724
res.send({ status: 'ok' });
@@ -731,6 +732,7 @@ export default function run(config: Partial<Config>) {
731732
// across workers, so a probe checks one worker per request.
732733
// Safe from a rate-limiting perspective (CodeQL js/missing-rate-limiting):
733734
// same rationale as /health; this returns only a static readiness status.
735+
// codeql[js/missing-rate-limiting]
734736
// lgtm[js/missing-rate-limiting]
735737
app.get('/ready', (_req, res) => {
736738
if (hasAnyVMContext()) {

packages/react-on-rails-pro-node-renderer/tests/configBuilder.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ describe('configBuilder', () => {
2121
'RENDERER_PASSWORD',
2222
'RAILS_ENV',
2323
'REPLAY_SERVER_ASYNC_OPERATION_LOGS',
24+
'RENDERER_ENABLE_HEALTH_ENDPOINTS',
25+
'RENDERER_SUPPORT_MODULES',
2426
'RENDERER_WORKERS_COUNT',
2527
] as const;
2628
const savedEnvValues = Object.fromEntries(envVarsToRestore.map((key) => [key, process.env[key]]));
@@ -102,6 +104,36 @@ describe('configBuilder', () => {
102104
expect(envValues.RENDERER_PASSWORD).toBe(false);
103105
});
104106

107+
it('keeps shared boolean env parsing backward-compatible for RENDERER_SUPPORT_MODULES=1', () => {
108+
process.env.NODE_ENV = 'test';
109+
process.env.RENDERER_SUPPORT_MODULES = '1';
110+
111+
const { buildConfig } = loadConfigBuilderWithMockedLogger();
112+
const config = buildConfig();
113+
114+
expect(config.supportModules).toBe(false);
115+
});
116+
117+
it('keeps shared boolean env parsing backward-compatible for REPLAY_SERVER_ASYNC_OPERATION_LOGS=1', () => {
118+
process.env.NODE_ENV = 'test';
119+
process.env.REPLAY_SERVER_ASYNC_OPERATION_LOGS = '1';
120+
121+
const { buildConfig } = loadConfigBuilderWithMockedLogger();
122+
const config = buildConfig();
123+
124+
expect(config.replayServerAsyncOperationLogs).toBe(false);
125+
});
126+
127+
it('accepts RENDERER_ENABLE_HEALTH_ENDPOINTS=1 without changing other boolean env flags', () => {
128+
process.env.NODE_ENV = 'test';
129+
process.env.RENDERER_ENABLE_HEALTH_ENDPOINTS = '1';
130+
131+
const { buildConfig } = loadConfigBuilderWithMockedLogger();
132+
const config = buildConfig();
133+
134+
expect(config.enableHealthEndpoints).toBe(true);
135+
});
136+
105137
it('masks module-load password defaults in sanitized logs', () => {
106138
process.env.RENDERER_PASSWORD = 'env-password';
107139
const { buildConfig, logSanitizedConfig, info } = loadConfigBuilderWithMockedLogger();

0 commit comments

Comments
 (0)