Files
seedit/scripts/find-forge-executable.js
plebeius dd5efca0fb fix CI
2026-01-29 19:17:08 +08:00

125 lines
3.9 KiB
JavaScript

#!/usr/bin/env node
/**
* Find the packaged Electron executable built by Electron Forge.
* This script locates the executable in the out/ directory structure.
*/
import { readdirSync, statSync, existsSync, readFileSync } from 'fs';
import { isAbsolute, join, resolve } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = resolve(__filename, '..');
const platform = process.platform;
const repoRoot = resolve(__dirname, '..');
const packageJson = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf-8'));
const appName = (packageJson.build?.productName || packageJson.name || 'seedit').toLowerCase();
const resolveOutDir = (dir) => (isAbsolute(dir) ? dir : join(repoRoot, dir));
const envOutDir = process.env.ELECTRON_FORGE_OUT_DIR;
const candidateRoots = [
envOutDir ? resolveOutDir(envOutDir) : null,
join(repoRoot, 'out'),
join(repoRoot, 'out', 'make'),
join(repoRoot, '..', 'out'),
join(repoRoot, '..', '..', 'out'),
join(repoRoot, 'electron', 'out'),
].filter(Boolean);
// Skip directories that contain helper binaries/app code, not the main executable
// 'resources' contains the app bundle with IPFS binaries in bin/ - don't recurse there
const skipDirs = new Set(['node_modules', '.git', 'bin', 'app', 'resources']);
function findExecutable(dir, platform) {
const entries = readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
if (skipDirs.has(entry.name)) continue;
if (platform === 'darwin' && entry.name.endsWith('.app')) {
const appPath = fullPath;
const exePath = join(appPath, 'Contents', 'MacOS', entry.name.replace('.app', ''));
if (existsSync(exePath)) {
return exePath;
}
}
const result = findExecutable(fullPath, platform);
if (result) return result;
} else if (entry.isFile()) {
// Check if it's an executable
if (platform === 'win32') {
const lowerName = entry.name.toLowerCase();
if (lowerName.endsWith('.exe') && !lowerName.includes('electron') && !lowerName.includes('crashpad')) {
if (lowerName.includes(appName)) {
return fullPath;
}
return fullPath;
}
} else if (platform === 'darwin') {
const stat = statSync(fullPath);
if (stat.isFile() && stat.mode & parseInt('111', 8)) {
const lowerName = entry.name.toLowerCase();
if (!lowerName.includes('helper') && !lowerName.includes('crashpad')) {
if (lowerName.includes(appName)) {
return fullPath;
}
return fullPath;
}
}
} else if (platform === 'linux') {
// Linux executables (AppImage or unpacked)
if (entry.name.endsWith('.AppImage')) {
return fullPath;
}
// Check for executable files (not .so libraries)
const stat = statSync(fullPath);
if (stat.isFile() && stat.mode & parseInt('111', 8) && !entry.name.includes('.so')) {
// Skip helper binaries
const lowerName = entry.name.toLowerCase();
if (!lowerName.includes('chrome') && !lowerName.includes('crashpad')) {
if (lowerName.includes(appName)) {
return fullPath;
}
return fullPath;
}
}
}
}
}
return null;
}
let executable = null;
const checkedDirs = [];
for (const root of candidateRoots) {
if (!existsSync(root)) {
checkedDirs.push(`${root} (missing)`);
continue;
}
const result = findExecutable(root, platform);
checkedDirs.push(root);
if (result) {
executable = result;
break;
}
}
if (!executable) {
console.error('Error: Could not find packaged executable.');
console.error('Platform:', platform);
console.error('Checked directories:', checkedDirs.join(', '));
process.exit(1);
}
console.log(executable);