#!/usr/bin/env bash set -euo pipefail ts() { date +"%H:%M:%S"; } lower() { echo "$1" | tr '[:upper:]' '[:lower:]'; } script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(cd -- "${script_dir}/../.." && pwd)" usage() { cat <<'EOF' Usage: ./build_tools/macos/build_mas_pkg.sh --run [options] What it does: - Builds Strawberry in Mac App Store mode (BUILD_FOR_MAC_APP_STORE=ON) - Runs deploy (macdeployqt + bundling) so the app bundle is self-contained - Embeds a Mac App Store provisioning profile into the app bundle - Codesigns the app with an Apple Distribution identity + entitlements - Builds a signed .pkg suitable for uploading to App Store Connect Required options: --run --codesign-identity "" (e.g. "Apple Distribution: Dry Ark LLC (TEAMID)") --installer-identity "" (e.g. "3rd Party Mac Developer Installer: Dry Ark LLC (TEAMID)") --provisionprofile Path to a *Mac App Store* provisioning profile (*.provisionprofile) Optional: --release | --debug Build config (default: Release) --clean Clean build dir before build --build-dir Override build directory --entitlements Codesign entitlements (default: dist/macos/entitlements.mas.plist) --bundle-id Override CFBundleIdentifier (default: com.dryark.strawberry) --pkg-out Output .pkg path (default: /strawberry-mas.pkg) Examples: ./build_tools/macos/build_mas_pkg.sh --run --release --clean \ --codesign-identity "Apple Distribution: Your Name (TEAMID)" \ --installer-identity "3rd Party Mac Developer Installer: Your Name (TEAMID)" \ --provisionprofile "$HOME/Library/MobileDevice/Provisioning Profiles/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.provisionprofile" Notes: - Mac App Store submissions do NOT use Developer ID notarization. - You must create a Mac App Store provisioning profile for your App ID in Apple Developer. EOF } if [[ "$(uname -s)" != "Darwin" ]]; then echo "Error: This script is for macOS only." >&2 exit 1 fi do_run=0 config="Release" do_clean=0 build_dir="" codesign_identity="" installer_identity="" provisionprofile="" entitlements="" bundle_id="com.dryark.strawberry" pkg_out="" 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 ;; --build-dir) build_dir="${2:-}"; shift 2 ;; --codesign-identity) codesign_identity="${2:-}"; shift 2 ;; --installer-identity) installer_identity="${2:-}"; shift 2 ;; --provisionprofile) provisionprofile="${2:-}"; shift 2 ;; --entitlements) entitlements="${2:-}"; shift 2 ;; --bundle-id) bundle_id="${2:-}"; shift 2 ;; --pkg-out) pkg_out="${2:-}"; shift 2 ;; -h|--help) usage; exit 0 ;; *) echo "Unknown arg: $1" >&2; usage; exit 2 ;; esac done if [[ "$do_run" -eq 0 ]]; then usage echo echo "==> [$(ts)] Tip: list available signing identities:" echo " security find-identity -p codesigning -v" echo " security find-identity -p basic -v" exit 0 fi if [[ -z "$codesign_identity" ]]; then echo "Error: missing --codesign-identity" >&2 exit 2 fi if [[ -z "$installer_identity" ]]; then echo "Error: missing --installer-identity" >&2 exit 2 fi if [[ -z "$provisionprofile" || ! -f "$provisionprofile" ]]; then echo "Error: missing/invalid --provisionprofile: $provisionprofile" >&2 exit 2 fi if [[ -z "$entitlements" ]]; then entitlements="${repo_root}/dist/macos/entitlements.mas.plist" fi if [[ ! -f "$entitlements" ]]; then echo "Error: entitlements file not found: $entitlements" >&2 exit 2 fi if [[ -z "$build_dir" ]]; then build_dir="${repo_root}/cmake-build-macos-$(lower "$config")-mas" fi if [[ -z "$pkg_out" ]]; then pkg_out="${build_dir}/strawberry-mas.pkg" fi echo "==> [$(ts)] Repo: ${repo_root}" echo "==> [$(ts)] Build dir: ${build_dir}" echo "==> [$(ts)] Config: ${config}" echo "==> [$(ts)] Bundle ID: ${bundle_id}" echo "==> [$(ts)] Entitlements: ${entitlements}" echo "==> [$(ts)] Provisioning profile: ${provisionprofile}" echo "==> [$(ts)] Output pkg: ${pkg_out}" echo "==> [$(ts)] Building (Mac App Store mode)" build_args=( "--release" ) if [[ "$config" == "Debug" ]]; then build_args=( "--debug" ); fi if [[ "$do_clean" -eq 1 ]]; then build_args+=( "--clean" ); fi build_args+=( "--build-dir" "$build_dir" "--mas" "--deploy" ) # Provide bundle id via CMake cache variable. export MACOS_BUNDLE_ID="$bundle_id" "${repo_root}/build_tools/macos/build_app.sh" "${build_args[@]}" app_path="${build_dir}/strawberry.app" bin_path="${app_path}/Contents/MacOS/strawberry" if [[ ! -x "$bin_path" ]]; then echo "Error: built app not found at: $app_path" >&2 exit 1 fi echo "==> [$(ts)] Embedding provisioning profile" cp -f "$provisionprofile" "${app_path}/Contents/embedded.provisionprofile" echo "==> [$(ts)] Codesigning app (Mac App Store)" codesign_args=( --force --timestamp --options runtime --sign "$codesign_identity" --entitlements "$entitlements" ) # Clean up any leftover codesign temp files from previous interrupted runs. find "$app_path" -name "*.cstemp" -print0 2>/dev/null | while IFS= read -r -d '' f; do rm -f "$f" || true done # Clear macOS provenance/quarantine metadata which can interfere with modifying files in-place. xattr -dr com.apple.provenance "$app_path" >/dev/null 2>&1 || true xattr -dr com.apple.quarantine "$app_path" >/dev/null 2>&1 || true # Sign nested code first, then frameworks, then the main app bundle. find "$app_path" -type f \( -name "*.dylib" -o -name "*.so" -o -perm -111 \) \ ! -name "*.cstemp" \ ! -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 # Only sign Mach-O binaries. if file -b "$f" | grep -q "Mach-O"; then codesign "${codesign_args[@]}" "$f" >/dev/null fi done 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 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 codesign "${codesign_args[@]}" "$app_path" >/dev/null echo "==> [$(ts)] Verifying codesign" codesign --verify --deep --strict --verbose=2 "$app_path" echo "==> [$(ts)] Building signed .pkg for App Store upload" rm -f "$pkg_out" >/dev/null 2>&1 || true productbuild \ --component "$app_path" /Applications \ --sign "$installer_identity" \ "$pkg_out" echo "==> [$(ts)] Verifying pkg signature" pkgutil --check-signature "$pkg_out" || true echo echo "Done." echo "App: $app_path" echo "PKG: $pkg_out"