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 enhances the `README_MAS.md` by providing detailed information on the differences between arm64-only, universal, and x86_64-only app architectures for Mac App Store uploads. It includes recommendations for achieving broad compatibility and clarifies the limitations of submitting multiple builds. This update aims to assist developers in making informed decisions regarding app architecture for their submissions.
242 lines
8.3 KiB
Bash
242 lines
8.3 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Build a universal (arm64+x86_64) Mac App Store upload package by:
|
|
# - merging two already-deployed Strawberry.app bundles (arm64 + x86_64) using lipo
|
|
# - embedding a Mac App Store provisioning profile
|
|
# - codesigning with Apple Distribution (+ entitlements)
|
|
# - producing a signed .pkg with productbuild (Installer identity)
|
|
#
|
|
# Intended workflow with two Macs:
|
|
# 1) On Apple Silicon Mac: build+deploy MAS app bundle (unsigned) → copy strawberry.app somewhere
|
|
# 2) On Intel Mac: build+deploy MAS app bundle (unsigned) → copy strawberry.app somewhere
|
|
# 3) On the Mac that holds your signing keys (either one): run THIS script to merge+sign+package
|
|
|
|
ts() { date +"%H:%M:%S"; }
|
|
|
|
if [[ -z "${BASH_VERSION:-}" ]]; then
|
|
echo "Error: this script must be run with bash." >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$(uname -s)" != "Darwin" ]]; then
|
|
echo "Error: This script is for macOS only." >&2
|
|
exit 1
|
|
fi
|
|
|
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd -- "${script_dir}/../.." && pwd)"
|
|
|
|
ensure_keychain_search_list() {
|
|
local login_kc="$HOME/Library/Keychains/login.keychain-db"
|
|
local system_kc="/Library/Keychains/System.keychain"
|
|
local roots_kc="/System/Library/Keychains/SystemRootCertificates.keychain"
|
|
|
|
local current
|
|
current="$(security list-keychains -d user 2>/dev/null | tr -d '"' | tr -d ' ' || true)"
|
|
if echo "$current" | grep -Fq "$system_kc"; then
|
|
return 0
|
|
fi
|
|
echo "==> [$(ts)] Note: adding System keychains to the user keychain search list"
|
|
security list-keychains -d user -s "$login_kc" "$system_kc" "$roots_kc" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
prepare_login_keychain_for_signing() {
|
|
local keychain_path="$HOME/Library/Keychains/login.keychain-db"
|
|
local pw="${1:-}"
|
|
if [[ -z "$pw" ]]; then
|
|
return 0
|
|
fi
|
|
echo "==> [$(ts)] Preparing login keychain for signing (unlock + key partition list)"
|
|
security unlock-keychain -p "$pw" "$keychain_path" >/dev/null 2>&1 || true
|
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$pw" "$keychain_path" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
preflight_identity() {
|
|
local what="$1"
|
|
local policy="$2"
|
|
local identity="$3"
|
|
if ! security find-identity -p "$policy" -v 2>/dev/null | grep -Fq "$identity"; then
|
|
echo "Error: ${what} identity not found/usable in Keychain: $identity" >&2
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
./build_tools/macos/build_mas_universal_pkg.sh --run [options]
|
|
|
|
Required:
|
|
--run
|
|
--arm-app <path> Path to arm64 Strawberry.app (already built+deployed, unsigned)
|
|
--x86-app <path> Path to x86_64 Strawberry.app (already built+deployed, unsigned)
|
|
--codesign-identity "<name>" Apple Distribution: ...
|
|
--installer-identity "<name>" 3rd Party Mac Developer Installer: ...
|
|
--provisionprofile <path> Mac App Store provisioning profile (*.provisionprofile)
|
|
|
|
Optional:
|
|
--out-dir <path> Output directory (default: cmake-build-macos-release-mas-universal)
|
|
--entitlements <plist> Codesign entitlements (default: dist/macos/entitlements.mas.plist)
|
|
--pkg-out <path> Output .pkg path (default: <out-dir>/strawberry-mas-universal.pkg)
|
|
--bundle-id <id> For display/logging only (does not rewrite Info.plist)
|
|
--keychain-password <pw> Or set env var STRAWBERRY_KEYCHAIN_PASSWORD (quote it!)
|
|
|
|
Notes:
|
|
- This script does NOT build Strawberry. It merges two pre-built app bundles.
|
|
- After lipo-merging, the app must be re-signed (this script does that).
|
|
EOF
|
|
}
|
|
|
|
do_run=0
|
|
arm_app=""
|
|
x86_app=""
|
|
out_dir=""
|
|
codesign_identity=""
|
|
installer_identity=""
|
|
provisionprofile=""
|
|
entitlements=""
|
|
pkg_out=""
|
|
bundle_id=""
|
|
keychain_password="${STRAWBERRY_KEYCHAIN_PASSWORD:-}"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--run) do_run=1; shift ;;
|
|
--arm-app) arm_app="${2:-}"; shift 2 ;;
|
|
--x86-app) x86_app="${2:-}"; shift 2 ;;
|
|
--out-dir) out_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 ;;
|
|
--pkg-out) pkg_out="${2:-}"; shift 2 ;;
|
|
--bundle-id) bundle_id="${2:-}"; shift 2 ;;
|
|
--keychain-password) keychain_password="${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
|
|
exit 0
|
|
fi
|
|
|
|
if [[ -z "$arm_app" || ! -d "$arm_app" ]]; then
|
|
echo "Error: missing/invalid --arm-app: $arm_app" >&2
|
|
exit 2
|
|
fi
|
|
if [[ -z "$x86_app" || ! -d "$x86_app" ]]; then
|
|
echo "Error: missing/invalid --x86-app: $x86_app" >&2
|
|
exit 2
|
|
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 "$out_dir" ]]; then
|
|
out_dir="${repo_root}/cmake-build-macos-release-mas-universal"
|
|
fi
|
|
mkdir -p "$out_dir"
|
|
|
|
universal_app="${out_dir}/strawberry.app"
|
|
if [[ -e "$universal_app" ]]; then
|
|
rm -rf "$universal_app"
|
|
fi
|
|
|
|
echo "==> [$(ts)] Repo: $repo_root"
|
|
echo "==> [$(ts)] arm app: $arm_app"
|
|
echo "==> [$(ts)] x86 app: $x86_app"
|
|
echo "==> [$(ts)] out dir: $out_dir"
|
|
if [[ -n "$bundle_id" ]]; then
|
|
echo "==> [$(ts)] bundle id (expected): $bundle_id"
|
|
fi
|
|
|
|
echo "==> [$(ts)] Creating universal app bundle (lipo merge)"
|
|
"${repo_root}/build_tools/macos/make_universal_app.sh" \
|
|
--arm-app "$arm_app" \
|
|
--x86-app "$x86_app" \
|
|
--out-app "$universal_app" \
|
|
--clean
|
|
|
|
echo "==> [$(ts)] Embedding provisioning profile"
|
|
cp -f "$provisionprofile" "${universal_app}/Contents/embedded.provisionprofile"
|
|
|
|
ensure_keychain_search_list
|
|
prepare_login_keychain_for_signing "$keychain_password"
|
|
preflight_identity "codesign" "codesigning" "$codesign_identity"
|
|
preflight_identity "installer" "basic" "$installer_identity"
|
|
|
|
echo "==> [$(ts)] Codesigning universal app (Mac App Store)"
|
|
codesign_args=( --force --timestamp --options runtime --sign "$codesign_identity" --entitlements "$entitlements" )
|
|
|
|
# Clean up any leftover codesign temp files and xattrs.
|
|
find "$universal_app" -name "*.cstemp" -print0 2>/dev/null | while IFS= read -r -d '' f; do rm -f "$f" || true; done
|
|
xattr -dr com.apple.provenance "$universal_app" >/dev/null 2>&1 || true
|
|
xattr -dr com.apple.quarantine "$universal_app" >/dev/null 2>&1 || true
|
|
|
|
# Sign nested code first, then frameworks, then the main app bundle.
|
|
find "$universal_app" -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
|
|
if /usr/bin/file -b "$f" | grep -q "Mach-O"; then
|
|
codesign "${codesign_args[@]}" "$f" >/dev/null
|
|
fi
|
|
done
|
|
|
|
find "$universal_app" -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 "$universal_app/Contents/Frameworks" "$universal_app/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[@]}" "$universal_app" >/dev/null
|
|
|
|
echo "==> [$(ts)] Verifying codesign"
|
|
codesign --verify --deep --strict --verbose=2 "$universal_app"
|
|
|
|
if [[ -z "$pkg_out" ]]; then
|
|
pkg_out="${out_dir}/strawberry-mas-universal.pkg"
|
|
fi
|
|
rm -f "$pkg_out" >/dev/null 2>&1 || true
|
|
|
|
echo "==> [$(ts)] Building signed .pkg for App Store upload"
|
|
productbuild \
|
|
--component "$universal_app" /Applications \
|
|
--sign "$installer_identity" \
|
|
"$pkg_out"
|
|
|
|
echo "==> [$(ts)] Verifying pkg signature"
|
|
pkgutil --check-signature "$pkg_out" || true
|
|
|
|
echo
|
|
echo "Done."
|
|
echo "Universal app: $universal_app"
|
|
echo "PKG: $pkg_out"
|
|
|