diff --git a/build_tools/macos/README_MAS.md b/build_tools/macos/README_MAS.md index ef0066d86..6e608dd34 100644 --- a/build_tools/macos/README_MAS.md +++ b/build_tools/macos/README_MAS.md @@ -103,9 +103,32 @@ In Apple Developer → **Profiles** → **+**: - Select the **Apple Distribution** certificate - Generate + Download -Install it by double-clicking it, or place it under: +### Where the `.provisionprofile` ends up (newer Xcode/macOS) -`~/Library/MobileDevice/Provisioning Profiles/` +Recent Xcode versions store “downloaded manual profiles” under: + +- `~/Library/Developer/Xcode/UserData/Provisioning Profiles/` + +Older tooling sometimes used: + +- `~/Library/MobileDevice/Provisioning Profiles/` + +This repo’s MAS build script does **not** require the profile to be in a specific folder — you can pass the path directly. + +To locate and pick the right profile, use: + +```bash +./build_tools/macos/find_mas_provisioning_profile.sh --bundle-id com.dryark.strawberry +``` + +### (Optional) Copy to the legacy folder + +If some other tools expect the legacy folder, you can copy it there: + +```bash +mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" +cp -f "/path/to/profile.provisionprofile" "$HOME/Library/MobileDevice/Provisioning Profiles/" +``` --- diff --git a/build_tools/macos/check_signing_identities.sh b/build_tools/macos/check_signing_identities.sh index 323c5a0dd..e4b7a40b5 100755 --- a/build_tools/macos/check_signing_identities.sh +++ b/build_tools/macos/check_signing_identities.sh @@ -137,10 +137,23 @@ EOF echo echo "==> [$(ts)] Provisioning profiles (Mac App Store builds require one)" -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}')" +prof_dirs=( + "${HOME}/Library/Developer/Xcode/UserData/Provisioning Profiles" + "${HOME}/Library/MobileDevice/Provisioning Profiles" +) +any_prof=0 +for prof_dir in "${prof_dirs[@]}"; do + if [[ -d "${prof_dir}" ]]; then + any_prof=1 + echo " ${prof_dir}" + ls -la "${prof_dir}" | head -n 20 + echo + fi +done +if [[ "$any_prof" -eq 0 ]]; then + echo "(no provisioning profile directories found in common locations)" fi +echo "Tip: to pick the right MAS profile for a bundle id, run:" +echo " ./build_tools/macos/find_mas_provisioning_profile.sh --bundle-id com.dryark.strawberry" + diff --git a/build_tools/macos/find_mas_provisioning_profile.sh b/build_tools/macos/find_mas_provisioning_profile.sh new file mode 100755 index 000000000..fa496c105 --- /dev/null +++ b/build_tools/macos/find_mas_provisioning_profile.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +set -euo pipefail + +ts() { date +"%H:%M:%S"; } + +usage() { + cat <<'EOF' +Usage: + ./build_tools/macos/find_mas_provisioning_profile.sh --bundle-id com.dryark.strawberry + +What it does: + - Scans common macOS provisioning profile locations (new Xcode + legacy) + - Decodes each *.provisionprofile (CMS) into plist + - Prints a readable table and recommends a best match for the given bundle id + +Notes: + - A provisioning profile is required for Mac App Store signing. + - This script only helps you *find* the right profile file. +EOF +} + +if [[ "$(uname -s)" != "Darwin" ]]; then + echo "Error: This script is for macOS only." >&2 + exit 1 +fi + +bundle_id="" +while [[ $# -gt 0 ]]; do + case "$1" in + --bundle-id) bundle_id="${2:-}"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown arg: $1" >&2; usage; exit 2 ;; + esac +done + +if [[ -z "$bundle_id" ]]; then + echo "Error: missing --bundle-id" >&2 + usage + exit 2 +fi + +if ! command -v security >/dev/null 2>&1; then + echo "Error: 'security' not found." >&2 + exit 1 +fi + +plutil_extract() { + local keypath="$1" + local plist="$2" + /usr/bin/plutil -extract "$keypath" raw -o - "$plist" 2>/dev/null || true +} + +find_profiles_in_dir() { + local dir="$1" + if [[ -d "$dir" ]]; then + find "$dir" -maxdepth 1 -type f -name "*.provisionprofile" 2>/dev/null || true + fi +} + +declare -a candidates +candidates=() + +# Newer Xcode location (as reported by user) +while IFS= read -r f; do candidates+=("$f"); done < <(find_profiles_in_dir "$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles") + +# Legacy location +while IFS= read -r f; do candidates+=("$f"); done < <(find_profiles_in_dir "$HOME/Library/MobileDevice/Provisioning Profiles") + +if [[ ${#candidates[@]} -eq 0 ]]; then + echo "==> [$(ts)] No .provisionprofile files found in common locations." + echo "Checked:" + echo " - $HOME/Library/Developer/Xcode/UserData/Provisioning Profiles" + echo " - $HOME/Library/MobileDevice/Provisioning Profiles" + exit 1 +fi + +echo "==> [$(ts)] Scanning ${#candidates[@]} provisioning profile(s) for bundle id: ${bundle_id}" +echo + +best_score=-1 +best_path="" +best_reason="" + +printf "%-4s %-36s %-10s %-25s %-40s %s\n" "No." "UUID" "TeamID" "Expires" "AppID" "Path" +printf "%s\n" "---- ------------------------------------ ---------- ------------------------- ---------------------------------------- ----" + +idx=0 +for f in "${candidates[@]}"; do + idx=$((idx + 1)) + + tmp="$(mktemp -t strawberry-profile.XXXXXX.plist)" + if ! security cms -D -i "$f" >"$tmp" 2>/dev/null; then + rm -f "$tmp" >/dev/null 2>&1 || true + continue + fi + + uuid="$(plutil_extract UUID "$tmp")" + name="$(plutil_extract Name "$tmp")" + teamid="$(plutil_extract 'TeamIdentifier.0' "$tmp")" + exp="$(plutil_extract ExpirationDate "$tmp")" + + # Profiles vary in which key they use for the app identifier. + appid="$(plutil_extract 'Entitlements.application-identifier' "$tmp")" + if [[ -z "$appid" ]]; then + appid="$(plutil_extract 'Entitlements.com.apple.application-identifier' "$tmp")" + fi + + rm -f "$tmp" >/dev/null 2>&1 || true + + # Fallbacks for display. + [[ -z "$uuid" ]] && uuid="(unknown)" + [[ -z "$teamid" ]] && teamid="(unknown)" + [[ -z "$exp" ]] && exp="(unknown)" + [[ -z "$appid" ]] && appid="(unknown)" + + printf "%-4s %-36s %-10s %-25s %-40s %s\n" "$idx" "$uuid" "$teamid" "$exp" "$appid" "$f" + + # Score match quality. + score=0 + reason="" + + # Prefer exact team+bundle match. + if [[ "$appid" != "(unknown)" && "$teamid" != "(unknown)" ]]; then + if [[ "$appid" == "${teamid}.${bundle_id}" ]]; then + score=100 + reason="exact match (${appid})" + elif [[ "$appid" == "${teamid}."* && "$appid" == *"*"* ]]; then + # Wildcard profile like TEAMID.* + score=60 + reason="wildcard match (${appid})" + elif [[ "$appid" == *".${bundle_id}" ]]; then + score=50 + reason="endswith match (${appid})" + fi + fi + + # Prefer profiles with a plausible name for MAS (heuristic). + if [[ "$score" -gt 0 && -n "$name" ]]; then + case "$name" in + *Mac\ App\ Store*|*App\ Store*|*appstore*|*AppStore*) + score=$((score + 5)) + reason="${reason}, name looks like MAS" + ;; + esac + fi + + if [[ "$score" -gt "$best_score" ]]; then + best_score="$score" + best_path="$f" + best_reason="$reason" + fi +done + +echo +if [[ "$best_score" -le 0 ]]; then + echo "==> [$(ts)] Could not confidently auto-select a profile for ${bundle_id}." + echo "Pick the profile whose AppID is TEAMID.${bundle_id} and is a macOS Mac App Store profile." + exit 2 +fi + +echo "==> [$(ts)] Recommended profile:" +echo " $best_path" +echo " reason: $best_reason" +echo +echo "Use it like:" +echo " ./build_tools/macos/build_mas_pkg.sh --run ... --provisionprofile \"$best_path\"" +