From f92419f20b602aea8e3149c1023c81c6dc0d6d4b Mon Sep 17 00:00:00 2001 From: David Helkowski Date: Thu, 22 Jan 2026 17:14:30 +0900 Subject: [PATCH] Enhance macOS build process with DMG support and notarization improvements This commit introduces the ability to build and notarize DMG files as part of the macOS deployment process. The build_sign_notarize.sh script is updated to include a new --dmg option, allowing users to create a DMG after app notarization. Additionally, the Dmg.cmake script is modified to accept a codesign identity from an environment variable, improving flexibility for developers. The README.md is also updated to reflect these changes and provide guidance on the new DMG build process. --- build_tools/README.md | 13 ++++ build_tools/macos/build_sign_notarize.sh | 84 +++++++++++++++++------- cmake/Dmg.cmake | 6 ++ 3 files changed, 81 insertions(+), 22 deletions(-) diff --git a/build_tools/README.md b/build_tools/README.md index b8ce50f1b..db0ca476c 100644 --- a/build_tools/README.md +++ b/build_tools/README.md @@ -64,3 +64,16 @@ Run with no args to list local signing identities + notarytool profiles: --notary-profile "" ``` +### Build + sign + notarize + DMG (recommended for public distribution) + +This produces: + +- a notarized `strawberry.app` (stapled) +- a notarized `strawberry-notarize.zip` (useful for Sparkle / downloads) +- a notarized `strawberry-*.dmg` (stapled) + +```bash +./build_tools/macos/build_sign_notarize.sh --run --release --clean --deploy --dmg \ + --identity "Developer ID Application: Your Name (TEAMID)" \ + --notary-profile "" +``` diff --git a/build_tools/macos/build_sign_notarize.sh b/build_tools/macos/build_sign_notarize.sh index 41b5d7c44..58460d534 100755 --- a/build_tools/macos/build_sign_notarize.sh +++ b/build_tools/macos/build_sign_notarize.sh @@ -18,6 +18,7 @@ Common options: --clean Clean build dir before build --deploy Run CMake 'deploy' target before signing (default: on) --no-deploy Do not run 'deploy' (not recommended for distribution) + --dmg Build a DMG after app notarization, then notarize+staple the DMG too --build-dir Override build directory Signing options: @@ -31,6 +32,7 @@ Notarization options (recommended): Outputs: - Signed app: /strawberry.app - Zip for notarization: /strawberry-notarize.zip + - DMG (optional): /strawberry-*.dmg Notes: - This script is intended for Developer ID distribution (outside Mac App Store). @@ -73,6 +75,7 @@ do_run=0 config="Release" do_clean=0 do_deploy=1 +do_dmg=0 build_dir="" identity="" entitlements="" @@ -87,6 +90,7 @@ while [[ $# -gt 0 ]]; do --clean) do_clean=1; shift ;; --deploy) do_deploy=1; shift ;; --no-deploy) do_deploy=0; shift ;; + --dmg) do_dmg=1; shift ;; --build-dir) build_dir="${2:-}"; shift 2 ;; --identity) identity="${2:-}"; shift 2 ;; --entitlements) entitlements="${2:-}"; shift 2 ;; @@ -112,6 +116,39 @@ fi app_path="${build_dir}/strawberry.app" bin_path="${app_path}/Contents/MacOS/strawberry" zip_path="${build_dir}/strawberry-notarize.zip" +dmg_path="" + +notarize_and_staple() { + local file_path="$1" + local label="$2" + + echo "==> [$(ts)] Notarizing ${label}" + local out + out="$(mktemp -t notarytool-submit.XXXXXX)" + xcrun notarytool submit "$file_path" --keychain-profile "$notary_profile" --wait --output-format plist --no-progress >"$out" + + local submit_id submit_status + submit_id="$(/usr/bin/plutil -extract id raw -o - "$out" 2>/dev/null || true)" + submit_status="$(/usr/bin/plutil -extract status raw -o - "$out" 2>/dev/null || true)" + rm -f "$out" || true + + if [[ -z "$submit_id" ]]; then + echo "Error: could not parse notarization submission id for ${label}." >&2 + exit 1 + fi + + echo "==> [$(ts)] Notary submission id: $submit_id" + echo "==> [$(ts)] Notary status: $submit_status" + + if [[ "$submit_status" != "Accepted" ]]; then + echo "Error: notarization failed for ${label} with status '$submit_status'. Fetching log..." >&2 + xcrun notarytool log "$submit_id" --keychain-profile "$notary_profile" || true + exit 1 + fi + + echo "==> [$(ts)] Stapling ${label}" + xcrun stapler staple "$file_path" +} if [[ -z "$identity" ]]; then echo "Error: Missing --identity (Developer ID Application identity)." >&2 @@ -130,6 +167,9 @@ if [[ "$do_clean" -eq 1 ]]; then build_args+=( "--clean" ); fi if [[ -n "$build_dir" ]]; then build_args+=( "--build-dir" "$build_dir" ); fi if [[ "$do_deploy" -eq 1 ]]; then build_args+=( "--deploy" ); fi +# Let CMake (deploy/macdeployqt/create-dmg) know the signing identity if it wants it. +export APPLE_DEVELOPER_ID="$identity" + "${repo_root}/build_tools/macos/build_app.sh" "${build_args[@]}" if [[ ! -x "$bin_path" ]]; then @@ -193,31 +233,28 @@ rm -f "$zip_path" ditto -c -k --sequesterRsrc --keepParent "$app_path" "$zip_path" if [[ "$skip_notarize" -eq 0 ]]; then - echo "==> [$(ts)] Notarizing" - # Use JSON output so we can reliably detect Invalid and fetch logs. - submit_json="$(xcrun notarytool submit "$zip_path" --keychain-profile "$notary_profile" --wait --output-format json --no-progress)" - submit_id="$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))' <<<"$submit_json" 2>/dev/null || true)" - submit_status="$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("status",""))' <<<"$submit_json" 2>/dev/null || true)" - - if [[ -z "$submit_id" ]]; then - echo "Error: could not parse notarization submission id. Raw output:" >&2 - echo "$submit_json" >&2 - exit 1 - fi - - echo "==> [$(ts)] Notary submission id: $submit_id" - echo "==> [$(ts)] Notary status: $submit_status" - - if [[ "$submit_status" != "Accepted" ]]; then - echo "Error: notarization failed with status '$submit_status'. Fetching log..." >&2 - xcrun notarytool log "$submit_id" --keychain-profile "$notary_profile" || true - exit 1 - fi - - echo "==> [$(ts)] Stapling" + notarize_and_staple "$zip_path" "ZIP" + echo "==> [$(ts)] Stapling app" xcrun stapler staple "$app_path" fi +if [[ "$do_dmg" -eq 1 ]]; then + echo "==> [$(ts)] Building DMG" + cmake --build "$build_dir" --target dmg + dmg_path="$(ls -1t "$build_dir"/strawberry-*.dmg 2>/dev/null | head -n 1 || true)" + if [[ -z "$dmg_path" ]]; then + echo "Error: DMG was not created in $build_dir" >&2 + exit 1 + fi + + echo "==> [$(ts)] Codesigning DMG" + codesign --force --timestamp --sign "$identity" "$dmg_path" + + if [[ "$skip_notarize" -eq 0 ]]; then + notarize_and_staple "$dmg_path" "DMG" + fi +fi + echo "==> [$(ts)] Gatekeeper assessment" spctl -a -vv --type execute "$app_path" || true @@ -225,4 +262,7 @@ echo echo "Done." echo "App: $app_path" echo "Zip: $zip_path" +if [[ -n "${dmg_path}" ]]; then + echo "DMG: $dmg_path" +fi diff --git a/cmake/Dmg.cmake b/cmake/Dmg.cmake index dfc03054c..ba042ac57 100644 --- a/cmake/Dmg.cmake +++ b/cmake/Dmg.cmake @@ -53,6 +53,12 @@ endif() if(MACDEPLOYQT_EXECUTABLE) + # Allow build scripts to provide a codesign identity via environment variable. + # This is used to optionally pass -codesign=... to macdeployqt and --codesign to create-dmg. + if(NOT APPLE_DEVELOPER_ID AND DEFINED ENV{APPLE_DEVELOPER_ID}) + set(APPLE_DEVELOPER_ID "$ENV{APPLE_DEVELOPER_ID}") + endif() + if(APPLE_DEVELOPER_ID) set(MACDEPLOYQT_CODESIGN -codesign=${APPLE_DEVELOPER_ID}) set(CREATEDMG_CODESIGN --codesign ${APPLE_DEVELOPER_ID})