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 refines the CMake configuration for macOS by finding the Sparkle framework early in the build process, allowing it to be bundled with the application. The Dmg.cmake script is updated to handle Sparkle's framework paths and ensure proper deployment. Additionally, the build_sign_notarize.sh script is improved to sign Sparkle's helper executables correctly and includes enhanced notarization feedback. The Brewfile and install_brew_deps.sh are also updated to include the new macdeploycheck dependency for better deployment checks.
229 lines
8.0 KiB
Bash
Executable File
229 lines
8.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)
|
|
--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
|
|
|
|
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
|
|
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 ;;
|
|
--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"
|
|
|
|
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
|
|
|
|
"${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
|
|
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"
|
|
xcrun stapler staple "$app_path"
|
|
fi
|
|
|
|
echo "==> [$(ts)] Gatekeeper assessment"
|
|
spctl -a -vv --type execute "$app_path" || true
|
|
|
|
echo
|
|
echo "Done."
|
|
echo "App: $app_path"
|
|
echo "Zip: $zip_path"
|
|
|