Skip to content

fix: strip NODE_ENV from npm subprocess so devDependencies always install#25649

Open
ercan2158 wants to merge 2 commits into
NousResearch:mainfrom
ercan2158:fix/npm-install-node-env
Open

fix: strip NODE_ENV from npm subprocess so devDependencies always install#25649
ercan2158 wants to merge 2 commits into
NousResearch:mainfrom
ercan2158:fix/npm-install-node-env

Conversation

@ercan2158

@ercan2158 ercan2158 commented May 14, 2026

Copy link
Copy Markdown

Problem

When NODE_ENV=production is set in the environment, npm install and npm ci skip devDependencies. The TUI and web UI builds rely on esbuild/typescript/vite (devDependencies), so the build fails silently during hermes update with sh: 1: tsc: not found.

The bundled TUI launcher sets NODE_ENV=production on its node subprocess (_launch_tui), so the value is inherited by every npm install spawned by hermes update, hermes plugins, shells, and slash commands launched under the TUI.

Fix

  • _make_tui_argv (TUI install): copy os.environ, set CI=1, and pop("NODE_ENV", None) before running npm install — devDependencies install regardless of what the parent process exported.
  • _run_npm_install_deterministic: accept optional env: dict[str, str] | None parameter; defensively strip NODE_ENV from a copy of either the supplied env or os.environ, so both npm ci and the fallback npm install always install devDependencies even when the caller forgets.

Both call sites now use the same removal pattern (dict.pop) for consistency.

npm run build paths are intentionally left alone — once devDependencies are installed, the build itself can (and should) run with whatever NODE_ENV the caller set, so production builds stay minified and React-prod-mode.

Tests

Three regression tests in tests/hermes_cli/test_web_ui_build.py::TestNpmInstallStripsNodeEnv:

  1. NODE_ENV=production in os.environ is stripped before subprocess.run.
  2. Explicit env= argument is honored, NODE_ENV is stripped from a copy (caller's dict is not mutated), other keys are preserved.
  3. When npm ci fails and falls back to npm install, both subprocess calls have NODE_ENV stripped.

Verification

$ NODE_ENV=production rm -rf ui-tui/node_modules
$ hermes update   # before: silently builds stale dist; after: TUI deps install, build succeeds

Related: #17906 (same root cause, takes the per-call-site approach), #22187, #18682.

Closes #17906

…tall

When NODE_ENV=production is set in the environment, npm install/ci
skips devDependencies. This breaks the TUI and web UI builds because
esbuild (a devDependency) never gets installed.

- TUI npm install: override NODE_ENV to empty string
- _run_npm_install_deterministic: pop NODE_ENV from subprocess env
  so both npm ci and fallback npm install install devDependencies
@ercan2158 ercan2158 requested a review from a team May 14, 2026 11:11
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels May 14, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #17906 (same root cause: NODE_ENV=production leaking into npm subprocess skips devDependencies), #22187, and #18682 (duplicate of #17906). This PR takes a slightly different approach by stripping NODE_ENV entirely in _run_npm_install_deterministic rather than forcing NODE_ENV=development in specific build functions. Also bumps esbuild.

Review feedback on NousResearch#25649:

- Replace `NODE_ENV: ""` with `sub_env.pop("NODE_ENV", None)` in
  `_make_tui_argv` so both npm subprocess sites use the same removal
  pattern (matches `_run_npm_install_deterministic`).
- Tighten `env` parameter annotation to `dict[str, str] | None`.
- Add regression tests covering: stripping NODE_ENV from inherited
  os.environ, from an explicit env arg (without mutating the caller's
  dict), and from the `npm install` fallback after `npm ci` fails.
- Revert esbuild ~0.27.0 → ^0.27.7 bump — unrelated to NODE_ENV fix;
  belongs in a separate PR if still wanted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use .github/PULL_REQUEST_TEMPLATE.md and resolve copilot comments

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a class of frontend/TUI build failures caused by inheriting NODE_ENV=production into npm install/npm ci, which makes npm omit devDependencies (breaking builds that require tooling like TypeScript/Vite/esbuild). The change makes npm dependency installs robust regardless of the parent process environment (notably when launched under the Hermes TUI).

Changes:

  • Strip NODE_ENV from the environment used for the TUI npm install subprocess so devDependencies always install.
  • Extend _run_npm_install_deterministic() with an optional env parameter and defensively strip NODE_ENV from a copy of the effective environment for both npm ci and the fallback npm install.
  • Add regression tests asserting NODE_ENV is stripped (including the npm cinpm install fallback path) and that caller-provided env dicts are not mutated.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
hermes_cli/main.py Strips NODE_ENV for npm dependency installs in both the TUI install path and the deterministic npm install helper.
tests/hermes_cli/test_web_ui_build.py Adds regression coverage verifying NODE_ENV stripping behavior and fallback behavior for deterministic npm installs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread hermes_cli/main.py
Comment on lines +1067 to 1072
# Strip NODE_ENV so devDependencies (esbuild, typescript, …) install
# even when inherited from a production-mode parent (e.g. the bundled
# TUI launcher sets NODE_ENV=production on its node subprocess).
sub_env = {**os.environ, "CI": "1"}
sub_env.pop("NODE_ENV", None)
result = subprocess.run(
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants