mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
ci(test): download the Electron binary once before unit specs run
pnpm skips Electron's postinstall (electron is deliberately not in
onlyBuiltDependencies), so the binary is downloaded lazily on the first
require('electron'). Since Tier A projects run concurrently (#1701),
several electron-backend and database specs resolve and exec it at the
same time; one process execs it while another is still extracting it
and fails with `spawnSync .../electron ETXTBSY`.
tools/testing/ensure-electron-binary.mjs downloads the binary once and
fails unless it runs under ELECTRON_RUN_AS_NODE and reports the pinned
version. The unit job runs it right after pnpm install, coverage:unit:ci
runs it first for local runs, and the performance journeys job runs it
before its ldd check, which passed vacuously while no binary existed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
887ac64d18
commit
dd28911ee4
4 files changed
+87
-1
No files matched your search
@@ -285,9 +285,12 @@ jobs:
|
||||
# and never launch a Playwright browser, so no `playwright
|
||||
# install`. The runner image ships Electron's shared libraries and
|
||||
# xvfb; fail fast with a clear message if an image update drops one.
|
||||
# pnpm skips Electron's postinstall, so download the binary first;
|
||||
# otherwise ldd sees no file and the check passes vacuously.
|
||||
- name: Check Electron runtime dependencies
|
||||
run: |
|
||||
command -v xvfb-run || { echo "::error::xvfb-run is missing on the runner"; exit 1; }
|
||||
node tools/testing/ensure-electron-binary.mjs || { echo "::error::Electron binary download failed"; exit 1; }
|
||||
missing="$(ldd node_modules/electron/dist/electron | grep 'not found' || true)"
|
||||
if [ -n "$missing" ]; then
|
||||
echo "::error::Electron is missing shared libraries:"
|
||||
@@ -368,6 +371,15 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# pnpm skips Electron's postinstall (it is not in
|
||||
# onlyBuiltDependencies), so the binary is fetched on the first
|
||||
# require('electron'). Specs that run SQLite under
|
||||
# ELECTRON_RUN_AS_NODE resolve it that way while Tier A projects
|
||||
# run concurrently, and one exec'ing it mid-extraction fails with
|
||||
# ETXTBSY. Download and verify it once, before any test starts.
|
||||
- name: Download Electron binary
|
||||
run: node tools/testing/ensure-electron-binary.mjs
|
||||
|
||||
- name: Validate agent guidance
|
||||
run: pnpm run agents:validate
|
||||
|
||||
|
||||
@@ -107,6 +107,18 @@ with `diagnostics: false`); `isolatedModules`-incompatible syntax such as a
|
||||
type re-export without `export type` still fails at load time, and spec type
|
||||
errors are caught by `typecheck:spec` (see Unit And Type Checks).
|
||||
|
||||
Some `electron-backend` and `database` specs run SQLite code in the Electron
|
||||
binary under `ELECTRON_RUN_AS_NODE`, resolving it with `require('electron')`.
|
||||
pnpm does not run Electron's postinstall (`electron` is deliberately absent from
|
||||
`onlyBuiltDependencies`, see [workspace shell](workspace-shell.md)), so the
|
||||
first `require` downloads and extracts the binary. With projects and Jest
|
||||
workers in parallel, one process can exec it while another is still extracting
|
||||
it (`spawnSync … ETXTBSY` on Linux). `coverage:unit:ci` and a CI step right
|
||||
after `pnpm install` therefore run `tools/testing/ensure-electron-binary.mjs`
|
||||
first, which downloads the binary once and fails unless it runs and reports
|
||||
the pinned version. In a fresh worktree, run it before starting several of
|
||||
these specs at once by other means.
|
||||
|
||||
In CI, a pull request skips the Tier A suite (and the merged-coverage upload)
|
||||
when every changed file is outside Tier A test inputs:
|
||||
`tools/coverage/unit-coverage-scope.mjs` holds the allowlist (Markdown, `docs/`,
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@
|
||||
"styles:inputs:check": "node tools/nx/check-stylesheet-inputs.mjs",
|
||||
"styles:inputs:validate": "pnpm run styles:inputs:test && pnpm run styles:inputs:check",
|
||||
"coverage:tools:test": "node --test tools/coverage/coverage-integrity.test.mjs tools/coverage/e2e-shard-reports.test.mjs tools/coverage/coverage-run-pool.test.mjs tools/coverage/unit-coverage-scope.test.mjs",
|
||||
"coverage:unit:ci": "node tools/coverage/run-tier-a-coverage.mjs",
|
||||
"coverage:unit:ci": "node tools/testing/ensure-electron-binary.mjs && node tools/coverage/run-tier-a-coverage.mjs",
|
||||
"coverage:merge": "node tools/coverage/merge-coverage.mjs",
|
||||
"coverage:health": "node tools/coverage/coverage-health.mjs",
|
||||
"coverage:policy:check": "node tools/coverage/check-coverage-policy.mjs",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Downloads the Electron binary once and proves it runs, before anything
|
||||
// spawns it concurrently.
|
||||
//
|
||||
// pnpm does not run Electron's postinstall (it is not in
|
||||
// `onlyBuiltDependencies`), so `pnpm install` leaves no binary behind and
|
||||
// `require('electron')` downloads and extracts it on first use. Unit specs
|
||||
// that execute SQLite code under `ELECTRON_RUN_AS_NODE` resolve it that way,
|
||||
// and Tier A projects and Jest workers run in parallel: one process can exec
|
||||
// the binary while another is still extracting it (ETXTBSY on Linux, clobbered
|
||||
// framework symlinks on macOS). Running this first leaves nothing to download.
|
||||
//
|
||||
// Usage: node tools/testing/ensure-electron-binary.mjs
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { createRequire } from 'node:module';
|
||||
import process from 'node:process';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { version } = require('electron/package.json');
|
||||
|
||||
let electronPath;
|
||||
try {
|
||||
electronPath = require('electron');
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Electron ${version} binary download failed: ${error.message}`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let reported;
|
||||
try {
|
||||
reported = execFileSync(
|
||||
electronPath,
|
||||
['-e', 'process.stdout.write(process.versions.electron)'],
|
||||
{
|
||||
encoding: 'utf8',
|
||||
// The child's stderr (e.g. a dyld or loader error) goes straight
|
||||
// to the log instead of being repeated in the message below.
|
||||
stdio: ['ignore', 'pipe', 'inherit'],
|
||||
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
|
||||
timeout: 60_000,
|
||||
}
|
||||
).trim();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Electron binary at ${electronPath} does not run (${error.code ?? `exit ${error.status}`}). ` +
|
||||
'If an interrupted download left a partial copy, delete node_modules/electron/dist and path.txt, then rerun.'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (reported !== version) {
|
||||
console.error(
|
||||
`Electron binary at ${electronPath} reports ${reported || 'no version'}, expected ${version}.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Electron ${version} binary ready at ${electronPath}`);
|
||||
Reference in new issue
Block a user