Skip to content

fix(ci): install node-jq binary explicitly (npm 11 blocks its preinstall)#24

Merged
wavyx merged 3 commits into
mainfrom
fix-ci-node-jq-binary
Jul 9, 2026
Merged

fix(ci): install node-jq binary explicitly (npm 11 blocks its preinstall)#24
wavyx merged 3 commits into
mainfrom
fix-ci-node-jq-binary

Conversation

@wavyx

@wavyx wavyx commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Root cause

The v0.19.0 release gate hung: every --jq test timed out with spawn .../node_modules/node-jq/bin/jq ENOENT. CI runs npm install -g npm@latest (required for OIDC trusted publishing), and npm >= 11 blocks dependency lifecycle scripts by default, so node-jq's preinstall never downloaded its jq binary. The CI matrix's Node-24 leg (bundled npm 11) failed identically. node-jq then hangs on the missing binary rather than erroring, so it surfaced as 20s timeouts, not a clear ENOENT.

Not a code regression and not test contention — an npm policy change (v0.18 released before it).

Fix

Run node-jq's installer directly (node node_modules/node-jq/scripts/install-binary.mjs, a plain node call, not an npm hook) after npm ci in both ci.yml and release.yml. Also corrected the testTimeout comment/value: the real driver is the client retry tests' real exponential-backoff sleeps (~14s worst case), bumped to 30s.

Gate before merge: this PR's CI matrix must go green.

Follow-up (not here)

npm-install end users on npm 11 also get a broken/hanging --jq. v0.20 (distribution) will add a runtime guard so a missing jq binary fails fast with a clear message instead of hanging.

https://claude.ai/code/session_018dRMUUj9vpKp3tqKQFsvE9

Greptile Summary

This PR fixes a CI breakage introduced by npm ≥ 11 blocking dependency lifecycle scripts, which prevented node-jq's preinstall hook from downloading the jq binary, causing all --jq tests to hang with ENOENT. Both workflow files now invoke node-jq's installer directly after npm ci.

  • CI/Release workflows: adds node node_modules/node-jq/scripts/install-binary.mjs as an explicit step; the script path is confirmed present in node-jq ≥ 6.x.
  • invoke.js: extracts scheduleHardKill so the SIGKILL escalation is unit-testable on Windows; a new fake-timer test covers it.
  • catalog.test.js / vitest.config.js: fixes Windows path-separator normalisation and corrects the testTimeout comment/value to reflect actual retry-backoff sleep budgets.

Confidence Score: 5/5

Safe to merge — changes are narrowly scoped to CI plumbing, a small testability refactor, and two correctness fixes in tests.

All changes are well-targeted: the workflow fix uses the confirmed-valid scripts/install-binary.mjs path from node-jq's published package, the scheduleHardKill extraction is a non-functional refactor backed by a new deterministic test, the Windows path-separator fix is correct, and the timeout bump matches the stated worst-case retry sleep budget.

No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/ci.yml Adds explicit node node_modules/node-jq/scripts/install-binary.mjs step after npm ci in the test matrix job; confirmed valid in node-jq >= 6.x.
.github/workflows/release.yml Same install-binary step added after npm ci; correctly positioned after npm install -g npm@latest.
src/lib/mcp/invoke.js Extracts scheduleHardKill from makeExec for testability without OS signal semantics.
test/lib/mcp/invoke.test.js New scheduleHardKill test uses vi.useFakeTimers() in try/finally and verifies SIGKILL fires at the grace boundary.
test/lib/mcp/catalog.test.js Fixes realCommandIds to split on /[/\]/ so Windows backslash separators normalise correctly to colon-form command IDs.
vitest.config.js Bumps testTimeout/hookTimeout from 20s to 30s with corrected comment citing exponential-backoff retry sleeps.

Reviews (3): Last reviewed commit: "test(mcp): make the SIGKILL escalation c..." | Re-trigger Greptile

The v0.19.0 release gate hung: every --jq test failed with
'spawn node_modules/node-jq/bin/jq ENOENT'. CI upgrades to npm@latest
(needed for OIDC trusted publishing), and npm >= 11 blocks dependency
lifecycle scripts by default, so node-jq's preinstall never downloaded
its jq binary. Node 24's bundled npm 11 broke the CI matrix leg the same
way. Run node-jq's installer directly (a plain node invocation, not an
npm lifecycle hook) after npm ci in both workflows.

Also corrects the testTimeout rationale: the real driver is the client
retry tests' real exponential-backoff sleeps (~14s worst case), not jq;
bump to 30s for safe headroom on coverage-instrumented CI.

Claude-Session: https://claude.ai/code/session_018dRMUUj9vpKp3tqKQFsvE9
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

realCommandIds() split file paths on '/' only, so on Windows readdirSync's
backslash paths produced ids like 'deal\list' that missed the audited
map (4 failures on the windows-latest CI legs). Split on both separators.

Claude-Session: https://claude.ai/code/session_018dRMUUj9vpKp3tqKQFsvE9
@wavyx wavyx force-pushed the fix-ci-node-jq-binary branch from 6155f2d to c135d27 Compare July 9, 2026 07:54
The 100% coverage gate failed on the windows-latest legs: makeExec's
SIGTERM->SIGKILL grace escalation is unreachable there (Windows kills a
child on the first signal), leaving its callback uncovered. Extract it as
scheduleHardKill() and unit-test it directly with fake timers, so the
escalation is exercised OS-independently while the real-process test keeps
asserting end-to-end behavior on POSIX.

Claude-Session: https://claude.ai/code/session_018dRMUUj9vpKp3tqKQFsvE9
@wavyx wavyx merged commit 0550acc into main Jul 9, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants