This commit introduces functions to ensure the System keychains are included in the user keychain search list, addressing common codesigning errors related to keychain trust chains. Additionally, it adds preflight checks for codesigning and installer identities, improving error reporting and guidance for developers. The README_MAS.md is updated to include troubleshooting steps for keychain-related issues, enhancing the overall usability of the macOS build process.
242 lines
8.3 KiB
Bash
Executable File
242 lines
8.3 KiB
Bash
Executable File
#!/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 "<name>" (e.g. "Apple Distribution: Dry Ark LLC (TEAMID)")
|
|
--installer-identity "<name>" (e.g. "3rd Party Mac Developer Installer: Dry Ark LLC (TEAMID)")
|
|
--provisionprofile <path> Path to a *Mac App Store* provisioning profile (*.provisionprofile)
|
|
|
|
Optional:
|
|
--release | --debug Build config (default: Release)
|
|
--clean Clean build dir before build
|
|
--build-dir <path> Override build directory
|
|
--entitlements <plist> Codesign entitlements (default: dist/macos/entitlements.mas.plist)
|
|
--bundle-id <id> Override CFBundleIdentifier (default: com.dryark.strawberry)
|
|
--pkg-out <path> Output .pkg path (default: <build-dir>/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"
|
|
|
|
ensure_keychain_search_list
|
|
preflight_identity "codesign" "-p codesigning" "$codesign_identity"
|
|
preflight_identity "installer" "-p basic" "$installer_identity"
|
|
|
|
ensure_keychain_search_list() {
|
|
# codesign builds the cert chain using the user keychain search list.
|
|
# If the list is missing the System keychain, you can get:
|
|
# "unable to build chain to self-signed root" + errSecInternalComponent
|
|
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 (fixes common codesign chain errors)"
|
|
echo " (This changes the user keychain search list; run 'security list-keychains -d user' to view.)"
|
|
security list-keychains -d user -s "$login_kc" "$system_kc" "$roots_kc" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
preflight_identity() {
|
|
local what="$1"
|
|
local predicate="$2"
|
|
local identity="$3"
|
|
|
|
if ! security find-identity "$predicate" -v 2>/dev/null | grep -Fq "$identity"; then
|
|
echo "Error: ${what} identity not found/usable in Keychain: $identity" >&2
|
|
echo "Run: ./build_tools/macos/check_signing_identities.sh" >&2
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
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"
|
|
|