Enhance CEF bundling support and update build scripts
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

- Introduced `bundleCEF` configuration to control CEF bundling in the build process.
- Updated `Jenkinsfile` to support separate builds for CEF and native applications on Windows and macOS.
- Modified `build-macos.mjs` and `finalize-desktop-artifacts.mjs` to incorporate CEF bundling logic and adjust artifact naming accordingly.
- Enhanced logging to indicate CEF bundling status during builds, improving transparency in the build process.
This commit is contained in:
Tom Butcher 2026-08-04 22:49:50 +01:00
parent 482cc5dc86
commit f52ca059a1
6 changed files with 65 additions and 40 deletions

34
Jenkinsfile vendored
View File

@ -105,26 +105,27 @@ def prepareMacBuildWorkspace() {
} }
} }
def buildOnLabel(label, buildCommand) { def buildOnLabel(label, buildCommand, bundleCef = false) {
def cefLabel = bundleCef ? 'cef' : 'native'
return { return {
node(label) { node(label) {
try { try {
stage("Checkout (${label})") { stage("Checkout (${label}/${cefLabel})") {
checkout scm checkout scm
} }
withBun { withBun {
stage("Check Bun (${label})") { stage("Check Bun (${label}/${cefLabel})") {
checkBun() checkBun()
} }
if (label.startsWith('macos')) { if (label.startsWith('macos')) {
stage("Prepare macOS workspace (${label})") { stage("Prepare macOS workspace (${label}/${cefLabel})") {
prepareMacBuildWorkspace() prepareMacBuildWorkspace()
} }
} }
stage("Install Dependencies (${label})") { stage("Install Dependencies (${label}/${cefLabel})") {
if (isUnix()) { if (isUnix()) {
sh 'bun install --frozen-lockfile' sh 'bun install --frozen-lockfile'
} else { } else {
@ -132,16 +133,18 @@ def buildOnLabel(label, buildCommand) {
} }
} }
stage("Write Build Metadata (${label})") { stage("Write Build Metadata (${label}/${cefLabel})") {
writeBuildMetadata() writeBuildMetadata()
} }
stage("Build (${label})") { stage("Build (${label}/${cefLabel})") {
runBuild(buildCommand) withEnv(["ELECTROBUN_BUNDLE_CEF=${bundleCef ? 'true' : 'false'}"]) {
runBuild(buildCommand)
}
} }
} }
stage("Archive Artifacts (${label})") { stage("Archive Artifacts (${label}/${cefLabel})") {
archiveArtifacts artifacts: 'app_dist/farmcontrol-*', fingerprint: true archiveArtifacts artifacts: 'app_dist/farmcontrol-*', fingerprint: true
} }
} finally { } finally {
@ -151,8 +154,8 @@ def buildOnLabel(label, buildCommand) {
} }
} }
def buildMacOnLabel(label, targetArch) { def buildMacOnLabel(label, targetArch, bundleCef = false) {
return buildOnLabel(label, "ELECTROBUN_TARGET_ARCH=${targetArch} bun run build:app:mac") return buildOnLabel(label, "ELECTROBUN_TARGET_ARCH=${targetArch} bun run build:app:mac", bundleCef)
} }
def setBuildNameFromPackageVersion() { def setBuildNameFromPackageVersion() {
@ -177,9 +180,12 @@ try {
setBuildNameFromPackageVersion() setBuildNameFromPackageVersion()
parallel( parallel(
'Windows Build': buildOnLabel('windows', 'bun run build:app'), 'Windows Build': buildOnLabel('windows', 'bun run build:app', false),
'MacOS x64 Build': buildMacOnLabel('macos', 'x64'), 'Windows CEF Build': buildOnLabel('windows', 'bun run build:app', true),
'MacOS arm64 Build': buildMacOnLabel('macos', 'arm64'), 'MacOS x64 Build': buildMacOnLabel('macos', 'x64', false),
'MacOS x64 CEF Build': buildMacOnLabel('macos', 'x64', true),
'MacOS arm64 Build': buildMacOnLabel('macos', 'arm64', false),
'MacOS arm64 CEF Build': buildMacOnLabel('macos', 'arm64', true),
'Ubuntu Deploy': { deploy() } 'Ubuntu Deploy': { deploy() }
) )

View File

@ -5,6 +5,10 @@ const packageJson = JSON.parse(readFileSync("./package.json", "utf8"));
const buildEnv = process.env.ELECTROBUN_BUILD_ENV || "dev"; const buildEnv = process.env.ELECTROBUN_BUILD_ENV || "dev";
const isStable = buildEnv === "stable"; const isStable = buildEnv === "stable";
const canCodesign = Boolean(process.env.ELECTROBUN_DEVELOPER_ID); const canCodesign = Boolean(process.env.ELECTROBUN_DEVELOPER_ID);
const bundleCEF = ["1", "true", "yes"].includes(
String(process.env.ELECTROBUN_BUNDLE_CEF || "").toLowerCase(),
);
const defaultRenderer = bundleCEF ? "cef" : "native";
const targetOs = const targetOs =
process.env.ELECTROBUN_OS || process.env.ELECTROBUN_OS ||
(process.platform === "darwin" (process.platform === "darwin"
@ -47,7 +51,8 @@ export default {
watch: ["scripts", "src"], watch: ["scripts", "src"],
watchIgnore: ["dist/**", "build/**", "app_dist/**"], watchIgnore: ["dist/**", "build/**", "app_dist/**"],
mac: { mac: {
bundleCEF: false, bundleCEF,
defaultRenderer,
icons: "assets/icon.iconset", icons: "assets/icon.iconset",
createDmg: false, createDmg: false,
codesign: isStable && canCodesign, codesign: isStable && canCodesign,
@ -55,10 +60,12 @@ export default {
}, },
linux: { linux: {
bundleCEF: false, bundleCEF: false,
defaultRenderer: "native",
icon: "assets/icon.png", icon: "assets/icon.png",
}, },
win: { win: {
bundleCEF: false, bundleCEF,
defaultRenderer,
icon: "assets/icon.iconset/icon_256x256.png", icon: "assets/icon.iconset/icon_256x256.png",
}, },
}, },

View File

@ -1,13 +1,14 @@
import { spawnSync } from "node:child_process"; import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import path from "node:path"; import path from "node:path";
import { getReleaseArch } from "./release-artifact-utils.mjs"; import { getReleaseArch, isBundleCefEnabled } from "./release-artifact-utils.mjs";
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const hostArch = getReleaseArch(process.arch); const hostArch = getReleaseArch(process.arch);
const targetArchs = process.env.ELECTROBUN_TARGET_ARCH const targetArchs = process.env.ELECTROBUN_TARGET_ARCH
? [getReleaseArch(process.env.ELECTROBUN_TARGET_ARCH)] ? [getReleaseArch(process.env.ELECTROBUN_TARGET_ARCH)]
: ["arm64", "x64"]; : ["arm64", "x64"];
const bundleCef = isBundleCefEnabled();
function canRunUnderArch(archFlag) { function canRunUnderArch(archFlag) {
const result = spawnSync("arch", [archFlag, "true"], { const result = spawnSync("arch", [archFlag, "true"], {
@ -72,6 +73,7 @@ const orderedTargets = [...targetArchs].sort((a, b) => {
}); });
console.log(`Host architecture: ${hostArch}`); console.log(`Host architecture: ${hostArch}`);
console.log(`CEF bundling: ${bundleCef ? "enabled" : "disabled"}`);
console.log(`Build order: ${orderedTargets.join(", ")}`); console.log(`Build order: ${orderedTargets.join(", ")}`);
let builtCount = 0; let builtCount = 0;
@ -85,9 +87,9 @@ for (const targetArch of orderedTargets) {
continue; continue;
} }
console.log(`\n=== Building macOS ${targetArch} (host ${hostArch}) ===`); console.log(`\n=== Building macOS ${targetArch} (host ${hostArch}, cef=${bundleCef}) ===`);
console.log( console.log(
`ELECTROBUN_BUILD_ENV=stable ELECTROBUN_OS=macos ELECTROBUN_ARCH=${targetArch}\n`, `ELECTROBUN_BUILD_ENV=stable ELECTROBUN_OS=macos ELECTROBUN_ARCH=${targetArch} ELECTROBUN_BUNDLE_CEF=${bundleCef}\n`,
); );
run(invocation.command, invocation.args, { env: invocation.env }); run(invocation.command, invocation.args, { env: invocation.env });

View File

@ -13,7 +13,8 @@ import { fileURLToPath } from 'node:url'
import { import {
getReleaseArch, getReleaseArch,
getReleaseArtifactName, getReleaseArtifactName,
getReleaseVersion getReleaseVersion,
isBundleCefEnabled
} from './release-artifact-utils.mjs' } from './release-artifact-utils.mjs'
import { import {
cleanExpandedWindowsApp, cleanExpandedWindowsApp,
@ -40,12 +41,17 @@ const targetOs =
const buildArch = getReleaseArch(process.env.ELECTROBUN_ARCH || process.arch) const buildArch = getReleaseArch(process.env.ELECTROBUN_ARCH || process.arch)
const version = const version =
process.env.ELECTROBUN_APP_VERSION || getReleaseVersion(packageJson) process.env.ELECTROBUN_APP_VERSION || getReleaseVersion(packageJson)
const bundleCef = isBundleCefEnabled()
const artifactDir = const artifactDir =
process.env.ELECTROBUN_ARTIFACT_DIR || path.join(rootDir, 'app_dist') process.env.ELECTROBUN_ARTIFACT_DIR || path.join(rootDir, 'app_dist')
const identifier = const identifier =
process.env.ELECTROBUN_APP_IDENTIFIER || 'com.tombutcher.farmcontrol' process.env.ELECTROBUN_APP_IDENTIFIER || 'com.tombutcher.farmcontrol'
const artifactPrefix = `farmcontrol-${version}-` const artifactPrefix = `farmcontrol-${version}-`
function artifactName(arch, ext) {
return getReleaseArtifactName(version, arch, ext, { cef: bundleCef })
}
function getBuildRoot() { function getBuildRoot() {
const electrobunBuildDir = process.env.ELECTROBUN_BUILD_DIR const electrobunBuildDir = process.env.ELECTROBUN_BUILD_DIR
if (!electrobunBuildDir) { if (!electrobunBuildDir) {
@ -159,10 +165,7 @@ function publishArtifact(sourcePath, arch, ext) {
} }
mkdirSync(artifactDir, { recursive: true }) mkdirSync(artifactDir, { recursive: true })
const destination = path.join( const destination = path.join(artifactDir, artifactName(arch, ext))
artifactDir,
getReleaseArtifactName(version, arch, ext)
)
cpSync(sourcePath, destination) cpSync(sourcePath, destination)
console.log(`Published ${destination}`) console.log(`Published ${destination}`)
return destination return destination
@ -408,10 +411,7 @@ async function buildMacDmg(appBundlePath, arch) {
) )
} }
const dmgPath = path.join( const dmgPath = path.join(artifactDir, artifactName(arch, 'dmg'))
artifactDir,
getReleaseArtifactName(version, arch, 'dmg')
)
mkdirSync(artifactDir, { recursive: true }) mkdirSync(artifactDir, { recursive: true })
@ -463,10 +463,7 @@ function cleanMacBuildDir(arch) {
} }
function buildMacPkg(appBundlePath, arch) { function buildMacPkg(appBundlePath, arch) {
const pkgPath = path.join( const pkgPath = path.join(artifactDir, artifactName(arch, 'pkg'))
artifactDir,
getReleaseArtifactName(version, arch, 'pkg')
)
const result = spawnSync( const result = spawnSync(
'pkgbuild', 'pkgbuild',
@ -512,10 +509,7 @@ function cleanWinBuildDir(arch) {
function buildWindowsNsis(appDir, arch) { function buildWindowsNsis(appDir, arch) {
const scriptPath = path.join(rootDir, 'scripts/build-windows-nsis.ps1') const scriptPath = path.join(rootDir, 'scripts/build-windows-nsis.ps1')
const exePath = path.join( const exePath = path.join(artifactDir, artifactName(arch, 'exe'))
artifactDir,
getReleaseArtifactName(version, arch, 'exe')
)
const buildInfoPath = path.join(rootDir, 'src/buildInfo.json') const buildInfoPath = path.join(rootDir, 'src/buildInfo.json')
const buildInfo = existsSync(buildInfoPath) const buildInfo = existsSync(buildInfoPath)
? JSON.parse(readFileSync(buildInfoPath, 'utf8')) ? JSON.parse(readFileSync(buildInfoPath, 'utf8'))
@ -579,6 +573,10 @@ if (buildEnv === 'dev') {
} }
async function main() { async function main() {
console.log(
`finalize-desktop-artifacts: os=${targetOs} arch=${buildArch} cef=${bundleCef} version=${version}`
)
if (targetOs === 'macos') { if (targetOs === 'macos') {
const appBundle = findMacAppBundle(buildArch) const appBundle = findMacAppBundle(buildArch)

View File

@ -12,6 +12,17 @@ export function getReleaseArch(platformArch = process.arch) {
return platformArch; return platformArch;
} }
export function getReleaseArtifactName(version, arch, ext) { export function isBundleCefEnabled(
return `farmcontrol-${version}-${arch}.${ext}`; value = process.env.ELECTROBUN_BUNDLE_CEF,
) {
return ["1", "true", "yes"].includes(String(value || "").toLowerCase());
}
export function getReleaseArtifactName(version, arch, ext, options = {}) {
const cef =
typeof options === "boolean"
? options
: Boolean(options.cef ?? isBundleCefEnabled());
const cefSuffix = cef ? "-cef" : "";
return `farmcontrol-${version}-${arch}${cefSuffix}.${ext}`;
} }

View File

@ -1,7 +1,7 @@
import { spawnSync } from "node:child_process"; import { spawnSync } from "node:child_process";
import path from "node:path"; import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url"; import { fileURLToPath, pathToFileURL } from "node:url";
import { getReleaseArch } from "./release-artifact-utils.mjs"; import { getReleaseArch, isBundleCefEnabled } from "./release-artifact-utils.mjs";
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const electrobunCli = path.join( const electrobunCli = path.join(
@ -15,6 +15,7 @@ const targetArch = getReleaseArch(
process.env.ELECTROBUN_FORCE_ARCH || process.env.ELECTROBUN_FORCE_ARCH ||
process.arch, process.arch,
); );
const bundleCef = isBundleCefEnabled();
const patchResult = spawnSync("bun", [path.join(rootDir, "scripts/patch-electrobun-src.mjs")], { const patchResult = spawnSync("bun", [path.join(rootDir, "scripts/patch-electrobun-src.mjs")], {
cwd: rootDir, cwd: rootDir,
@ -82,7 +83,7 @@ const env = {
}; };
console.log( console.log(
`run-electrobun-build: env=${buildEnv} os=macos arch=${targetArch} (host ${getReleaseArch(process.arch)})`, `run-electrobun-build: env=${buildEnv} os=macos arch=${targetArch} cef=${bundleCef} (host ${getReleaseArch(process.arch)})`,
); );
const result = spawnSync( const result = spawnSync(