farmcontrol-ui/scripts/patch-electrobun-src.mjs
Tom Butcher 888c0fe38a
Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
Enhance build configuration and scripts for multi-platform support
- Updated electrobun.config.ts to include environment-based settings for macOS builds.
- Added new build scripts for Windows and macOS, including support for architecture-specific builds.
- Introduced scripts for cleaning build artifacts, managing dependencies, and ensuring core binaries are present.
- Enhanced Jenkinsfile to integrate new build processes and improve artifact management.
- Added new assets for application icons and installer configurations.
- Implemented NSIS and MSI packaging scripts for Windows installer creation.
- Updated package.json with new scripts for development and build processes, including post-installation tasks.
2026-08-02 00:28:33 +01:00

278 lines
8.0 KiB
JavaScript

import {
cpSync,
existsSync,
mkdirSync,
readFileSync,
writeFileSync,
} from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const electrobunDir = path.join(rootDir, "node_modules/electrobun");
const sharedSrcDir = path.join(electrobunDir, "src/shared");
const sharedDistDir = path.join(electrobunDir, "dist/api/shared");
if (!existsSync(sharedDistDir)) {
console.error("patch-electrobun-src: electrobun dist/api/shared not found");
process.exit(1);
}
mkdirSync(sharedSrcDir, { recursive: true });
for (const fileName of [
"cef-version.ts",
"bun-version.ts",
"electrobun-version.ts",
"naming.ts",
"rpc.ts",
]) {
const source = path.join(sharedDistDir, fileName);
const destination = path.join(sharedSrcDir, fileName);
if (existsSync(source)) {
cpSync(source, destination);
}
}
const platformPatch = `import { platform, arch } from "os";
export type SupportedOS = "macos" | "win" | "linux";
export type SupportedArch = "arm64" | "x64";
const platformName = platform();
const archName = arch();
export const OS: SupportedOS = (() => {
switch (platformName) {
case "win32":
return "win";
case "darwin":
return "macos";
case "linux":
return "linux";
default:
throw new Error(\`Unsupported platform: \${platformName}\`);
}
})();
function normalizeForcedArch(value) {
if (value === "arm64" || value === "aarch64") {
return "arm64";
}
if (value === "x64" || value === "amd64") {
return "x64";
}
return null;
}
export const ARCH: SupportedArch = (() => {
const forcedArch = normalizeForcedArch(
process.env.ELECTROBUN_FORCE_ARCH || process.env.ELECTROBUN_TARGET_ARCH,
);
if (forcedArch) {
return forcedArch;
}
if (OS === "win") {
return "x64";
}
switch (archName) {
case "arm64":
return "arm64";
case "x64":
return "x64";
default:
throw new Error(\`Unsupported architecture: \${archName}\`);
}
})();
export const HOST_ARCH: SupportedArch = (() => {
if (OS === "win") {
return "x64";
}
switch (archName) {
case "arm64":
return "arm64";
case "x64":
return "x64";
default:
throw new Error(\`Unsupported architecture: \${archName}\`);
}
})();
export function getPlatformOS(): SupportedOS {
return OS;
}
export function getPlatformArch(): SupportedArch {
return ARCH;
}
`;
writeFileSync(path.join(sharedSrcDir, "platform.ts"), platformPatch);
const templatesDir = path.join(electrobunDir, "src/cli/templates");
mkdirSync(templatesDir, { recursive: true });
writeFileSync(
path.join(templatesDir, "embedded.ts"),
`export function getTemplateNames() {
return [];
}
export function getTemplate(name: string) {
throw new Error(\`Template not available in patched electrobun CLI: \${name}\`);
}
`,
);
const RCEDIT_RESOLVER_FN = `function resolveProjectRceditPkgPath(projectRoot) {
const candidates = [
join(projectRoot, "node_modules/rcedit/package.json"),
join(projectRoot, "node_modules/electrobun/node_modules/rcedit/package.json"),
];
for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}
throw new Error(
"rcedit not found under " + projectRoot + ". Install rcedit in the project (bun add -d rcedit).",
);
}`;
const cliPath = path.join(electrobunDir, "src/cli/index.ts");
let cliSource = readFileSync(cliPath, "utf8");
if (!cliSource.includes("HOST_ARCH")) {
cliSource = cliSource.replace(
'import { OS, ARCH } from "../shared/platform";',
'import { OS, ARCH, HOST_ARCH } from "../shared/platform";',
);
cliSource = cliSource.replace(
"const hostPaths = getPlatformPaths(OS, ARCH);",
"const hostPaths = getPlatformPaths(OS, HOST_ARCH);",
);
}
if (!cliSource.includes("toolPaths")) {
cliSource = cliSource.replace(
"const targetPaths = getPlatformPaths(currentTarget.os, currentTarget.arch);",
"const targetPaths = getPlatformPaths(currentTarget.os, currentTarget.arch);\n\t\tconst toolPaths = getPlatformPaths(currentTarget.os, ARCH !== HOST_ARCH ? HOST_ARCH : ARCH);",
);
cliSource = cliSource.replaceAll("const zstdPath = targetPaths.ZSTD;", "const zstdPath = toolPaths.ZSTD;");
cliSource = cliSource.replaceAll("const bsdiffpath = targetPaths.BSDIFF;", "const bsdiffpath = toolPaths.BSDIFF;");
cliSource = cliSource.replace(
"zigAsarCli = join(targetPaths.BSPATCH).replace(\"bspatch\", \"zig-asar\");",
"zigAsarCli = join(toolPaths.BSPATCH).replace(\"bspatch\", \"zig-asar\");",
);
writeFileSync(cliPath, cliSource);
cliSource = readFileSync(cliPath, "utf8");
}
if (!cliSource.includes("hookBunBinary")) {
cliSource = cliSource.replace(
`const hostPaths = getPlatformPaths(OS, HOST_ARCH);
const result = Bun.spawnSync([hostPaths.BUN_BINARY, hookScript],`,
`const hostPaths = getPlatformPaths(OS, HOST_ARCH);
const hookBunBinary = existsSync(hostPaths.BUN_BINARY)
? hostPaths.BUN_BINARY
: process.execPath;
const result = Bun.spawnSync([hookBunBinary, hookScript],`,
);
cliSource = cliSource.replace(
"console.error(\"Tried to run with bun at:\", hostPaths.BUN_BINARY);",
"console.error(\"Tried to run with bun at:\", hookBunBinary);",
);
writeFileSync(cliPath, cliSource);
cliSource = readFileSync(cliPath, "utf8");
}
if (!cliSource.includes("resolveProjectRceditPkgPath")) {
cliSource = cliSource.replaceAll(
"const rceditPkgPath = require.resolve(\"rcedit/package.json\");",
"const rceditPkgPath = resolveProjectRceditPkgPath(projectRoot);",
);
cliSource = cliSource.replace(
"function getPlatformPaths(",
`${RCEDIT_RESOLVER_FN}
function getPlatformPaths(`,
);
writeFileSync(cliPath, cliSource);
cliSource = readFileSync(cliPath, "utf8");
} else {
cliSource = cliSource.replace(
/function resolveProjectRceditPkgPath\(projectRoot\) \{[\s\S]*?\n\}/m,
RCEDIT_RESOLVER_FN,
);
writeFileSync(cliPath, cliSource);
}
const electrobunCjsPath = path.join(electrobunDir, "bin/electrobun.cjs");
let electrobunCjs = readFileSync(electrobunCjsPath, "utf8");
if (!electrobunCjs.includes("farmcontrol-use-patched-cli")) {
electrobunCjs = electrobunCjs.replace(
`async function main() {
try {
const args = process.argv.slice(2);
const cliPath = await ensureCliBinary();
// Replace this process with the actual CLI
const child = spawn(cliPath, args, {
stdio: 'inherit',
cwd: process.cwd()
});`,
`async function main() {
try {
const args = process.argv.slice(2);
const patchedCliPath = join(electrobunDir, 'src', 'cli', 'index.ts');
if (existsSync(patchedCliPath)) {
// farmcontrol-use-patched-cli: bundled electrobun.exe cannot resolve project rcedit
const bunBinary = process.env.BUN_INSTALL
? join(process.env.BUN_INSTALL, 'bin', 'bun' + binExt)
: 'bun';
const child = spawn(bunBinary, [patchedCliPath, ...args], {
stdio: 'inherit',
cwd: process.cwd(),
env: process.env,
shell: platform === 'win',
});
child.on('exit', (code) => {
process.exit(code || 0);
});
child.on('error', (error) => {
console.error('Failed to start electrobun patched CLI:', error.message);
process.exit(1);
});
return;
}
const cliPath = await ensureCliBinary();
// Replace this process with the actual CLI
const child = spawn(cliPath, args, {
stdio: 'inherit',
cwd: process.cwd()
});`,
);
writeFileSync(electrobunCjsPath, electrobunCjs);
}
const rceditSrc = path.join(rootDir, "node_modules/rcedit");
const rceditDest = path.join(electrobunDir, "node_modules/rcedit");
if (existsSync(rceditSrc)) {
mkdirSync(path.join(electrobunDir, "node_modules"), { recursive: true });
cpSync(rceditSrc, rceditDest, { recursive: true, force: true });
}
const markerPath = path.join(electrobunDir, ".farmcontrol-electrobun-patched");
writeFileSync(markerPath, `patched-at=${new Date().toISOString()}\n`);
console.log("patch-electrobun-src: electrobun src/shared ready for cross-arch builds");