Skip to content

fix(nix): hashless npm deps via importNpmLock - #48213

Closed
ak2k wants to merge 1 commit into
NousResearch:mainfrom
ak2k:fix/nix-importnpmlock
Closed

fix(nix): hashless npm deps via importNpmLock#48213
ak2k wants to merge 1 commit into
NousResearch:mainfrom
ak2k:fix/nix-importnpmlock

Conversation

@ak2k

@ak2k ak2k commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What

Fetch the npm workspace dependencies with pkgs.importNpmLock instead of fetchNpmDeps + a hand-maintained npmDepsHash, and remove the fix-lockfiles tooling whose only job was keeping that hash in sync.

Why

nix/lib.nix pins one npmDepsHash for the whole workspace. Any change to package-lock.json that doesn't also refresh that hash breaks the bundled hermes-tui / hermes-desktop-renderer build for Nix flake consumers with a fixed-output hash mismatch. It recurs because the hash is hand-maintained and the workflow that refreshed it (nix-lockfile-fix.yml) was removed along with the rest of nix CI in 9eb0bcd ("change(ci): rip out nix ci for now"). See #37692.

importNpmLock resolves each dependency from the lockfile's own integrity field, so package-lock.json is the single source of truth and there is no separate hash to drift. This removes the failure class instead of re-automating the refresh: the old fetchNpmDeps hook did a byte-for-byte diff of the source lockfile against the realized deps cache — which is what both fix-lockfiles and the deleted newline-normalizing patchPhase worked around — whereas importNpmLock's hook overwrites the lockfile rather than diffing it, so neither workaround is needed.

What changes

  • nix/lib.nix: the shared npmDeps becomes pkgs.importNpmLock.importNpmLock { npmRoot = src; }, and mkNpmPassthru sets npmConfigHook = pkgs.importNpmLock.npmConfigHook. npmDepsHash, npmDepsFetcherVersion, and the newline-normalizing patchPhase are removed.
  • fix-lockfiles is removed — the mkFixLockfiles builder, its mkNpmDevShellHook wiring, and the .#fix-lockfiles flake package. It only refreshed npmDepsHash, and its sole CI consumer was already removed in 9eb0bcd.
  • npm-lockfile-fix is retained: importNpmLock requires an integrity-complete lockfile, which that tool guarantees when the lockfile is regenerated (update_<attr>_lockfile).

The four buildNpmPackage consumers (tui, web, desktop) are untouched — they spread the same mkNpmPassthru attrs.

Testing

I built the workspace packages through this path on aarch64-darwin and confirmed the deps resolve on x86_64-linux:

  • hermes-tui — esbuild bundle
  • hermes-web — tsc + vite build
  • hermes-desktop renderer — including npm rebuild node-pty --build-from-source staging the native pty.node
  • hermes-agent (.default) — embeds tui + web

The dev shell still evaluates and .#fix-lockfiles is correctly gone.

Notes

  • importNpmLock's hook runs npm install --ignore-scripts then npm rebuild (the old fetchNpmDeps hook also ran npm ci --ignore-scripts + npm rebuild, so native modules like node-pty build the same way). npm install is more permissive than npm ci: if package.json and the lockfile ever drift, the old path failed at build, whereas this path installs from the lockfile-derived store paths. update_<attr>_lockfile still ends in nix build .#<attr>, so the verify step is preserved; a npm ci --dry-run sync check could restore the stricter guard if wanted.
  • Requires a nixpkgs with pkgs.importNpmLock (present since 2024; the current pin satisfies it).

Closes #37692.

The npm workspace pins a single npmDepsHash for fetchNpmDeps. Any change to
package-lock.json that doesn't also refresh that hash breaks the bundled
hermes-tui / hermes-desktop-renderer build for Nix flake consumers, and no
nix CI catches it — the workflow that ran fix-lockfiles was removed in
9eb0bcd ("change(ci): rip out nix ci for now").

Fetch the workspace deps with pkgs.importNpmLock instead. It resolves each
package from the lockfile's own integrity hashes, so package-lock.json is the
single source of truth and there is no separate hash to drift.

This also removes:

- the fix-lockfiles checker/refresher and its devShell wiring — it existed
  only to keep npmDepsHash in sync, so it is dead once the hash is gone, and
  its sole CI consumer was already removed in 9eb0bcd;
- the patchPhase that normalized lockfile trailing newlines — importNpmLock's
  npmConfigHook overwrites the lockfile rather than diffing it, so the
  normalization is unnecessary.

npm-lockfile-fix is retained: importNpmLock requires an integrity-complete
lockfile, which that tool guarantees when the lockfile is regenerated.
@alt-glitch alt-glitch added type/bug Something isn't working area/nix Nix flake, NixOS module, container packaging P1 High — major feature broken, no workaround labels Jun 18, 2026
@elementh

elementh commented Jun 18, 2026

Copy link
Copy Markdown

Humbly asking, is there any ETA for this changes? I wanted to try Hermes in nixos as a module but it won’t build :)

@ak2k

ak2k commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Humbly asking, is there any ETA for this changes? I wanted to try Hermes in nixos as a module but it won’t build :)

Hi! I don't know how long it will take for the PR to get merged; however, for nix usage, here's another option that's quite stable but at the cost of being up to a day or so behind upstream: https://github.com/numtide/llm-agents.nix

@elementh

Copy link
Copy Markdown

Hi! I don't know how long it will take for the PR to get merged; however, for nix usage, here's another option that's quite stable but at the cost of being up to a day or so behind upstream: https://github.com/numtide/llm-agents.nix

Thanks for the suggestion, I'll check it out :)

@ak2k

ak2k commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

For cross-reference: this PR removes the hand-maintained workspace npmDepsHash behind the recurring "stale hash on main" build breaks for flake consumers.

importNpmLock derives each dependency hash from package-lock.json's own integrity fields, so there's no separate hash to refresh per lockfile change — the lockfile becomes the single source of truth.

(Same class, earlier recurrences: #12965, #15244, #15272, #15314, #19760.)

@alt-glitch

Copy link
Copy Markdown
Collaborator

Thank you for this, @ak2k 🙏 — switching to importNpmLock was exactly the right call. The hand-maintained npmDepsHash wasn't just drifting on lockfile changes, it's fundamentally not portable across nixpkgs versions: a flake consumer with hermes-agent.inputs.nixpkgs.follows = "nixpkgs" builds the deps FOD with their nixpkgs and gets a different hash than the one we commit. That bit a downstream NixOS homelab (MRDGH2821/nix-config) right after we refreshed the hash in #48457 — their newer nixos-unstable produced a different FOD hash and the build failed again. importNpmLock resolving each package from the lockfile's own integrity removes that whole failure class.

Your branch had gone stale (~96 commits), so I cherry-picked your commit onto current main with your authorship preserved and merged it via #48883 (commit 3ca0ef7). Verified end-to-end before merge: all npm packages (tui/web/desktop/default) build under both the repo's pinned nixpkgs and the consumer's newer one, a NixOS VM boots with the gateway active (running), and the affected homelab's literal home-lab config builds clean under its original nixpkgs.

Appreciate the clean fix and the clear writeup. 🚀

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

Labels

area/nix Nix flake, NixOS module, container packaging P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Nix build broken — hermes-desktop-renderer npmDepsHash is out of date

3 participants