All checks were successful
farmcontrol/farmcontrol-server/pipeline/head This commit looks good
- Updated the NSIS installer script to replace references from `launcher.exe` to `farmcontrol-server.exe`, ensuring consistency with the new executable naming. - Removed the `apply-windows-launcher-icon.mjs` script and integrated its functionality into a new script, `prepare-windows-app-binaries.mjs`, which handles branding and icon embedding for Windows app binaries. - Enhanced the finalization process of desktop artifacts to utilize the new script for preparing Windows app binaries, improving the overall build workflow.
169 lines
5.2 KiB
JavaScript
169 lines
5.2 KiB
JavaScript
import {
|
|
cpSync,
|
|
existsSync,
|
|
readFileSync,
|
|
renameSync,
|
|
rmSync,
|
|
writeFileSync,
|
|
} from "node:fs";
|
|
import path from "node:path";
|
|
import { spawnSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
|
/** User-facing Windows app entry point (Electrobun launcher). */
|
|
export const WINDOWS_APP_EXECUTABLE = "farmcontrol-server.exe";
|
|
|
|
/**
|
|
* Bun runtime filename embedded in the Electrobun launcher binary.
|
|
* Must stay exactly 7 characters (same length as "bun.exe") for binary patching.
|
|
*/
|
|
export const WINDOWS_RUNTIME_EXECUTABLE = "farm.exe";
|
|
|
|
const LAUNCHER_BINARY = "launcher.exe";
|
|
const LEGACY_RUNTIME_BINARY = "bun.exe";
|
|
const LAUNCHER_RUNTIME_REFERENCE = Buffer.from(`${LEGACY_RUNTIME_BINARY}\0`);
|
|
const PATCHED_RUNTIME_REFERENCE = Buffer.from(`${WINDOWS_RUNTIME_EXECUTABLE}\0`);
|
|
|
|
function ensureIconIco() {
|
|
const iconPath = path.join(rootDir, "assets/icon.ico");
|
|
if (existsSync(iconPath)) {
|
|
return iconPath;
|
|
}
|
|
|
|
const sources = [
|
|
path.join(rootDir, "assets/farmcontrolhosticon.png"),
|
|
path.join(rootDir, "assets/icon.png"),
|
|
];
|
|
const source = sources.find((candidate) => existsSync(candidate));
|
|
if (!source) {
|
|
throw new Error(
|
|
"prepare-windows-app-binaries: assets/icon.ico not found and no source PNG to generate it",
|
|
);
|
|
}
|
|
|
|
console.log(`prepare-windows-app-binaries: generating icon.ico from ${source}`);
|
|
const result = spawnSync(
|
|
"bun",
|
|
[path.join(rootDir, "scripts/prepare-app-icons.mjs"), source],
|
|
{ cwd: rootDir, stdio: "inherit", env: process.env },
|
|
);
|
|
|
|
if (result.status !== 0 || !existsSync(iconPath)) {
|
|
throw new Error("prepare-windows-app-binaries: failed to generate assets/icon.ico");
|
|
}
|
|
|
|
return iconPath;
|
|
}
|
|
|
|
function patchLauncherRuntimeReference(launcherPath) {
|
|
const binary = readFileSync(launcherPath);
|
|
const index = binary.indexOf(LAUNCHER_RUNTIME_REFERENCE);
|
|
if (index === -1) {
|
|
if (binary.includes(PATCHED_RUNTIME_REFERENCE)) {
|
|
return;
|
|
}
|
|
|
|
throw new Error(
|
|
`prepare-windows-app-binaries: could not find ${LEGACY_RUNTIME_BINARY} reference in ${launcherPath}`,
|
|
);
|
|
}
|
|
|
|
PATCHED_RUNTIME_REFERENCE.copy(binary, index);
|
|
writeFileSync(launcherPath, binary);
|
|
console.log(
|
|
`prepare-windows-app-binaries: patched ${path.basename(launcherPath)} to spawn ${WINDOWS_RUNTIME_EXECUTABLE}`,
|
|
);
|
|
}
|
|
|
|
function renameWindowsRuntime(binDir) {
|
|
const legacyRuntimePath = path.join(binDir, LEGACY_RUNTIME_BINARY);
|
|
const runtimePath = path.join(binDir, WINDOWS_RUNTIME_EXECUTABLE);
|
|
|
|
if (existsSync(runtimePath)) {
|
|
return runtimePath;
|
|
}
|
|
|
|
if (!existsSync(legacyRuntimePath)) {
|
|
throw new Error(
|
|
`prepare-windows-app-binaries: neither ${LEGACY_RUNTIME_BINARY} nor ${WINDOWS_RUNTIME_EXECUTABLE} found in ${binDir}`,
|
|
);
|
|
}
|
|
|
|
renameSync(legacyRuntimePath, runtimePath);
|
|
console.log(
|
|
`prepare-windows-app-binaries: renamed ${LEGACY_RUNTIME_BINARY} -> ${WINDOWS_RUNTIME_EXECUTABLE}`,
|
|
);
|
|
return runtimePath;
|
|
}
|
|
|
|
function createBrandedAppExecutable(binDir) {
|
|
const launcherPath = path.join(binDir, LAUNCHER_BINARY);
|
|
const appExecutablePath = path.join(binDir, WINDOWS_APP_EXECUTABLE);
|
|
|
|
if (!existsSync(launcherPath)) {
|
|
if (existsSync(appExecutablePath)) {
|
|
patchLauncherRuntimeReference(appExecutablePath);
|
|
return appExecutablePath;
|
|
}
|
|
|
|
throw new Error(
|
|
`prepare-windows-app-binaries: ${LAUNCHER_BINARY} not found in ${binDir}`,
|
|
);
|
|
}
|
|
|
|
patchLauncherRuntimeReference(launcherPath);
|
|
cpSync(launcherPath, appExecutablePath);
|
|
rmSync(launcherPath, { force: true });
|
|
console.log(
|
|
`prepare-windows-app-binaries: created ${WINDOWS_APP_EXECUTABLE} and removed ${LAUNCHER_BINARY}`,
|
|
);
|
|
return appExecutablePath;
|
|
}
|
|
|
|
async function applyWindowsExecutableMetadata(exePath, iconPath) {
|
|
const rcedit = (await import("rcedit")).default;
|
|
await rcedit(exePath, {
|
|
icon: iconPath,
|
|
"version-string": {
|
|
CompanyName: "Tom Butcher",
|
|
FileDescription: "Farm Control Server",
|
|
ProductName: "Farm Control Server",
|
|
OriginalFilename: path.basename(exePath),
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Brand Windows app binaries for install: farmcontrol-server.exe entry point,
|
|
* farm.exe runtime (patched launcher reference), and embedded icons.
|
|
*/
|
|
export async function prepareWindowsAppBinaries(appDir) {
|
|
if (process.platform !== "win32") {
|
|
console.log("prepare-windows-app-binaries: skipped (not Windows)");
|
|
return;
|
|
}
|
|
|
|
const binDir = path.join(appDir, "bin");
|
|
if (!existsSync(binDir)) {
|
|
throw new Error(`prepare-windows-app-binaries: bin directory not found: ${binDir}`);
|
|
}
|
|
|
|
const iconPath = ensureIconIco();
|
|
const runtimePath = renameWindowsRuntime(binDir);
|
|
const appExecutablePath = createBrandedAppExecutable(binDir);
|
|
|
|
for (const exePath of [appExecutablePath, runtimePath]) {
|
|
console.log(`prepare-windows-app-binaries: embedding icon into ${exePath}`);
|
|
try {
|
|
await applyWindowsExecutableMetadata(exePath, iconPath);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
throw new Error(
|
|
`prepare-windows-app-binaries: failed to update ${path.basename(exePath)}: ${message}`,
|
|
);
|
|
}
|
|
}
|
|
}
|