diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9503c5c..0b8809a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -145,6 +145,13 @@ jobs: - name: Test run: swift test --package-path engine + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install create-dmg + run: npm install --global create-dmg@8.1.0 + - name: Build, sign, notarize, staple, package, checksum, appcast run: scripts/sign-and-notarize.sh diff --git a/RELEASING.md b/RELEASING.md index 36c425b..6718e83 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -23,6 +23,9 @@ downloads and are rewritten on every release. with `notarytool`, and the failure only shows up at submission. - The Sparkle update key pair. The public half is compiled into the app; the private half signs the feed. +- create-dmg 8.1.0, which lays out the disk image window and draws its disk icon: + `npm install --global create-dmg@8.1.0` (Node 20 or newer). `scripts/package-dmg.sh` refuses any + other version, so a disk image made by hand matches the one the workflow makes. **If the update private key is lost, no installed copy can ever be updated again.** A later release cannot fix it, because the installed app only trusts the public key baked into the copy someone diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 3f9fbcf..84b0d68 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -60,3 +60,12 @@ follows a quintic ease in and out, with one clock driving the guest's hinge and Copyright (c) 2020 Apple Inc. and the Swift project authors. Licensed under the Apache License, Version 2.0. https://github.com/apple/swift-argument-parser + +### create-dmg + +Copyright (c) Sindre Sorhus. Licensed under the MIT license. +https://github.com/sindresorhus/create-dmg + +The release disk image is made with create-dmg 8.1.0 when it is packaged, and nothing from it is +kept in this repository. Its window background and layout ship inside every disk image. The disk +icon is macOS's removable drive icon, as create-dmg carries it, with the app's icon drawn on top. diff --git a/scripts/package-dmg.sh b/scripts/package-dmg.sh index d984652..edb016f 100755 --- a/scripts/package-dmg.sh +++ b/scripts/package-dmg.sh @@ -5,25 +5,40 @@ # dragged out of the DMG passes Gatekeeper on a machine that has never been online. The DMG then # gets a ticket of its own so the download itself is not flagged. # +# create-dmg lays out the window (background, arrow, icon positions) and the disk icon on a read +# and write image before converting it, so the signature below still comes last. One pinned +# version keeps a DMG made by hand laid out the same as the one the release workflow makes. +# # Usage: scripts/package-dmg.sh [app] set -euo pipefail +create_dmg_version="8.1.0" + repo_root="$(cd "$(dirname "$0")/.." && pwd)" app="${1:-${repo_root}/build/OpenDeviceHub.app}" [ -d "${app}" ] || { echo "No app at ${app}" >&2; exit 1; } +installed="$(create-dmg --version 2>/dev/null || true)" +if [ "${installed}" != "${create_dmg_version}" ]; then + echo "Needs create-dmg ${create_dmg_version}, found ${installed:-none}." >&2 + echo "Install it with: npm install --global create-dmg@${create_dmg_version}" >&2 + exit 1 +fi + version="$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "${app}/Contents/Info.plist")" dmg="${repo_root}/build/OpenDeviceHub-${version}.dmg" staging="$(mktemp -d)" cleanup() { rm -rf "${staging}"; } trap cleanup EXIT INT TERM -ditto "${app}" "${staging}/$(basename "${app}")" -ln -s /Applications "${staging}/Applications" +# Its progress goes to standard error, so standard output stays the DMG's path alone. +create-dmg --overwrite --no-version-in-filename --no-code-sign \ + --dmg-title="OpenDeviceHub ${version}" "${app}" "${staging}" >&2 +made="$(find "${staging}" -maxdepth 1 -name '*.dmg' -print -quit)" +[ -n "${made}" ] || { echo "create-dmg made no DMG" >&2; exit 1; } rm -f "${dmg}" -hdiutil create -quiet -volname "OpenDeviceHub ${version}" -srcfolder "${staging}" \ - -ov -format UDZO "${dmg}" +mv "${made}" "${dmg}" if [ -n "${ODH_SIGNING_IDENTITY:-}" ]; then codesign --force --timestamp --sign "${ODH_SIGNING_IDENTITY}" "${dmg}"