From db0446da9ce32a8d8fa868ee474e328758a50bce Mon Sep 17 00:00:00 2001 From: Brendonovich <14191578+Brendonovich@users.noreply.github.com> Date: Tue, 3 Nov 2026 14:44:43 +0000 Subject: [PATCH] feat(desktop): add a custom macOS DMG background The build job packs the disk image layout (background, icon positions and volume icon) around an empty app folder, and sign-desktop-macos.sh puts the stapled app in its place instead of making a plain hdiutil image. --- .github/release/sign-desktop-macos.sh | 26 +++++++++++++----- .../desktop/electron-builder.config.test.ts | 16 +++++++++-- packages/desktop/electron-builder.config.ts | 15 +++++++++- packages/desktop/icons/dmg/background.png | Bin 0 -> 5748 bytes packages/desktop/icons/dmg/background@2x.png | Bin 0 -> 17589 bytes packages/desktop/script/pack.ts | 11 +++++++- 6 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 packages/desktop/icons/dmg/background.png create mode 100644 packages/desktop/icons/dmg/background@2x.png diff --git a/.github/release/sign-desktop-macos.sh b/.github/release/sign-desktop-macos.sh index 0742b888271..17519546f8b 100755 --- a/.github/release/sign-desktop-macos.sh +++ b/.github/release/sign-desktop-macos.sh @@ -1,15 +1,16 @@ #!/usr/bin/env bash # sign-desktop-macos.sh ...: signs each unsigned .app inside-out with hardened runtime, # notarizes them in one submission, staples them, and only then builds opencode-desktop-mac-.{zip,dmg,app.tar.gz} -# from the stapled apps. Dry runs sign ad-hoc and skip notarization. Dev and beta trade larger files for faster -# packaging (zip and dmg at zlib level 1) and skip the .app.tar.gz, which only the retired Tauri updater feed read. +# from the stapled apps, putting each app into the disk image layout packed next to its tarball. Dry runs sign ad-hoc +# and skip notarization. Dev and beta trade larger files for faster packaging (zip and dmg at zlib level 1) and skip +# the .app.tar.gz, which only the retired Tauri updater feed read. # Notarization reads APPLE_NOTARY_KEY (the .p8 contents), APPLE_NOTARY_KEY_ID and APPLE_NOTARY_ISSUER_ID. . "$(dirname "$0")/lib.sh" out=$1 shift mkdir -p "$out" -apps=$(mktemp -d) +apps=$(mktemp -d) layouts=$(mktemp -d) identity=- timestamp=--timestamp=none zip_level=() dmg_level=() preview=false case "$OPENCODE_CHANNEL" in dev | beta) preview=true ;; esac @@ -36,6 +37,7 @@ for tarball in "$@"; do die "Expected exactly one .app bundle in $tarball" fi app=$(bundle "$arch") + cp "${tarball%.app.tar}.layout.dmg" "$layouts/$arch.dmg" # Every nested Mach-O file and bundle, deepest first, then the app itself. list=$(mktemp) @@ -79,10 +81,20 @@ for dir in "$apps"/*; do spctl --assess --type execute --verbose=2 "$app" fi ditto -c -k ${zip_level[@]+"${zip_level[@]}"} --sequesterRsrc --keepParent "$app" "$out/opencode-desktop-mac-$arch.zip" - staging=$(mktemp -d) - ditto "$app" "$staging/$name" - ln -s /Applications "$staging/Applications" - hdiutil create -volname "${name%.app}" -srcfolder "$staging" -fs HFS+ -format UDZO ${dmg_level[@]+"${dmg_level[@]}"} -ov "$out/opencode-desktop-mac-$arch.dmg" + # The layout holds the background, icon positions and volume icon around an empty app folder. Only that folder + # and the Applications link may be visible, so the image ships nothing but the stapled app beside the link. + hdiutil convert "$layouts/$arch.dmg" -format UDRW -o "$layouts/$arch-rw.dmg" + volume=$(mktemp -d) + hdiutil attach "$layouts/$arch-rw.dmg" -nobrowse -noautoopen -mountpoint "$volume" + if [ "$(ls "$volume")" != "$(printf 'Applications\n%s' "$name")" ] || [ "$(readlink "$volume/Applications")" != /Applications ] || + [ -n "$(ls -A "$volume/$name")" ]; then + die "Unexpected disk image layout: $(ls -A "$volume")" + fi + rmdir "$volume/$name" + ditto "$app" "$volume/$name" + hdiutil detach "$volume" + hdiutil resize -sectors min "$layouts/$arch-rw.dmg" + hdiutil convert "$layouts/$arch-rw.dmg" -format UDZO ${dmg_level[@]+"${dmg_level[@]}"} -ov -o "$out/opencode-desktop-mac-$arch.dmg" if [ "$preview" = true ]; then continue; fi COPYFILE_DISABLE=1 tar --no-mac-metadata -czf "$out/opencode-desktop-mac-$arch.app.tar.gz" -C "$dir" "$name" done diff --git a/packages/desktop/electron-builder.config.test.ts b/packages/desktop/electron-builder.config.test.ts index 3e29318dcac..2898101bdb1 100644 --- a/packages/desktop/electron-builder.config.test.ts +++ b/packages/desktop/electron-builder.config.test.ts @@ -88,12 +88,24 @@ test("legacy Linux launcher stays hidden", async () => { expect(desktop).toContain("NoDisplay=true") }) -test.each(channels)("macOS bundle for $channel", ({ channel }) => { +test.each(channels)("macOS bundle for $channel", async ({ channel }) => { const config = load(channel) expect(config.mac?.icon).toBe(channel === "prod" ? "resources/icons/icon.icon" : "resources/icons/icon.icns") expect(config.dmg?.icon).toBe(channel === "prod" ? "resources/icons/icon.icns" : undefined) - // The prod override must keep the rest of the shared macOS settings. + // The prod overrides must keep the rest of the shared macOS settings and disk image layout. expect(config.mac?.target).toEqual(["dmg", "zip"]) + expect(config.dmg).toMatchObject({ + title: "${productName}", + background: "icons/dmg/background.png", + contents: [ + { x: 180, y: 210, type: "file" }, + { x: 480, y: 210, type: "link", path: "/Applications" }, + ], + }) + + // electron-builder combines the background with its @2x image only when both exist; otherwise Retina blurs it. + for (const file of ["icons/dmg/background.png", "icons/dmg/background@2x.png"]) + expect(await Bun.file(path.join(import.meta.dirname, file)).exists(), file).toBe(true) for (const key of [ "NSMicrophoneUsageDescription", diff --git a/packages/desktop/electron-builder.config.ts b/packages/desktop/electron-builder.config.ts index d147662ffd3..cb88c04fa3b 100644 --- a/packages/desktop/electron-builder.config.ts +++ b/packages/desktop/electron-builder.config.ts @@ -172,6 +172,19 @@ const getBase = (appId: string): Configuration => { }, target: ["dmg", "zip"], }, + // script/pack.ts builds this layout around an empty app folder, and sign-desktop-macos.sh puts the notarized app + // in its place. Finder draws the icons and their labels, always black, over the background, so it stays light. + dmg: { + title: "${productName}", + background: "icons/dmg/background.png", + iconSize: 128, + contents: [ + { x: 180, y: 210, type: "file" }, + { x: 480, y: 210, type: "link", path: "/Applications" }, + ], + // publish-desktop.sh computes the update feeds from the final files, so no blockmap. + writeUpdateInfo: false, + }, protocols: { name: "OpenCode", schemes: ["opencode"], @@ -261,7 +274,7 @@ export function getConfig() { // (Dock, Finder, system dialogs and the system icon styles). Older macOS keeps using the ICNS. mac: { ...base.mac, icon: "resources/icons/icon.icon" }, // The DMG volume icon would otherwise use the same actool-generated ICNS. - dmg: { icon: "resources/icons/icon.icns" }, + dmg: { ...base.dmg, icon: "resources/icons/icon.icns" }, publish: { provider: "generic", url: "https://opencode.ai/update/api/latest/desktop/opencode/", diff --git a/packages/desktop/icons/dmg/background.png b/packages/desktop/icons/dmg/background.png new file mode 100644 index 0000000000000000000000000000000000000000..5b4b8f77d51579b67e43618359e662e7b4c9c018 GIT binary patch literal 5748 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 H{ObPzK4|HF literal 0 HcmV?d00001 diff --git a/packages/desktop/script/pack.ts b/packages/desktop/script/pack.ts index 0950d953044..e9bdd7f3cc9 100644 --- a/packages/desktop/script/pack.ts +++ b/packages/desktop/script/pack.ts @@ -6,7 +6,7 @@ import { getConfig } from "../electron-builder.config" import { getCurrentCli, resolveChannel } from "./utils" // Packages the built app (out/) for OPENCODE_TARGET into dist/desktop at the repository root, unsigned: -// macOS: the .app as a tarball. Windows: the unpacked app in app/, or with OPENCODE_DESKTOP_APP, the NSIS +// macOS: the .app as a tarball and the disk image layout. Windows: the unpacked app in app/, or with OPENCODE_DESKTOP_APP, the NSIS // installer around that (signed) app. Linux: AppImage, deb and rpm. The release workflow signs afterwards. const dir = path.resolve(import.meta.dir, "..") @@ -53,6 +53,15 @@ if (!app && os === "mac") { if (bundles.length !== 1) throw new Error(`Expected one .app bundle in ${parent}, found ${bundles.join(", ")}`) await writeUpdateConfig(path.join(parent, bundles[0], "Contents", "Resources")) await $`tar -cf ${path.join(out, `opencode-desktop-mac-${cli.cpu}.app.tar`)} -C ${parent} ${bundles[0]}` + // The disk image layout (background, icon positions and volume icon) around an empty app folder, with room + // for sign-desktop-macos.sh to put the notarized app in its place. + const placeholder = path.join("dist", "dmg", bundles[0]) + await mkdir(placeholder, { recursive: true }) + await $`bunx electron-builder ${flags} -c.mac.target=dmg -c.dmg.size=2g -c.dmg.shrink=false --prepackaged ${placeholder}` + await cp( + path.join("dist", `opencode-desktop-mac-${cli.cpu}.dmg`), + path.join(out, `opencode-desktop-mac-${cli.cpu}.layout.dmg`), + ) } if (!app && os === "win") {