Some checks failed
Build / Build openSUSE (leap:15.6) (push) Has been cancelled
Build / Build openSUSE (leap:16.0) (push) Has been cancelled
Build / Build openSUSE (tumbleweed) (push) Has been cancelled
Build / Build Fedora (42) (push) Has been cancelled
Build / Build Fedora (43) (push) Has been cancelled
Build / Build Fedora (44) (push) Has been cancelled
Build / Build OpenMandriva (cooker) (push) Has been cancelled
Build / Build Mageia (9) (push) Has been cancelled
Build / Build Debian (bookworm) (push) Has been cancelled
Build / Build Debian (forky) (push) Has been cancelled
Build / Build Debian (trixie) (push) Has been cancelled
Build / Build Ubuntu (noble) (push) Has been cancelled
Build / Build Ubuntu (questing) (push) Has been cancelled
Build / Build Ubuntu (resolute) (push) Has been cancelled
Build / Upload Ubuntu PPA (noble) (push) Has been cancelled
Build / Upload Ubuntu PPA (questing) (push) Has been cancelled
Build / Upload Ubuntu PPA (resolute) (push) Has been cancelled
Build / Build FreeBSD (push) Has been cancelled
Build / Build OpenBSD (push) Has been cancelled
Build / Build macOS Public (release, macos-15) (push) Has been cancelled
Build / Build macOS Public (release, macos-15-intel) (push) Has been cancelled
Build / Build macOS Private (release, macos-arm64) (push) Has been cancelled
Build / Build Windows MinGW (i686, debug) (push) Has been cancelled
Build / Build Windows MinGW (i686, release) (push) Has been cancelled
Build / Build Windows MinGW (x86_64, debug) (push) Has been cancelled
Build / Build Windows MinGW (x86_64, release) (push) Has been cancelled
Build / Build Windows MSVC (arm64, debug, arm64 debug, windows-11-arm) (push) Has been cancelled
Build / Build Windows MSVC (arm64, release, arm64 release, windows-11-arm) (push) Has been cancelled
Build / Build Windows MSVC (x86, debug, x86 debug, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86, release, x86 release, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86_64, debug, x86_64 debug, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86_64, release, x86_64 release, windows-2022) (push) Has been cancelled
Build / Upload (push) Has been cancelled
Build / Attach to release (push) Has been cancelled
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.
269 lines
9.0 KiB
Bash
Executable File
269 lines
9.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ts() { date +"%H:%M:%S"; }
|
|
|
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd -- "${script_dir}/../.." && pwd)"
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
./build_tools/macos/build_sign_notarize.sh # list local signing identities + notary profiles
|
|
./build_tools/macos/build_sign_notarize.sh --run [options] # build, sign, notarize, staple
|
|
|
|
Common options:
|
|
--run Perform build/sign/notarize (otherwise list identities/profiles)
|
|
--release | --debug Build config (default: Release)
|
|
--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 <path> Override build directory
|
|
|
|
Signing options:
|
|
--identity "<name>" Codesign identity (e.g. "Developer ID Application: Your Name (TEAMID)")
|
|
--entitlements <plist> Optional entitlements plist for codesign
|
|
|
|
Notarization options (recommended):
|
|
--notary-profile <name> notarytool keychain profile name (created via `xcrun notarytool store-credentials <name> ...`)
|
|
--skip-notarize Skip notarization
|
|
|
|
Outputs:
|
|
- Signed app: <build-dir>/strawberry.app
|
|
- Zip for notarization: <build-dir>/strawberry-notarize.zip
|
|
- DMG (optional): <build-dir>/strawberry-*.dmg
|
|
|
|
Notes:
|
|
- This script is intended for Developer ID distribution (outside Mac App Store).
|
|
- If you want Sparkle updates, you'll typically ship a notarized .zip + an appcast feed.
|
|
EOF
|
|
}
|
|
|
|
list_identities_and_profiles() {
|
|
echo "==> [$(ts)] macOS code signing identities (Keychain)"
|
|
security find-identity -p codesigning -v || true
|
|
|
|
echo
|
|
echo "==> [$(ts)] notarytool credential profiles"
|
|
echo "Note: this Xcode notarytool version does not provide a 'list-profiles' command."
|
|
echo "If you forgot the profile name you created, check Keychain Access or re-run:"
|
|
echo " xcrun notarytool store-credentials \"<profile-name>\" --apple-id \"you@example.com\" --team-id \"TEAMID\""
|
|
|
|
echo
|
|
echo "==> [$(ts)] Provisioning profiles (macOS)"
|
|
prof_dir="${HOME}/Library/MobileDevice/Provisioning Profiles"
|
|
if [[ -d "${prof_dir}" ]]; then
|
|
ls -la "${prof_dir}" | head -n 50
|
|
else
|
|
echo "(none found at '${prof_dir}')"
|
|
fi
|
|
}
|
|
|
|
if [[ "$(uname -s)" != "Darwin" ]]; then
|
|
echo "Error: This script is for macOS only." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v xcode-select >/dev/null 2>&1 || ! xcode-select -p >/dev/null 2>&1; then
|
|
echo "Error: Xcode Command Line Tools not found." >&2
|
|
echo "Install them first: xcode-select --install" >&2
|
|
exit 1
|
|
fi
|
|
|
|
do_run=0
|
|
config="Release"
|
|
do_clean=0
|
|
do_deploy=1
|
|
do_dmg=0
|
|
build_dir=""
|
|
identity=""
|
|
entitlements=""
|
|
notary_profile=""
|
|
skip_notarize=0
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--run) do_run=1; shift ;;
|
|
--release) config="Release"; shift ;;
|
|
--debug) config="Debug"; shift ;;
|
|
--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 ;;
|
|
--notary-profile) notary_profile="${2:-}"; shift 2 ;;
|
|
--skip-notarize) skip_notarize=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "Unknown arg: $1" >&2; usage; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
if [[ "$do_run" -eq 0 ]]; then
|
|
usage
|
|
echo
|
|
list_identities_and_profiles
|
|
exit 0
|
|
fi
|
|
|
|
if [[ -z "$build_dir" ]]; then
|
|
lc_config="$(echo "$config" | tr '[:upper:]' '[:lower:]')"
|
|
build_dir="${repo_root}/cmake-build-macos-${lc_config}"
|
|
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
|
|
exit 2
|
|
fi
|
|
|
|
if [[ "$skip_notarize" -eq 0 && -z "$notary_profile" ]]; then
|
|
echo "Error: Missing --notary-profile (or pass --skip-notarize)." >&2
|
|
exit 2
|
|
fi
|
|
|
|
echo "==> [$(ts)] Building Strawberry"
|
|
build_args=( "--release" )
|
|
if [[ "$config" == "Debug" ]]; then build_args=( "--debug" ); fi
|
|
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
|
|
echo "Error: built app not found at: $app_path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> [$(ts)] Codesigning (hardened runtime)"
|
|
codesign_args=( --force --timestamp --options runtime --sign "$identity" )
|
|
if [[ -n "$entitlements" ]]; then
|
|
codesign_args+=( --entitlements "$entitlements" )
|
|
fi
|
|
|
|
# Sign nested code first, then frameworks, then the main app bundle.
|
|
#
|
|
# Important: do NOT codesign individual files *inside* a .framework bundle (e.g. Sparkle.framework/Sparkle),
|
|
# because codesign expects frameworks to be signed as bundles and may error with
|
|
# "bundle format is ambiguous (could be app or framework)".
|
|
|
|
# 1) Sign dylibs and standalone executables that are NOT inside a .framework/.app/.xpc bundle.
|
|
find "$app_path" -type f \( -name "*.dylib" -o -name "*.so" -o -perm -111 \) \
|
|
! -path "*/Contents/Frameworks/*.framework/*" \
|
|
! -path "*/Contents/Frameworks/*.app/*" \
|
|
! -path "*/Contents/Frameworks/*.xpc/*" \
|
|
! -path "*/Contents/PlugIns/*.framework/*" \
|
|
! -path "*/Contents/PlugIns/*.app/*" \
|
|
! -path "*/Contents/PlugIns/*.xpc/*" \
|
|
-print0 | while IFS= read -r -d '' f; do
|
|
codesign "${codesign_args[@]}" "$f" >/dev/null
|
|
done
|
|
|
|
# 2) Sign nested helper apps and XPC services (Sparkle ships these inside its framework).
|
|
find "$app_path" -type d \( -name "*.xpc" -o -name "*.app" \) -print0 2>/dev/null | while IFS= read -r -d '' b; do
|
|
codesign "${codesign_args[@]}" "$b" >/dev/null
|
|
done
|
|
|
|
# 2b) Sparkle.framework contains a standalone helper executable "Autoupdate" under Versions/* that is
|
|
# not inside an .app or .xpc bundle. Notarization requires it be signed with Developer ID + timestamp.
|
|
sparkle_fw="$app_path/Contents/Frameworks/Sparkle.framework"
|
|
if [[ -d "$sparkle_fw" ]]; then
|
|
find "$sparkle_fw/Versions" -type f -perm -111 \
|
|
! -path "*/_CodeSignature/*" \
|
|
-print0 2>/dev/null | while IFS= read -r -d '' f; do
|
|
codesign "${codesign_args[@]}" "$f" >/dev/null
|
|
done
|
|
fi
|
|
|
|
# 3) Sign frameworks as bundles.
|
|
find "$app_path/Contents/Frameworks" "$app_path/Contents/PlugIns" -type d -name "*.framework" -print0 2>/dev/null | while IFS= read -r -d '' fw; do
|
|
codesign "${codesign_args[@]}" "$fw" >/dev/null
|
|
done
|
|
|
|
# 4) Finally sign the main app.
|
|
codesign "${codesign_args[@]}" "$app_path" >/dev/null
|
|
|
|
echo "==> [$(ts)] Verifying codesign"
|
|
codesign --verify --deep --strict --verbose=2 "$app_path"
|
|
|
|
echo "==> [$(ts)] Creating zip for notarization"
|
|
rm -f "$zip_path"
|
|
ditto -c -k --sequesterRsrc --keepParent "$app_path" "$zip_path"
|
|
|
|
if [[ "$skip_notarize" -eq 0 ]]; then
|
|
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
|
|
|
|
echo
|
|
echo "Done."
|
|
echo "App: $app_path"
|
|
echo "Zip: $zip_path"
|
|
if [[ -n "${dmg_path}" ]]; then
|
|
echo "DMG: $dmg_path"
|
|
fi
|
|
|