Enhance macOS build scripts for provisioning profile handling and identity management
This commit improves the `find_mas_provisioning_profile.sh` script by expanding the search for provisioning profiles to include both `.provisionprofile` and `.mobileprovision` files. It also introduces a new function to print SHA-1 values for identities, helping to avoid ambiguity when multiple identities share the same display name. Additionally, the `check_signing_identities.sh` script is updated to provide clearer recommendations for using SHA-1 hashes with codesigning and installer identities, enhancing the overall usability and clarity for developers working with macOS builds.
This commit is contained in:
@@ -7,6 +7,41 @@ lower() { echo "$1" | tr '[:upper:]' '[:lower:]'; }
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd -- "${script_dir}/../.." && pwd)"
|
||||
|
||||
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 policy="$2"
|
||||
local identity="$3"
|
||||
|
||||
# NOTE: security expects "-p <policy>" as *two* args; do not pass "-p codesigning" as one string.
|
||||
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
|
||||
echo "Run: ./build_tools/macos/check_signing_identities.sh" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
@@ -147,40 +182,10 @@ 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"
|
||||
preflight_identity "codesign" "codesigning" "$codesign_identity"
|
||||
preflight_identity "installer" "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" )
|
||||
|
||||
@@ -156,4 +156,52 @@ 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"
|
||||
\n\necho\n
|
||||
echo "==> [$(ts)] Recommended SHA-1 values to use (avoids ambiguity when names are duplicated)"
|
||||
cat <<'EOF'
|
||||
When you have multiple identities with the same display name, prefer using the SHA-1 hash in scripts:
|
||||
|
||||
--codesign-identity "<SHA1>"
|
||||
--installer-identity "<SHA1>"
|
||||
|
||||
This prevents codesign/productbuild from picking an unexpected identity.
|
||||
EOF
|
||||
echo
|
||||
|
||||
extract_identities() {
|
||||
local policy="$1" # codesigning | basic
|
||||
# Output: SHA1|LABEL
|
||||
security find-identity -p "$policy" -v 2>/dev/null \
|
||||
| sed -n 's/^[[:space:]]*[0-9][0-9]*[)] \([0-9A-F]\{40\}\) "\(.*\)"$/\1|\2/p'
|
||||
}
|
||||
|
||||
print_sha_list() {
|
||||
local title="$1"
|
||||
local policy="$2"
|
||||
local label_match="$3"
|
||||
|
||||
echo "$title"
|
||||
local matches
|
||||
matches="$(extract_identities "$policy" | grep -F "$label_match" || true)"
|
||||
if [[ -z "$matches" ]]; then
|
||||
echo " (none found)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local first=1
|
||||
while IFS='|' read -r sha label; do
|
||||
[[ -z "$sha" || -z "$label" ]] && continue
|
||||
if [[ $first -eq 1 ]]; then
|
||||
echo " recommended: $sha ($label)"
|
||||
first=0
|
||||
else
|
||||
echo " alternative: $sha ($label)"
|
||||
fi
|
||||
done <<<"$matches"
|
||||
}
|
||||
|
||||
print_sha_list "Mac App Store (app signing) [use with --codesign-identity]:" "codesigning" "Apple Distribution:"
|
||||
print_sha_list "Mac App Store (pkg signing) [use with --installer-identity]:" "basic" "3rd Party Mac Developer Installer:"
|
||||
print_sha_list "Developer ID (app signing) [outside App Store]:" "codesigning" "Developer ID Application:"
|
||||
print_sha_list "Developer ID (pkg signing) [outside App Store]:" "basic" "Developer ID Installer:"
|
||||
|
||||
|
||||
210
build_tools/macos/find_mas_provisioning_profile.py
Normal file
210
build_tools/macos/find_mas_provisioning_profile.py
Normal file
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import datetime as dt
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import plistlib
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProfileInfo:
|
||||
path: Path
|
||||
uuid: str
|
||||
name: str
|
||||
team_id: str
|
||||
expiration: Optional[dt.datetime]
|
||||
app_id: str
|
||||
platforms: List[str]
|
||||
|
||||
|
||||
def run(cmd: List[str]) -> Tuple[int, bytes, bytes]:
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = p.communicate()
|
||||
return p.returncode, out, err
|
||||
|
||||
|
||||
def decode_profile_plist_bytes(profile_path: Path) -> Optional[bytes]:
|
||||
# Provisioning profiles are typically CMS/PKCS#7 SignedData blobs whose payload is a plist.
|
||||
# However, some tools store them as plain XML plists already. Also, LibreSSL/OpenSSL behavior
|
||||
# differs: LibreSSL usually requires an explicit '-verify' to emit the embedded content.
|
||||
data = profile_path.read_bytes()
|
||||
|
||||
# Fast path: already a plist (XML).
|
||||
if b"<plist" in data:
|
||||
return data
|
||||
|
||||
# Decode CMS/PKCS7 to extract embedded plist payload.
|
||||
# Try a small matrix of commands/inform formats for compatibility.
|
||||
candidates: List[List[str]] = []
|
||||
for inform in ("DER", "PEM"):
|
||||
candidates.append(["/usr/bin/openssl", "cms", "-verify", "-noverify", "-inform", inform, "-in", str(profile_path)])
|
||||
candidates.append(["/usr/bin/openssl", "smime", "-verify", "-noverify", "-inform", inform, "-in", str(profile_path)])
|
||||
|
||||
for cmd in candidates:
|
||||
rc, out, _err = run(cmd)
|
||||
if rc == 0 and b"<plist" in out:
|
||||
return out
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def parse_plist(plist_bytes: bytes) -> Dict[str, Any]:
|
||||
return plistlib.loads(plist_bytes)
|
||||
|
||||
|
||||
def iso(dt_obj: Optional[dt.datetime]) -> str:
|
||||
if not dt_obj:
|
||||
return "(unknown)"
|
||||
# Force UTC-ish display if tz-aware, otherwise as-is.
|
||||
try:
|
||||
return dt_obj.isoformat().replace("+00:00", "Z")
|
||||
except Exception:
|
||||
return str(dt_obj)
|
||||
|
||||
|
||||
def safe_str(v: Any) -> str:
|
||||
if v is None:
|
||||
return ""
|
||||
if isinstance(v, bytes):
|
||||
try:
|
||||
return v.decode("utf-8", errors="replace")
|
||||
except Exception:
|
||||
return repr(v)
|
||||
return str(v)
|
||||
|
||||
|
||||
def profile_info_from_plist(path: Path, p: Dict[str, Any]) -> ProfileInfo:
|
||||
uuid = safe_str(p.get("UUID", "")) or "(unknown)"
|
||||
name = safe_str(p.get("Name", "")) or "(unknown)"
|
||||
team_ids = p.get("TeamIdentifier") or []
|
||||
team_id = safe_str(team_ids[0]) if isinstance(team_ids, list) and team_ids else ""
|
||||
if not team_id:
|
||||
prefixes = p.get("ApplicationIdentifierPrefix") or []
|
||||
team_id = safe_str(prefixes[0]) if isinstance(prefixes, list) and prefixes else "(unknown)"
|
||||
exp = p.get("ExpirationDate")
|
||||
expiration = exp if isinstance(exp, dt.datetime) else None
|
||||
ent = p.get("Entitlements") or {}
|
||||
app_id = safe_str(ent.get("application-identifier") or ent.get("com.apple.application-identifier") or "") or "(unknown)"
|
||||
platforms = p.get("Platform") or []
|
||||
if isinstance(platforms, str):
|
||||
platforms = [platforms]
|
||||
platforms = [safe_str(x) for x in platforms if x is not None]
|
||||
return ProfileInfo(path=path, uuid=uuid, name=name, team_id=team_id or "(unknown)", expiration=expiration, app_id=app_id, platforms=platforms)
|
||||
|
||||
|
||||
def score(profile: ProfileInfo, bundle_id: str, now: dt.datetime) -> Tuple[int, str]:
|
||||
# Prefer non-expired.
|
||||
if profile.expiration and profile.expiration < now:
|
||||
return (-1, "expired")
|
||||
|
||||
score = 0
|
||||
reason = []
|
||||
|
||||
# Prefer exact app id match TEAMID.bundle_id
|
||||
if profile.team_id != "(unknown)" and profile.app_id != "(unknown)":
|
||||
exact = f"{profile.team_id}.{bundle_id}"
|
||||
if profile.app_id == exact:
|
||||
score += 100
|
||||
reason.append(f"exact {profile.app_id}")
|
||||
elif profile.app_id.endswith(f".{bundle_id}"):
|
||||
score += 60
|
||||
reason.append(f"endswith {profile.app_id}")
|
||||
elif "*" in profile.app_id and profile.app_id.startswith(f"{profile.team_id}."):
|
||||
score += 40
|
||||
reason.append(f"wildcard {profile.app_id}")
|
||||
|
||||
# Heuristic: name suggests MAS.
|
||||
n = profile.name.lower()
|
||||
if "mac app store" in n or "app store" in n or "appstore" in n:
|
||||
score += 5
|
||||
reason.append("name looks like MAS")
|
||||
|
||||
# Prefer macOS platform if present.
|
||||
plats = [p.lower() for p in profile.platforms]
|
||||
if any("macos" in p for p in plats):
|
||||
score += 2
|
||||
reason.append("platform macos")
|
||||
|
||||
return (score, ", ".join(reason) if reason else "")
|
||||
|
||||
|
||||
def find_profiles() -> List[Path]:
|
||||
dirs = [
|
||||
Path.home() / "Library" / "Developer" / "Xcode" / "UserData" / "Provisioning Profiles",
|
||||
Path.home() / "Library" / "MobileDevice" / "Provisioning Profiles",
|
||||
]
|
||||
out: List[Path] = []
|
||||
for d in dirs:
|
||||
if not d.is_dir():
|
||||
continue
|
||||
for p in d.iterdir():
|
||||
if p.is_file() and (p.name.endswith(".provisionprofile") or p.name.endswith(".mobileprovision")):
|
||||
out.append(p)
|
||||
return sorted(out)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--bundle-id", required=True)
|
||||
args = ap.parse_args()
|
||||
bundle_id = args.bundle_id
|
||||
|
||||
if not Path("/usr/bin/openssl").exists():
|
||||
print("Error: /usr/bin/openssl not found.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
candidates = find_profiles()
|
||||
if not candidates:
|
||||
print("No provisioning profiles found in common locations.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f"Scanning {len(candidates)} provisioning profile(s) for bundle id: {bundle_id}")
|
||||
print()
|
||||
print(f"{'No.':<4} {'UUID':<36} {'TeamID':<10} {'Expires':<25} {'AppID':<45} Path")
|
||||
print(f"{'-'*4} {'-'*36} {'-'*10} {'-'*25} {'-'*45} ----")
|
||||
|
||||
infos: List[ProfileInfo] = []
|
||||
for i, p in enumerate(candidates, start=1):
|
||||
plist_bytes = decode_profile_plist_bytes(p)
|
||||
if not plist_bytes:
|
||||
continue
|
||||
try:
|
||||
pl = parse_plist(plist_bytes)
|
||||
info = profile_info_from_plist(p, pl)
|
||||
infos.append(info)
|
||||
print(f"{i:<4} {info.uuid:<36} {info.team_id:<10} {iso(info.expiration):<25} {info.app_id:<45} {info.path}")
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if not infos:
|
||||
print("\nCould not decode any provisioning profiles with openssl cms.", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
now = dt.datetime.now(dt.timezone.utc)
|
||||
best: Optional[Tuple[int, str, ProfileInfo]] = None
|
||||
for info in infos:
|
||||
sc, why = score(info, bundle_id, now)
|
||||
if best is None or sc > best[0]:
|
||||
best = (sc, why, info)
|
||||
|
||||
print()
|
||||
if best is None or best[0] <= 0:
|
||||
print(f"Could not confidently auto-select a profile for {bundle_id}.", file=sys.stderr)
|
||||
print("Pick the profile whose AppID is TEAMID.<bundle-id> and is a macOS Mac App Store profile.", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
_, why, info = best
|
||||
print("Recommended profile:")
|
||||
print(f" {info.path}")
|
||||
print(f" reason: {why}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -10,7 +10,7 @@ Usage:
|
||||
|
||||
What it does:
|
||||
- Scans common macOS provisioning profile locations (new Xcode + legacy)
|
||||
- Decodes each *.provisionprofile (CMS) into plist
|
||||
- Uses Apple's `security cms -D` to decode each *.provisionprofile into a plist
|
||||
- Prints a readable table and recommends a best match for the given bundle id
|
||||
|
||||
Notes:
|
||||
@@ -44,6 +44,12 @@ if ! command -v security >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
plistbuddy_print() {
|
||||
local keypath="$1"
|
||||
local plist="$2"
|
||||
/usr/libexec/PlistBuddy -c "Print :${keypath}" "$plist" 2>/dev/null || true
|
||||
}
|
||||
|
||||
plutil_extract() {
|
||||
local keypath="$1"
|
||||
local plist="$2"
|
||||
@@ -53,37 +59,29 @@ plutil_extract() {
|
||||
find_profiles_in_dir() {
|
||||
local dir="$1"
|
||||
if [[ -d "$dir" ]]; then
|
||||
find "$dir" -maxdepth 1 -type f -name "*.provisionprofile" 2>/dev/null || true
|
||||
find "$dir" -maxdepth 1 -type f \( -name "*.provisionprofile" -o -name "*.mobileprovision" \) 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"
|
||||
echo "==> [$(ts)] No provisioning profiles found in common locations."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> [$(ts)] Scanning ${#candidates[@]} provisioning profile(s) for bundle id: ${bundle_id}"
|
||||
echo
|
||||
printf "%-4s %-36s %-10s %-25s %-45s %s\n" "No." "UUID" "TeamID" "Expires" "AppID" "Path"
|
||||
printf "%s\n" "---- ------------------------------------ ---------- ------------------------- --------------------------------------------- ----"
|
||||
|
||||
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))
|
||||
@@ -97,44 +95,42 @@ for f in "${candidates[@]}"; do
|
||||
uuid="$(plutil_extract UUID "$tmp")"
|
||||
name="$(plutil_extract Name "$tmp")"
|
||||
teamid="$(plutil_extract 'TeamIdentifier.0' "$tmp")"
|
||||
if [[ -z "$teamid" ]]; then
|
||||
teamid="$(plutil_extract 'ApplicationIdentifierPrefix.0' "$tmp")"
|
||||
fi
|
||||
exp="$(plutil_extract ExpirationDate "$tmp")"
|
||||
|
||||
# Profiles vary in which key they use for the app identifier.
|
||||
appid="$(plutil_extract 'Entitlements.application-identifier' "$tmp")"
|
||||
# App identifier lives under Entitlements; use PlistBuddy because some key names contain dots.
|
||||
appid="$(plistbuddy_print 'Entitlements:application-identifier' "$tmp")"
|
||||
if [[ -z "$appid" ]]; then
|
||||
appid="$(plutil_extract 'Entitlements.com.apple.application-identifier' "$tmp")"
|
||||
appid="$(plistbuddy_print '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"
|
||||
printf "%-4s %-36s %-10s %-25s %-45s %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})"
|
||||
elif [[ "$appid" == "${teamid}."* && "$appid" == *"*"* ]]; then
|
||||
score=40
|
||||
reason="wildcard 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*)
|
||||
@@ -161,7 +157,4 @@ 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\""
|
||||
|
||||
|
||||
70
build_tools/macos/print_mas_build_cmd.sh
Executable file
70
build_tools/macos/print_mas_build_cmd.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ts() { date +"%H:%M:%S"; }
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
./build_tools/macos/print_mas_build_cmd.sh [--bundle-id com.dryark.strawberry] [--profile <path>]
|
||||
|
||||
What it does:
|
||||
- Tries to auto-pick a provisioning profile for the bundle id
|
||||
- Prints an exact build command you can copy/paste for build_mas_pkg.sh
|
||||
|
||||
Notes:
|
||||
- This helper intentionally does NOT try to auto-pick signing identities by parsing Apple tool output.
|
||||
Use SHA-1 identities from:
|
||||
./build_tools/macos/check_signing_identities.sh
|
||||
EOF
|
||||
}
|
||||
|
||||
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)"
|
||||
|
||||
bundle_id="com.dryark.strawberry"
|
||||
profile_path=""
|
||||
codesign_identity=""
|
||||
installer_identity=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--bundle-id) bundle_id="${2:-}"; shift 2 ;;
|
||||
--profile) profile_path="${2:-}"; shift 2 ;;
|
||||
--codesign-identity) codesign_identity="${2:-}"; shift 2 ;;
|
||||
--installer-identity) installer_identity="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "Unknown arg: $1" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$profile_path" ]]; then
|
||||
# Attempt to auto-select profile using the finder script.
|
||||
finder="${repo_root}/build_tools/macos/find_mas_provisioning_profile.sh"
|
||||
if [[ -x "$finder" ]]; then
|
||||
out="$("$finder" --bundle-id "$bundle_id" 2>/dev/null || true)"
|
||||
# Parse the line after "Recommended profile:"
|
||||
profile_path="$(printf '%s\n' "$out" | awk 'found{print $1; exit} /^Recommended profile:/{found=1} found && $0 ~ /^ \\// {print $1; exit}' | sed 's/^[[:space:]]*//')"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "==> [$(ts)] Recommended build command:"
|
||||
echo
|
||||
echo "./build_tools/macos/build_mas_pkg.sh --run --release --clean \\"
|
||||
echo " --codesign-identity \"${codesign_identity:-<SHA1 from check_signing_identities.sh>}\" \\"
|
||||
echo " --installer-identity \"${installer_identity:-<SHA1 from check_signing_identities.sh>}\" \\"
|
||||
if [[ -n "$profile_path" ]]; then
|
||||
echo " --provisionprofile \"${profile_path}\""
|
||||
else
|
||||
echo " --provisionprofile \"</path/to/profile.provisionprofile>\""
|
||||
echo
|
||||
echo "Note: could not auto-pick a provisioning profile for bundle id '${bundle_id}'."
|
||||
echo "Run:"
|
||||
echo " ./build_tools/macos/find_mas_provisioning_profile.sh --bundle-id ${bundle_id}"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user