test(package): reject an Electron install takeover by exact command

CodeRabbit was right about #20787. Replacing the pinned postinstall string
with a /electron/i keyword check was wrong in both directions, verified:

  rebuild-native-deps.mjs && rebuild-native-deps.mjs   PASSED  (should fail)
  rebuild-native-deps.mjs && check-electron-version    FAILED  (should pass)

The owner's own path contains no "electron", so duplicating it slipped
through -- the one case the contract is named for. And a substring match
rejects any later step that merely mentions Electron, which is the same
over-tightness that broke every open PR in the first place, relocated.

Later steps are now checked against the exact owned command plus the known
Electron install commands. A second case pins the rejections themselves,
because reading the real postinstall cannot show a bad chain would be caught
-- that is how #20787 shipped with a guard that did not guard.

Split into its own file rather than adding a max-lines disable (AGENTS.md).
This commit is contained in:
Neil
2026-09-15 00:08:18 -07:00
parent c9ae17fe3d
commit 2dcbc6a83a
2 changed files with 46 additions and 12 deletions
@@ -0,0 +1,46 @@
import { readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { parse } from 'yaml'
const projectDir = resolve(import.meta.dirname, '../..')
const readProject = (file) => readFileSync(join(projectDir, file), 'utf8')
const packageJson = JSON.parse(readProject('package.json'))
const pnpmWorkspace = parse(readProject('pnpm-workspace.yaml'))
const OWNED_ELECTRON_REBUILD = 'node config/scripts/rebuild-native-deps.mjs'
// Why exact commands and not /electron/i: the owner's own path has no "electron" in it, so a
// keyword check waves a duplicated rebuild through -- the case this contract is named for --
// while rejecting any later step that merely mentions Electron (#20787).
const ELECTRON_INSTALL_COMMANDS = [
OWNED_ELECTRON_REBUILD,
'electron-rebuild',
'electron-builder install-app-deps',
'install-app-deps'
]
const takesOverElectronInstall = (step) =>
ELECTRON_INSTALL_COMMANDS.some((command) => step.includes(command))
describe('Electron binary install ownership', () => {
it('keeps root postinstall as the single Electron binary install owner', () => {
// The invariant is that the root postinstall owns the Electron binary install, not that
// nothing may run after it -- pinning the whole string broke every open PR (#20726).
const steps = packageJson.scripts.postinstall.split('&&').map((step) => step.trim())
expect(steps[0]).toBe(OWNED_ELECTRON_REBUILD)
for (const step of steps.slice(1)) {
expect(takesOverElectronInstall(step)).toBe(false)
}
expect(pnpmWorkspace.allowBuilds).not.toHaveProperty('electron')
})
// Why a separate case: the assertion above only reads the real postinstall, so it cannot show
// a bad chain would be caught. #20787 shipped a keyword check that missed a duplicated
// rebuild; these fixtures pin the rejections themselves.
it('rejects a chained step that would take over the Electron install', () => {
expect(takesOverElectronInstall(OWNED_ELECTRON_REBUILD)).toBe(true)
expect(takesOverElectronInstall('npx electron-rebuild')).toBe(true)
expect(takesOverElectronInstall('npx electron-builder install-app-deps')).toBe(true)
expect(takesOverElectronInstall('node config/scripts/sync-anti-slop-plugin.mjs')).toBe(false)
expect(takesOverElectronInstall('node config/scripts/check-electron-version.mjs')).toBe(false)
})
})
@@ -24,18 +24,6 @@ describe('Electron runtime package contract', () => {
linux: createPackagedRuntimeNodeModuleResources('linux')
}
it('keeps root postinstall as the single Electron binary install owner', () => {
// Why not an exact match: the invariant is that the root postinstall owns the Electron
// binary install, not that nothing else may run after it. Pinning the whole string made
// any unrelated chained step (a lint-plugin sync, say) a CI failure for every open PR.
const postinstall = packageJson.scripts.postinstall
const steps = postinstall.split('&&').map((step) => step.trim())
expect(steps[0]).toBe('node config/scripts/rebuild-native-deps.mjs')
// No later step may take over the Electron install the first step owns.
expect(steps.slice(1).join(' ')).not.toMatch(/electron/i)
expect(pnpmWorkspace.allowBuilds).not.toHaveProperty('electron')
})
it('keeps the native Windows registry addon optional and platform-gated', () => {
const rebuildScript = readProject('config/scripts/rebuild-native-deps.mjs')
const ensureScript = readProject('config/scripts/ensure-native-runtime.mjs')