Compare commits

..

2 Commits

Author SHA1 Message Date
eb49ba7ce3 Add DMG background images and update asset management
All checks were successful
farmcontrol/farmcontrol-server/pipeline/head This commit looks good
- Introduced new background images for macOS DMG packaging, including standard and retina versions.
- Updated the finalize-desktop-artifacts.mjs script to check for the presence of these background images, enhancing error handling.
- Refactored the ensureMacDmgAssets function to improve asset validation and streamline the DMG building process.
2026-08-01 23:14:13 +01:00
f63b1fbc37 Enhance macOS DMG generation with asset management
- Added functionality to ensure the presence of DMG assets, including a volume icon and background image, using Jimp for image creation.
- Refactored the DMG building process to incorporate these assets, improving the visual presentation of the generated DMG.
- Updated the buildMacDmgWithCreateDmg function to accept additional parameters for app bundle name and DMG assets, enhancing flexibility.
2026-08-01 23:11:19 +01:00
3 changed files with 103 additions and 17 deletions

BIN
assets/dmg/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -189,6 +189,52 @@ function cleanStagingArtifacts(keepNames) {
}
}
const MAC_DMG_ASSETS_DIR = path.join(rootDir, "packaging/macos/dmg");
const MAC_DMG_BACKGROUND_PATH = path.join(rootDir, "assets/dmg/background.png");
const MAC_DMG_BACKGROUND_RETINA_PATH = path.join(
rootDir,
"assets/dmg/background@2x.png",
);
const MAC_DMG_APP_NAME = "Farm Control Server.app";
const MAC_DMG_WINDOW_WIDTH = 380;
const MAC_DMG_WINDOW_HEIGHT = 540;
function ensureMacDmgAssets() {
mkdirSync(MAC_DMG_ASSETS_DIR, { recursive: true });
const voliconPath = path.join(MAC_DMG_ASSETS_DIR, "volicon.icns");
const iconsetPath = path.join(rootDir, "assets/icon.iconset");
if (!existsSync(voliconPath) && existsSync(iconsetPath)) {
const iconutil = spawnSync(
"iconutil",
["-c", "icns", "-o", voliconPath, iconsetPath],
{ stdio: "inherit" },
);
if (iconutil.status !== 0) {
throw new Error(
`iconutil failed to create DMG volicon with exit code ${iconutil.status ?? 1}`,
);
}
}
if (!existsSync(MAC_DMG_BACKGROUND_PATH)) {
throw new Error(`DMG background not found at ${MAC_DMG_BACKGROUND_PATH}`);
}
if (!existsSync(MAC_DMG_BACKGROUND_RETINA_PATH)) {
console.warn(
`finalize-desktop-artifacts: retina DMG background not found at ${MAC_DMG_BACKGROUND_RETINA_PATH}`,
);
}
return {
volicon: existsSync(voliconPath) ? voliconPath : null,
background: MAC_DMG_BACKGROUND_PATH,
};
}
function findCreateDmgCommand() {
const which = spawnSync("which", ["create-dmg"], { encoding: "utf8" });
if (which.status === 0 && which.stdout.trim()) {
@ -207,24 +253,50 @@ function findCreateDmgCommand() {
return null;
}
function buildMacDmgWithCreateDmg(createDmg, dmgPath, sourceFolder) {
function buildMacDmgWithCreateDmg(
createDmg,
dmgPath,
sourceFolder,
appBundleName,
dmgAssets,
) {
rmSync(dmgPath, { force: true });
const result = spawnSync(
createDmg,
[
"--volname",
"Farm Control Server",
"--app-drop-link",
"480",
"170",
"--format",
"UDZO",
dmgPath,
sourceFolder,
],
{ stdio: "inherit" },
);
const args = [
"--volname",
"Farm Control Server",
"--window-pos",
"200",
"120",
"--window-size",
String(MAC_DMG_WINDOW_WIDTH),
String(MAC_DMG_WINDOW_HEIGHT),
"--icon-size",
"120",
"--icon",
appBundleName,
"98",
"270",
"--hide-extension",
appBundleName,
"--app-drop-link",
"282",
"270",
"--format",
"UDZO",
];
if (dmgAssets.volicon) {
args.push("--volicon", dmgAssets.volicon);
}
if (dmgAssets.background) {
args.push("--background", dmgAssets.background);
}
args.push(dmgPath, sourceFolder);
const result = spawnSync(createDmg, args, { stdio: "inherit" });
if (result.status !== 0) {
throw new Error(`create-dmg failed with exit code ${result.status ?? 1}`);
@ -269,6 +341,13 @@ async function buildMacDmg(appBundlePath, arch) {
);
}
const appBundleName = path.basename(appBundlePath);
if (appBundleName !== MAC_DMG_APP_NAME) {
console.warn(
`finalize-desktop-artifacts: expected ${MAC_DMG_APP_NAME}, found ${appBundleName}`,
);
}
const dmgPath = path.join(
artifactDir,
getReleaseArtifactName(version, arch, "dmg"),
@ -297,9 +376,16 @@ async function buildMacDmg(appBundlePath, arch) {
}
let builtDmgPath;
const dmgAssets = ensureMacDmgAssets();
try {
builtDmgPath = buildMacDmgWithCreateDmg(createDmg, dmgPath, sourceFolder);
builtDmgPath = buildMacDmgWithCreateDmg(
createDmg,
dmgPath,
sourceFolder,
appBundleName,
dmgAssets,
);
} finally {
if (!useDirectSource) {
rmSync(stagingDir, { recursive: true, force: true });