Rebuild Electron binary when startup finds it missing
Template tests / tests (push) Failing after 15s
Template tests / tests (push) Failing after 15s
This commit is contained in:
@@ -35,6 +35,61 @@ function platformBinaryCandidates(platform) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function electronBinaryCandidates({ packageRoot, distDir, platform }) {
|
||||||
|
const candidatePaths = [];
|
||||||
|
const pathHint = packageRoot ? readElectronPathHint(packageRoot) : null;
|
||||||
|
|
||||||
|
if (pathHint) {
|
||||||
|
candidatePaths.push(path.join(distDir, pathHint));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const relativePath of platformBinaryCandidates(platform)) {
|
||||||
|
candidatePaths.push(path.join(distDir, relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidatePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
function runNpmRebuild({
|
||||||
|
packageRoot,
|
||||||
|
platform = process.platform,
|
||||||
|
arch = process.arch,
|
||||||
|
npmExecPath = process.env.npm_execpath || null,
|
||||||
|
npmNodeExecPath = process.env.npm_node_execpath || process.execPath,
|
||||||
|
}) {
|
||||||
|
if (!npmExecPath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = spawnSync(
|
||||||
|
npmNodeExecPath,
|
||||||
|
[npmExecPath, 'rebuild', 'electron', '--force', '--foreground-scripts'],
|
||||||
|
{
|
||||||
|
cwd: packageRoot,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
npm_config_platform: platform,
|
||||||
|
npm_config_arch: arch,
|
||||||
|
},
|
||||||
|
stdio: 'inherit',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.signal) {
|
||||||
|
throw new Error(`Electron repair was interrupted by ${result.signal}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status !== 0) {
|
||||||
|
throw new Error(`Electron rebuild failed with exit code ${result.status ?? 1}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function repairElectronInstall({
|
function repairElectronInstall({
|
||||||
packageRoot,
|
packageRoot,
|
||||||
platform = process.platform,
|
platform = process.platform,
|
||||||
@@ -81,6 +136,7 @@ function buildMissingElectronError({ packageRoot, distDir, candidatePaths }) {
|
|||||||
'Try reinstalling dependencies from the repo root:',
|
'Try reinstalling dependencies from the repo root:',
|
||||||
'',
|
'',
|
||||||
' npm install',
|
' npm install',
|
||||||
|
' npm rebuild electron --force --foreground-scripts',
|
||||||
'',
|
'',
|
||||||
'If that does not help, delete node_modules/electron and install again.',
|
'If that does not help, delete node_modules/electron and install again.',
|
||||||
'',
|
'',
|
||||||
@@ -103,34 +159,29 @@ function resolveElectronBinary({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const distDir = overrideDistPath || path.join(packageRoot, 'dist');
|
const distDir = overrideDistPath || path.join(packageRoot, 'dist');
|
||||||
const candidatePaths = [];
|
const candidatePaths = electronBinaryCandidates({ packageRoot, distDir, platform });
|
||||||
const pathHint = packageRoot ? readElectronPathHint(packageRoot) : null;
|
|
||||||
|
|
||||||
if (pathHint) {
|
|
||||||
candidatePaths.push(path.join(distDir, pathHint));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const relativePath of platformBinaryCandidates(platform)) {
|
|
||||||
candidatePaths.push(path.join(distDir, relativePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolved = candidatePaths.find((candidate) => fs.existsSync(candidate));
|
const resolved = candidatePaths.find((candidate) => fs.existsSync(candidate));
|
||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
if (packageRoot && repairElectronInstall({ packageRoot, platform, arch })) {
|
if (packageRoot) {
|
||||||
const repairedHint = readElectronPathHint(packageRoot);
|
if (runNpmRebuild({ packageRoot, platform, arch })) {
|
||||||
const repairedCandidates = [];
|
const rebuilt = electronBinaryCandidates({ packageRoot, distDir, platform }).find((candidate) =>
|
||||||
if (repairedHint) {
|
fs.existsSync(candidate)
|
||||||
repairedCandidates.push(path.join(distDir, repairedHint));
|
);
|
||||||
|
if (rebuilt) {
|
||||||
|
return rebuilt;
|
||||||
}
|
}
|
||||||
for (const relativePath of platformBinaryCandidates(platform)) {
|
|
||||||
repairedCandidates.push(path.join(distDir, relativePath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const repaired = repairedCandidates.find((candidate) => fs.existsSync(candidate));
|
if (repairElectronInstall({ packageRoot, platform, arch })) {
|
||||||
|
const repaired = electronBinaryCandidates({ packageRoot, distDir, platform }).find((candidate) =>
|
||||||
|
fs.existsSync(candidate)
|
||||||
|
);
|
||||||
if (repaired) {
|
if (repaired) {
|
||||||
return repaired;
|
return repaired;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(buildMissingElectronError({ packageRoot, distDir, candidatePaths }));
|
throw new Error(buildMissingElectronError({ packageRoot, distDir, candidatePaths }));
|
||||||
}
|
}
|
||||||
@@ -140,8 +191,10 @@ function resolveElectronBinary({
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
buildMissingElectronError,
|
buildMissingElectronError,
|
||||||
|
electronBinaryCandidates,
|
||||||
readElectronPathHint,
|
readElectronPathHint,
|
||||||
repairElectronInstall,
|
repairElectronInstall,
|
||||||
|
runNpmRebuild,
|
||||||
resolveElectronBinary,
|
resolveElectronBinary,
|
||||||
resolveElectronPackageRoot,
|
resolveElectronPackageRoot,
|
||||||
platformBinaryCandidates,
|
platformBinaryCandidates,
|
||||||
|
|||||||
@@ -65,6 +65,40 @@ test('repairs a broken Electron install before resolving the binary', (t) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('rebuilds Electron through npm when the binary is missing', (t) => {
|
||||||
|
const root = makeTmpDir('electron-rebuild');
|
||||||
|
t.after(() => rmrf(root));
|
||||||
|
|
||||||
|
fs.mkdirSync(path.join(root, 'dist'), { recursive: true });
|
||||||
|
const fakeNpmCli = path.join(root, 'fake-npm-cli.js');
|
||||||
|
fs.writeFileSync(
|
||||||
|
fakeNpmCli,
|
||||||
|
[
|
||||||
|
"const fs = require('node:fs');",
|
||||||
|
"const path = require('node:path');",
|
||||||
|
"fs.mkdirSync(path.join(__dirname, 'dist'), { recursive: true });",
|
||||||
|
"fs.writeFileSync(path.join(__dirname, 'dist', 'electron.exe'), 'binary');",
|
||||||
|
"fs.writeFileSync(path.join(__dirname, 'path.txt'), 'electron.exe');",
|
||||||
|
].join('\n')
|
||||||
|
);
|
||||||
|
|
||||||
|
const originalNpmExecPath = process.env.npm_execpath;
|
||||||
|
const originalNpmNodeExecPath = process.env.npm_node_execpath;
|
||||||
|
process.env.npm_execpath = fakeNpmCli;
|
||||||
|
process.env.npm_node_execpath = process.execPath;
|
||||||
|
t.after(() => {
|
||||||
|
if (originalNpmExecPath === undefined) delete process.env.npm_execpath;
|
||||||
|
else process.env.npm_execpath = originalNpmExecPath;
|
||||||
|
if (originalNpmNodeExecPath === undefined) delete process.env.npm_node_execpath;
|
||||||
|
else process.env.npm_node_execpath = originalNpmNodeExecPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
resolveElectronBinary({ packageRoot: root, platform: 'win32' }),
|
||||||
|
path.join(root, 'dist', 'electron.exe')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('reports a helpful error when the runtime is missing', (t) => {
|
test('reports a helpful error when the runtime is missing', (t) => {
|
||||||
const root = makeTmpDir('electron-missing');
|
const root = makeTmpDir('electron-missing');
|
||||||
t.after(() => rmrf(root));
|
t.after(() => rmrf(root));
|
||||||
|
|||||||
Reference in New Issue
Block a user