Merge pull request #3332 from hehuaiyu/fix/windows-bun-shim-resolution
fix(tooling): resolve Bun executable on Windows
This commit is contained in:
@@ -40,14 +40,14 @@ bun install
|
||||
bun run electron:dev
|
||||
```
|
||||
|
||||
`bun run electron:dev` starts the web dev server with HMR, then launches Electron against `packages/electron/main.mjs`.
|
||||
`bun run electron:dev` starts the web dev server with HMR, then launches Electron against `packages/electron/main.mjs`. On Windows, the HMR launcher resolves npm's `bun.cmd` shim to the underlying `bun.exe` before spawning Bun child processes.
|
||||
|
||||
The Electron workspace package trusts Electron's install script so `bun install` downloads the platform runtime in fresh checkouts and worktrees.
|
||||
|
||||
Electron's postinstall (`node install.js`) is run by `bun install` with the system Node. Older Electron releases bundled `extract-zip@2.0.1`, which under Node 24 silently unpacked only the first entry of the Electron zip, leaving `dist/` without the binary and `path.txt` missing. Electron 43+ ships its own fixed extractor (`@electron-internal/extract-zip`), but to keep interrupted or wrong-architecture installs from blocking desktop work:
|
||||
|
||||
- The root `postinstall` runs `ensure-electron.mjs --best-effort`, which detects an incomplete Electron install (missing binary, stale `dist/version`/`path.txt`, or a binary of the wrong architecture) and repairs it by re-running the postinstall under Bun (which extracts correctly), falling back to Node.
|
||||
- `electron-dev.mjs` runs the same check (fail-fast, not best-effort) before launching, so `bun run electron:dev` self-heals even when an install was interrupted.
|
||||
- `electron-dev.mjs` runs the same check (fail-fast, not best-effort) before launching, so `bun run electron:dev` self-heals even when an install was interrupted. On Windows, the repair resolves npm's `bun.cmd` shim to the underlying `bun.exe` before spawning it from Node.
|
||||
- The check can be run on demand with `bun run --cwd packages/electron ensure:electron`; set `ELECTRON_SKIP_BINARY_DOWNLOAD=1` to skip repair (e.g. CI without a network).
|
||||
- Unit tests in `scripts/ensure-electron.test.mjs` (run via `bun run --cwd packages/electron test:architecture`) cover healthy/missing/stale installs, wrong-architecture binaries, repair fallback, and `--best-effort`.
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import path from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
import { resolveBunExecutable } from '../../../scripts/lib/bun-executable.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoElectronDir = path.resolve(__dirname, '..');
|
||||
@@ -225,7 +227,11 @@ export function isComplete(electronDir, expected = expectedArch()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function resolveInstallCommands(env) {
|
||||
function resolveInstallCommands(env, bunResolver = resolveBunExecutable) {
|
||||
const normalize = (commands) => commands.map(([bin, args]) => [
|
||||
bin === 'bun' ? bunResolver({ env }) : bin,
|
||||
args,
|
||||
]);
|
||||
if (env.OPENCHAMBER_ELECTRON_INSTALL_COMMANDS) {
|
||||
try {
|
||||
const parsed = JSON.parse(env.OPENCHAMBER_ELECTRON_INSTALL_COMMANDS);
|
||||
@@ -233,22 +239,22 @@ function resolveInstallCommands(env) {
|
||||
Array.isArray(parsed) &&
|
||||
parsed.every((command) => Array.isArray(command) && typeof command[0] === 'string')
|
||||
) {
|
||||
return parsed;
|
||||
return normalize(parsed);
|
||||
}
|
||||
} catch {
|
||||
// Fall through to the default commands.
|
||||
}
|
||||
}
|
||||
return [
|
||||
return normalize([
|
||||
['bun', ['install.js']],
|
||||
['node', ['install.js']],
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
export function repair(electronDir, options = {}) {
|
||||
const env = options.env ?? process.env;
|
||||
const runner = options.runner ?? spawnSync;
|
||||
const commands = options.commands ?? resolveInstallCommands(env);
|
||||
const commands = options.commands ?? resolveInstallCommands(env, options.bunResolver);
|
||||
|
||||
// A partial extraction can leave stale entries (and a stale path.txt) that
|
||||
// a re-run would merge with. Start clean so the repair is deterministic.
|
||||
@@ -265,6 +271,7 @@ export function repair(electronDir, options = {}) {
|
||||
cwd: electronDir,
|
||||
stdio: options.stdio ?? 'inherit',
|
||||
env: { ...env, ELECTRON_SKIP_BINARY_DOWNLOAD: undefined },
|
||||
windowsHide: true,
|
||||
});
|
||||
if (result.error) {
|
||||
console.warn(`[electron:ensure] could not run \`${label}\`: ${result.error.message}`);
|
||||
|
||||
@@ -259,6 +259,32 @@ test('repair skips a command whose runner errors and keeps trying the rest', (t)
|
||||
assert.deepEqual(calls, commands);
|
||||
});
|
||||
|
||||
test('repair resolves Bun before invoking the install command', (t) => {
|
||||
const dir = withFixture(t);
|
||||
const calls = [];
|
||||
const resolvedBun = 'C:\\npm\\node_modules\\bun\\bin\\bun.exe';
|
||||
const result = repair(dir, {
|
||||
env: { TEST_ENV: 'present' },
|
||||
bunResolver: ({ env }) => {
|
||||
assert.equal(env.TEST_ENV, 'present');
|
||||
return resolvedBun;
|
||||
},
|
||||
runner: (bin, args, options) => {
|
||||
calls.push([bin, args, options.windowsHide]);
|
||||
if (bin !== resolvedBun) return { status: 1 };
|
||||
const platform = platformPath();
|
||||
fs.mkdirSync(path.join(dir, 'dist', path.dirname(platform)), { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, 'dist', 'version'), '41.2.1');
|
||||
fs.writeFileSync(path.join(dir, 'path.txt'), platform);
|
||||
fs.writeFileSync(path.join(dir, 'dist', platform), headerBytesForArch(expectedArch()));
|
||||
return { status: 0 };
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result, true);
|
||||
assert.deepEqual(calls, [[resolvedBun, ['install.js'], true]]);
|
||||
});
|
||||
|
||||
test('repair returns false when the runner reports a failure status for every command', (t) => {
|
||||
const dir = withFixture(t);
|
||||
const calls = [];
|
||||
|
||||
Reference in New Issue
Block a user