Files
TrueGrowth/scripts/run-branded-electron-dev.mjs

161 lines
4.4 KiB
JavaScript

import { spawn, spawnSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const appRoot = path.resolve(__dirname, '..');
const brandName = 'TrueGrowth';
const bundleIdentifier = 'ai.truegrowth.desktop.dev';
const cacheRoot = path.join(appRoot, '.truegrowth-desktop');
const brandedAppPath = path.join(cacheRoot, `${brandName}.app`);
const mainEntry = path.join(appRoot, 'apps/electron/main.mjs');
const cacheFormatVersion = '2';
const appVersion =
JSON.parse(fs.readFileSync(path.join(appRoot, 'package.json'), 'utf8'))
.version || '0.0.0';
const isMac = process.platform === 'darwin';
const prepareOnly = process.argv.includes('--prepare-only');
function runPlistBuddy(args) {
const result = spawnSync('/usr/libexec/PlistBuddy', args, {
encoding: 'utf8',
});
if (result.status !== 0) {
const details = result.stderr || result.stdout || args.join(' ');
throw new Error(`Failed to update Electron app metadata: ${details}`);
}
}
function setPlistValue(plistPath, key, value) {
const escapedValue = String(value).replace(/"/g, '\\"');
const setCommand = `Set :${key} "${escapedValue}"`;
const addCommand = `Add :${key} string "${escapedValue}"`;
const setResult = spawnSync(
'/usr/libexec/PlistBuddy',
['-c', setCommand, plistPath],
{
encoding: 'utf8',
}
);
if (setResult.status === 0) {
return;
}
runPlistBuddy(['-c', addCommand, plistPath]);
}
function signMacApp(appPath) {
const verifyResult = spawnSync(
'/usr/bin/codesign',
['--verify', '--deep', '--strict', appPath],
{
encoding: 'utf8',
}
);
if (verifyResult.status === 0) {
return;
}
const signResult = spawnSync(
'/usr/bin/codesign',
['--force', '--deep', '--sign', '-', appPath],
{
encoding: 'utf8',
}
);
if (signResult.status !== 0) {
const details = signResult.stderr || signResult.stdout || appPath;
throw new Error(`Failed to sign branded Electron app: ${details}`);
}
}
function getElectronExecutable() {
const result = spawnSync(process.execPath, ['-p', "require('electron')"], {
cwd: appRoot,
encoding: 'utf8',
});
if (result.status !== 0) {
throw new Error(result.stderr || 'Unable to resolve Electron executable.');
}
return result.stdout.trim();
}
function ensureBrandedMacApp(electronExecutable) {
const sourceAppPath = electronExecutable.slice(
0,
electronExecutable.indexOf('.app') + '.app'.length
);
const sourceInfoPath = path.join(sourceAppPath, 'Contents/Info.plist');
const sourceVersion = `${fs.statSync(sourceInfoPath).mtimeMs.toFixed(
0
)}:${cacheFormatVersion}`;
const stampPath = path.join(cacheRoot, 'electron-source-version.txt');
const cachedVersion = fs.existsSync(stampPath)
? fs.readFileSync(stampPath, 'utf8')
: '';
if (!fs.existsSync(brandedAppPath) || cachedVersion !== sourceVersion) {
fs.rmSync(brandedAppPath, { recursive: true, force: true });
fs.mkdirSync(cacheRoot, { recursive: true });
fs.cpSync(sourceAppPath, brandedAppPath, {
recursive: true,
verbatimSymlinks: true,
});
fs.writeFileSync(stampPath, sourceVersion);
}
const infoPath = path.join(brandedAppPath, 'Contents/Info.plist');
setPlistValue(infoPath, 'CFBundleName', brandName);
setPlistValue(infoPath, 'CFBundleDisplayName', brandName);
setPlistValue(infoPath, 'CFBundleIdentifier', bundleIdentifier);
setPlistValue(infoPath, 'CFBundleShortVersionString', appVersion);
setPlistValue(infoPath, 'CFBundleVersion', appVersion);
setPlistValue(
infoPath,
'LSApplicationCategoryType',
'public.app-category.productivity'
);
signMacApp(brandedAppPath);
return path.join(brandedAppPath, 'Contents/MacOS/Electron');
}
function launch(executablePath) {
const child = spawn(executablePath, [mainEntry], {
cwd: appRoot,
env: {
...process.env,
TRUEGROWTH_BRANDED_ELECTRON_DEV: '1',
},
stdio: 'inherit',
});
child.on('exit', (code, signal) => {
if (typeof code === 'number') {
process.exit(code);
}
if (signal) {
process.kill(process.pid, signal);
}
});
}
const electronExecutable = getElectronExecutable();
const executablePath = isMac
? ensureBrandedMacApp(electronExecutable)
: electronExecutable;
if (prepareOnly) {
console.log(executablePath);
} else {
launch(executablePath);
}