Skip to content

fix(update): provision a managed Node runtime when system npm fails engines.npm - #76464

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-04379839
Aug 1, 2026
Merged

teknium1 merged 1 commit into
mainfrom
hermes/hermes-04379839

Conversation

@teknium1

@teknium1 teknium1 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

hermes update now recovers from EBADENGINE on system-Node installs by provisioning Hermes' own managed Node runtime instead of dead-ending with a manual-fix hint.

Root cause of the outage: f88ed6c set engines.npm: ">=12.0.0" (correct — npm 11.10-11.x parses min-release-age but ignores min-release-age-exclude, which would hard-fail the excluded fresh packages), but no shipping Node bundles npm 12 yet (Node 24.16 ships 11.13). With engine-strict=true, every user on system Node fails npm ci/npm install, and the existing recovery only upgrades a Hermes-managed npm — system npm got a hint and a mixed-state install (updated code, stale Node deps, no TUI/web/desktop rebuild). This is what hit emozilla (#75598 is the same incident shape).

Changes

  • hermes_constants.py: new bootstrap_hermes_managed_node() — cross-platform managed-Node provisioning (POSIX: _nb_install_bundled_node from node-bootstrap.sh, the same pinned-nodejs.org path install.sh uses; Windows: the existing portable-zip download from the heal path). Reuses a healthy managed tree when present.
  • scripts/lib/node-bootstrap.sh: HERMES_NODE_SKIP_LINKS=1 skips the ~/.local/bin node/npm/npx symlinks so the recovery-provisioned tree never shadows the user's own toolchain on PATH.
  • hermes_cli/npm_engine.py: maybe_repair_npm_engine() now returns the npm path to retry with — the same npm after an in-place managed upgrade, or a freshly provisioned managed npm when the failing npm is foreign (system/nvm/brew/Nix — still never modified). Node-only mismatches on a foreign npm are now recoverable too (the managed tree ships a supported Node); the fresh tree's bundled npm is upgraded into engines.npm range so the retry can't fail identically.
  • hermes_cli/main.py: both call sites (_run_npm_install_deterministic, TUI install) retry with the returned npm and put the managed tree first on PATH so npm lifecycle scripts resolve the managed node.
  • tests/hermes_cli/test_npm_engine.py: updated contract + new coverage for foreign-npm provisioning, failed-bootstrap fallback to the manual hint, and both Node-only mismatch shapes.

Validation

Before After
System npm 11.13 + engines.npm: >=12 EBADENGINE, mixed-state install managed Node provisioned, npm 12.0.2, retry succeeds
System npm modified? never never (byte-identical in E2E)
~/.local/bin links n/a not re-pointed (skip-links)
Healthy managed tree present in-place upgrade unchanged; reused in 0.05s

E2E with a real download into a temp HERMES_HOME against Jeff's verbatim error output: provisioned node v22.23.2, upgraded bundled npm 10.9.4 → 12.0.2, fake system npm untouched. Targeted tests: 20/20 test_npm_engine.py, 68/68 test_cmd_update.py + test_hermes_constants.py, 36/36 web-ui/desktop/gui suites.

Infographic

EBADENGINE recovery: provision, don't modify

…ngines.npm

The npm 12 requirement (f88ed6c) strands every system-Node install:
no shipping Node bundles npm >=12, engine-strict makes EBADENGINE fatal,
and the recovery in npm_engine.py refuses to touch a system npm — so
'hermes update' leaves the install in a mixed state (updated code, stale
Node deps, no TUI/web/desktop rebuild) with only a manual-fix hint.

Instead of modifying the user's toolchain (still never done), the
EBADENGINE recovery now provisions Hermes' own managed Node tree under
$HERMES_HOME/node — the same pinned-nodejs.org path install.sh and
install.ps1 use — upgrades THAT npm into the required range, and hands
the caller the managed npm for its single retry.

- hermes_constants.bootstrap_hermes_managed_node(): cross-platform
  provisioning (POSIX via node-bootstrap.sh _nb_install_bundled_node,
  Windows via the existing portable-zip download); reuses a healthy tree.
- node-bootstrap.sh: HERMES_NODE_SKIP_LINKS=1 skips the ~/.local/bin
  node/npm/npx symlinks so the private tree never shadows the user's
  own toolchain on PATH.
- maybe_repair_npm_engine() now returns the npm path to retry with
  (managed-in-place upgrade or freshly provisioned runtime); both call
  sites retry with the returned path and put the managed tree first on
  PATH so npm lifecycle scripts resolve the managed node.
- Node-only mismatches on a foreign npm are now also recoverable (the
  managed tree ships a supported Node); on a managed npm they still
  correctly decline.

E2E (real download, temp HERMES_HOME): provisioned node v22.23.2,
upgraded bundled npm 10.9.4 -> 12.0.2, system npm byte-identical after,
no ~/.local/bin links re-pointed, healthy-tree reuse in 0.05s.
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/cli CLI entry point, hermes_cli/, setup wizard area/install-update Installer, updater, packaging, wheels, doctor sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 38ec51b

ℹ️ Info

Desktop E2E visual evidence · View test artifacts · View job

3 visual diffs.

inline evidence upload failed.

Failed to upload diff-1508682a2ae8-boot-ready-diff.png with gh image (exit code 1): Error uploading /home/runner/work/_temp/e2e-evidence/diff-1508682a2ae8-boot-ready-diff.png: step 0 (get upload token): uploadToken not found on repo page — do you have write access to NousResearch/hermes-agent? (or, if NousResearch enforces SAML SSO, authorize at https://github.com/orgs/NousResearch/sso)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard P1 High — major feature broken, no workaround sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants