Skip to content

fix(build): v3.8.47 hotfix — npm tarball missing head-response-guard.cjs (#7065) + electron win spawn - #7055

Merged
diegosouzapw merged 6 commits into
mainfrom
fix/release-v3.8.47-electron-win-spawn
Jul 13, 2026
Merged

diegosouzapw merged 6 commits into
mainfrom
fix/release-v3.8.47-electron-win-spawn

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Post-release fix (v3.8.47 Phase 4 finding)

The Build Electron Desktop App / windows job failed twice (run 29260179839, original + rerun) with:

[electron] better-sqlite3 rebuild against electron 43.1.0 failed (exit null).

Root cause: spawnSync("npx.cmd", ...) without shell: true — since Node's CVE-2024-27980 hardening, spawning .cmd/.bat shims without a shell fails outright (status: null). The call was introduced this cycle by #6605 (electron 42→43 + build-from-source) and only runs on tag builds, so it first fired at the v3.8.47 tag. macOS/Linux jobs pass (plain npx).

Fix: extracted the spawn plan into scripts/build/electronRebuildPlan.mjs (pure, import-safe) returning shell: true on win32 — same pattern as scripts/quality/run-eslint-json.mjs. Args are fixed literals; no untrusted input reaches the shell.

Test (TDD): tests/unit/electron-rebuild-spawn-win.test.ts — red before (module absent/shell-less), green after (2/2).

After merge: dispatch electron-release.yml with version=v3.8.47 (workflow_dispatch builds from main = tag tree + this fix) to publish the missing Windows desktop artifact. The fix reaches release/v3.8.48 via the Phase 5 sync-back.

…te3 rebuild

Node's CVE-2024-27980 hardening makes spawnSync of .cmd/.bat shims fail with
status null unless shell:true — the v3.8.47 tag build died on the Windows job
with 'better-sqlite3 rebuild against electron 43.1.0 failed (exit null)'.
Extracted the spawn plan into electronRebuildPlan.mjs (pure, import-safe) with
a regression test; args are fixed literals, no untrusted input reaches the shell.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request resolves a Windows build failure where spawning npx.cmd without a shell fails due to Node's CVE-2024-27980 hardening. It introduces a helper function buildRebuildSpawnPlan to determine the correct command, arguments, and shell option based on the platform, integrates it into the Electron standalone preparation script, and adds corresponding unit tests. The reviewer suggested defaulting the platform parameter to process.platform in buildRebuildSpawnPlan to simplify the call site in prepare-electron-standalone.mjs.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

* tag build ("better-sqlite3 rebuild against electron 43.1.0 failed (exit null)").
* The args are a fixed literal list — no untrusted input reaches the shell.
*/
export function buildRebuildSpawnPlan(platform) {

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.

medium

We can make the platform parameter optional by defaulting it to process.platform. This simplifies the call site in prepare-electron-standalone.mjs while still allowing the unit tests to pass specific platforms for testing.

Suggested change
export function buildRebuildSpawnPlan(platform) {
export function buildRebuildSpawnPlan(platform = process.platform) {

rmSync(join(destMod, "build"), { recursive: true, force: true });

console.log(`[electron] rebuilding better-sqlite3 against electron ${electronVersion} ABI…`);
const plan = buildRebuildSpawnPlan(process.platform);

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.

medium

Since we can default the platform parameter to process.platform in buildRebuildSpawnPlan, we can simplify this call by omitting the argument.

Suggested change
const plan = buildRebuildSpawnPlan(process.platform);
const plan = buildRebuildSpawnPlan();

@github-actions

Copy link
Copy Markdown
Contributor

CI Coverage Report

  • Coverage job: skipped
  • PR test policy: success

Coverage artifact was not available for this run.

The prepublish prune allowlist (pack-artifact-policy.ts) lacked
head-response-guard.cjs, so assembleStandalone copied it and the prune
deleted it — every boot of the published 3.8.47 crashed with
ERR_MODULE_NOT_FOUND (3rd occurrence of this class; tls-options/3.8.41).
Also added to PACK_ARTIFACT_REQUIRED_PATHS so check:pack-artifact fails
loudly, plus a closure test that derives every server-ws.mjs sibling
import and asserts both lists cover it.
@diegosouzapw diegosouzapw changed the title fix(build): spawn npx.cmd through a shell in the electron better-sqlite3 rebuild fix(build): v3.8.47 hotfix — npm tarball missing head-response-guard.cjs (#7065) + electron win spawn Jul 13, 2026
@diegosouzapw

Copy link
Copy Markdown
Collaborator Author

Scope expanded — this PR is now the full v3.8.47 hotfix:

  1. [BUG] Its not opening #7065 (critical): the published npm 3.8.47 crashes on every boot — dist/head-response-guard.cjs was pruned from the tarball (allowlist gap in pack-artifact-policy.ts; 3rd occurrence of this class after tls-options/3.8.41). Fixed + check:pack-artifact now enforces it + a closure test derives every server-ws.mjs sibling import so the class can't recur. Same root cause as the VPS boot-loop (assembleStandalone omits head-response-guard.cjs from dist/ — deployed bundle boot-loops (2nd occurrence) #7040).
  2. Electron Windows spawn fix (original scope).

After merge: republish npm (needs a new version — 3.8.47 is immutable) + dispatch the electron workflow.

- ci.yml sonarqube job: download the coverage artifact into coverage/ so
  lcov.info lands where sonar.javascript.lcov.reportPaths points — the
  upload strips the common coverage/ prefix, so 'path: .' left new-code
  coverage at 0% on every scan
- kiro auto-import: await the async isCloudEnabled() gate (S6544 — a bare
  truthiness check on the Promise made syncToCloud run even with cloud
  sync disabled)
- stream.ts: real JSON fallback in the reasoning-split clone (S3923 — both
  ternary branches called structuredClone, the promised fallback was dead)
- codex executor: handle the async reader.cancel() rejection (S4822)
- deterministic localeCompare sorts (S2871 x2)
- classify-pr-changes.mjs: confine the argv list file to the workspace
  (jssecurity:S8707 path-traversal guard) + behavioral tests
- Dockerfile: rebuild better-sqlite3 with npm's bundled node-gyp instead
  of npx --yes (docker:S6505 — no on-demand registry install)
The org's SonarCloud FREE plan cannot associate a custom quality gate — only
the built-in Sonar way (80% new coverage) applies, which no full-cycle release
PR can realistically meet. The scan still uploads (dashboard, PR decoration);
the CI job just no longer fails on the gate. A tuned 'OmniRoute way' gate
(coverage >=60, duplication <=5) is already configured in the org for the day
the plan is upgraded — re-enable wait=true then.
pack-artifact-policy.test.ts encoded the pre-#7065 REQUIRED list (without
head-response-guard.cjs) and dockerfile-better-sqlite3-node-gyp-6700.test.ts
asserted the npx invocation the Sonar fix replaced with npm's bundled
node-gyp — both now assert the corrected behavior.
@diegosouzapw
diegosouzapw merged commit 7ee5bbc into main Jul 13, 2026
8 checks passed
@diegosouzapw
diegosouzapw deleted the fix/release-v3.8.47-electron-win-spawn branch July 19, 2026 21:01
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…-inference#7065), electron win spawn, Sonar gate zeroed (cheaper-inference#7055)

* fix(build): spawn npx.cmd through a shell in the electron better-sqlite3 rebuild

Node's CVE-2024-27980 hardening makes spawnSync of .cmd/.bat shims fail with
status null unless shell:true — the v3.8.47 tag build died on the Windows job
with 'better-sqlite3 rebuild against electron 43.1.0 failed (exit null)'.
Extracted the spawn plan into electronRebuildPlan.mjs (pure, import-safe) with
a regression test; args are fixed literals, no untrusted input reaches the shell.

* fix(build): ship head-response-guard.cjs in the npm tarball (cheaper-inference#7065)

The prepublish prune allowlist (pack-artifact-policy.ts) lacked
head-response-guard.cjs, so assembleStandalone copied it and the prune
deleted it — every boot of the published 3.8.47 crashed with
ERR_MODULE_NOT_FOUND (3rd occurrence of this class; tls-options/3.8.41).
Also added to PACK_ARTIFACT_REQUIRED_PATHS so check:pack-artifact fails
loudly, plus a closure test that derives every server-ws.mjs sibling
import and asserts both lists cover it.

* fix(ci): zero the Sonar quality-gate findings on new code

- ci.yml sonarqube job: download the coverage artifact into coverage/ so
  lcov.info lands where sonar.javascript.lcov.reportPaths points — the
  upload strips the common coverage/ prefix, so 'path: .' left new-code
  coverage at 0% on every scan
- kiro auto-import: await the async isCloudEnabled() gate (S6544 — a bare
  truthiness check on the Promise made syncToCloud run even with cloud
  sync disabled)
- stream.ts: real JSON fallback in the reasoning-split clone (S3923 — both
  ternary branches called structuredClone, the promised fallback was dead)
- codex executor: handle the async reader.cancel() rejection (S4822)
- deterministic localeCompare sorts (S2871 x2)
- classify-pr-changes.mjs: confine the argv list file to the workspace
  (jssecurity:S8707 path-traversal guard) + behavioral tests
- Dockerfile: rebuild better-sqlite3 with npm's bundled node-gyp instead
  of npx --yes (docker:S6505 — no on-demand registry install)

* chore(ci): make the Sonar quality gate informational (wait=false)

The org's SonarCloud FREE plan cannot associate a custom quality gate — only
the built-in Sonar way (80% new coverage) applies, which no full-cycle release
PR can realistically meet. The scan still uploads (dashboard, PR decoration);
the CI job just no longer fails on the gate. A tuned 'OmniRoute way' gate
(coverage >=60, duplication <=5) is already configured in the org for the day
the plan is upgraded — re-enable wait=true then.

* chore(release): v3.8.48 hotfix bump + changelog (npm 3.8.47 unbootable, cheaper-inference#7065)

* test: align pack-artifact + dockerfile guards to the corrected contracts

pack-artifact-policy.test.ts encoded the pre-cheaper-inference#7065 REQUIRED list (without
head-response-guard.cjs) and dockerfile-better-sqlite3-node-gyp-6700.test.ts
asserted the npx invocation the Sonar fix replaced with npm's bundled
node-gyp — both now assert the corrected behavior.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…-inference#7065), electron win spawn, Sonar gate zeroed (cheaper-inference#7055)

* fix(build): spawn npx.cmd through a shell in the electron better-sqlite3 rebuild

Node's CVE-2024-27980 hardening makes spawnSync of .cmd/.bat shims fail with
status null unless shell:true — the v3.8.47 tag build died on the Windows job
with 'better-sqlite3 rebuild against electron 43.1.0 failed (exit null)'.
Extracted the spawn plan into electronRebuildPlan.mjs (pure, import-safe) with
a regression test; args are fixed literals, no untrusted input reaches the shell.

* fix(build): ship head-response-guard.cjs in the npm tarball (cheaper-inference#7065)

The prepublish prune allowlist (pack-artifact-policy.ts) lacked
head-response-guard.cjs, so assembleStandalone copied it and the prune
deleted it — every boot of the published 3.8.47 crashed with
ERR_MODULE_NOT_FOUND (3rd occurrence of this class; tls-options/3.8.41).
Also added to PACK_ARTIFACT_REQUIRED_PATHS so check:pack-artifact fails
loudly, plus a closure test that derives every server-ws.mjs sibling
import and asserts both lists cover it.

* fix(ci): zero the Sonar quality-gate findings on new code

- ci.yml sonarqube job: download the coverage artifact into coverage/ so
  lcov.info lands where sonar.javascript.lcov.reportPaths points — the
  upload strips the common coverage/ prefix, so 'path: .' left new-code
  coverage at 0% on every scan
- kiro auto-import: await the async isCloudEnabled() gate (S6544 — a bare
  truthiness check on the Promise made syncToCloud run even with cloud
  sync disabled)
- stream.ts: real JSON fallback in the reasoning-split clone (S3923 — both
  ternary branches called structuredClone, the promised fallback was dead)
- codex executor: handle the async reader.cancel() rejection (S4822)
- deterministic localeCompare sorts (S2871 x2)
- classify-pr-changes.mjs: confine the argv list file to the workspace
  (jssecurity:S8707 path-traversal guard) + behavioral tests
- Dockerfile: rebuild better-sqlite3 with npm's bundled node-gyp instead
  of npx --yes (docker:S6505 — no on-demand registry install)

* chore(ci): make the Sonar quality gate informational (wait=false)

The org's SonarCloud FREE plan cannot associate a custom quality gate — only
the built-in Sonar way (80% new coverage) applies, which no full-cycle release
PR can realistically meet. The scan still uploads (dashboard, PR decoration);
the CI job just no longer fails on the gate. A tuned 'OmniRoute way' gate
(coverage >=60, duplication <=5) is already configured in the org for the day
the plan is upgraded — re-enable wait=true then.

* chore(release): v3.8.48 hotfix bump + changelog (npm 3.8.47 unbootable, cheaper-inference#7065)

* test: align pack-artifact + dockerfile guards to the corrected contracts

pack-artifact-policy.test.ts encoded the pre-cheaper-inference#7065 REQUIRED list (without
head-response-guard.cjs) and dockerfile-better-sqlite3-node-gyp-6700.test.ts
asserted the npx invocation the Sonar fix replaced with npm's bundled
node-gyp — both now assert the corrected behavior.
KooshaPari pushed a commit to KooshaPari/OmniRoute that referenced this pull request Aug 11, 2026
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…-inference#7065), electron win spawn, Sonar gate zeroed (cheaper-inference#7055)

* fix(build): spawn npx.cmd through a shell in the electron better-sqlite3 rebuild

Node's CVE-2024-27980 hardening makes spawnSync of .cmd/.bat shims fail with
status null unless shell:true — the v3.8.47 tag build died on the Windows job
with 'better-sqlite3 rebuild against electron 43.1.0 failed (exit null)'.
Extracted the spawn plan into electronRebuildPlan.mjs (pure, import-safe) with
a regression test; args are fixed literals, no untrusted input reaches the shell.

* fix(build): ship head-response-guard.cjs in the npm tarball (cheaper-inference#7065)

The prepublish prune allowlist (pack-artifact-policy.ts) lacked
head-response-guard.cjs, so assembleStandalone copied it and the prune
deleted it — every boot of the published 3.8.47 crashed with
ERR_MODULE_NOT_FOUND (3rd occurrence of this class; tls-options/3.8.41).
Also added to PACK_ARTIFACT_REQUIRED_PATHS so check:pack-artifact fails
loudly, plus a closure test that derives every server-ws.mjs sibling
import and asserts both lists cover it.

* fix(ci): zero the Sonar quality-gate findings on new code

- ci.yml sonarqube job: download the coverage artifact into coverage/ so
  lcov.info lands where sonar.javascript.lcov.reportPaths points — the
  upload strips the common coverage/ prefix, so 'path: .' left new-code
  coverage at 0% on every scan
- kiro auto-import: await the async isCloudEnabled() gate (S6544 — a bare
  truthiness check on the Promise made syncToCloud run even with cloud
  sync disabled)
- stream.ts: real JSON fallback in the reasoning-split clone (S3923 — both
  ternary branches called structuredClone, the promised fallback was dead)
- codex executor: handle the async reader.cancel() rejection (S4822)
- deterministic localeCompare sorts (S2871 x2)
- classify-pr-changes.mjs: confine the argv list file to the workspace
  (jssecurity:S8707 path-traversal guard) + behavioral tests
- Dockerfile: rebuild better-sqlite3 with npm's bundled node-gyp instead
  of npx --yes (docker:S6505 — no on-demand registry install)

* chore(ci): make the Sonar quality gate informational (wait=false)

The org's SonarCloud FREE plan cannot associate a custom quality gate — only
the built-in Sonar way (80% new coverage) applies, which no full-cycle release
PR can realistically meet. The scan still uploads (dashboard, PR decoration);
the CI job just no longer fails on the gate. A tuned 'OmniRoute way' gate
(coverage >=60, duplication <=5) is already configured in the org for the day
the plan is upgraded — re-enable wait=true then.

* chore(release): v3.8.48 hotfix bump + changelog (npm 3.8.47 unbootable, cheaper-inference#7065)

* test: align pack-artifact + dockerfile guards to the corrected contracts

pack-artifact-policy.test.ts encoded the pre-cheaper-inference#7065 REQUIRED list (without
head-response-guard.cjs) and dockerfile-better-sqlite3-node-gyp-6700.test.ts
asserted the npx invocation the Sonar fix replaced with npm's bundled
node-gyp — both now assert the corrected behavior.
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.

1 participant