56 lines
3.4 KiB
Bash
Executable File
56 lines
3.4 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
source "$ROOT/scripts/common.sh"
|
|
PROVISIONING=()
|
|
case "${1:-}" in
|
|
--allow-provisioning-updates) PROVISIONING=(-allowProvisioningUpdates) ;;
|
|
'') ;;
|
|
-h|--help) echo 'Usage: appstore/tools/archive.sh [--allow-provisioning-updates]'; echo 'Creates an App Store archive and locally exports an IPA. Never uploads.'; exit 0 ;;
|
|
*) echo "Unknown option: $1" >&2; exit 2 ;;
|
|
esac
|
|
[[ $# -le 1 ]] || { echo 'Too many arguments' >&2; exit 2; }
|
|
OUTPUT="$(mktemp -d "$ROOT/appstore/build/$(date +%Y%m%d-%H%M%S).XXXXXX")"
|
|
DERIVED="$(mktemp -d "$TARNAX_SCRATCH_ROOT/appstore-derived.XXXXXX")"
|
|
echo "Output: $OUTPUT"
|
|
xcodebuild -project "$ROOT/Tarnax.xcodeproj" -scheme Tarnax -configuration Release \
|
|
-destination 'generic/platform=iOS' -derivedDataPath "$DERIVED" \
|
|
-archivePath "$OUTPUT/Tarnax.xcarchive" \
|
|
${PROVISIONING[@]+"${PROVISIONING[@]}"} archive 2>&1 | tee "$OUTPUT/archive.log"
|
|
APP="$OUTPUT/Tarnax.xcarchive/Products/Applications/Tarnax.app"
|
|
[[ -f "$APP/PrivacyInfo.xcprivacy" ]] || { echo 'Privacy manifest missing from archive' >&2; exit 1; }
|
|
plutil -lint "$APP/PrivacyInfo.xcprivacy"
|
|
xcrun nm -m "$APP/Tarnax" > "$DERIVED/symbols.txt"
|
|
if grep -E 'weak external.*Foundation4DateV2(ge|le)oiy' "$DERIVED/symbols.txt"; then
|
|
echo 'Incompatible Foundation Date comparison entry point detected' >&2; exit 1
|
|
fi
|
|
if grep -E 'StoreCapture|CaptureFixture' "$DERIVED/symbols.txt"; then
|
|
echo 'Screenshot fixture leaked into production archive' >&2; exit 1
|
|
fi
|
|
xcodebuild -exportArchive -archivePath "$OUTPUT/Tarnax.xcarchive" \
|
|
-exportPath "$OUTPUT/export" -exportOptionsPlist "$ROOT/appstore/build/ExportOptions-AppStore.plist" \
|
|
${PROVISIONING[@]+"${PROVISIONING[@]}"} 2>&1 | tee "$OUTPUT/export.log"
|
|
[[ -f "$OUTPUT/export/Tarnax.ipa" ]] || { echo 'IPA export missing' >&2; exit 1; }
|
|
VERIFY="$(mktemp -d "$TARNAX_SCRATCH_ROOT/appstore-verify.XXXXXX")"
|
|
trap 'rm -rf "$VERIFY"' EXIT
|
|
unzip -q "$OUTPUT/export/Tarnax.ipa" -d "$VERIFY"
|
|
codesign --verify --deep --strict "$VERIFY/Payload/Tarnax.app"
|
|
security cms -D -i "$VERIFY/Payload/Tarnax.app/embedded.mobileprovision" > "$VERIFY/profile.plist"
|
|
python3 - "$VERIFY" "$ROOT/appstore/build/latest.json" "$OUTPUT" <<'PY'
|
|
from pathlib import Path
|
|
import json,plistlib,sys,hashlib,datetime
|
|
root=Path(sys.argv[1]);app=root/'Payload/Tarnax.app'
|
|
with (app/'Info.plist').open('rb') as f:info=plistlib.load(f)
|
|
with (root/'profile.plist').open('rb') as f:profile=plistlib.load(f)
|
|
assert info['CFBundleIdentifier']=='net.absdev.Tarnax'
|
|
assert not profile.get('ProvisionedDevices'), 'Device-limited profile is not App Store distribution'
|
|
assert not profile.get('ProvisionsAllDevices'), 'Enterprise profile is not App Store distribution'
|
|
assert not profile['Entitlements'].get('get-task-allow',False), 'Debugging enabled'
|
|
assert profile['ExpirationDate']>datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
|
output=Path(sys.argv[3]);ipa=output/'export/Tarnax.ipa'
|
|
checksum=hashlib.sha256(ipa.read_bytes()).hexdigest()
|
|
(output/'export/Tarnax.ipa.sha256').write_text(checksum+' Tarnax.ipa\n')
|
|
Path(sys.argv[2]).write_text(json.dumps({'version':info['CFBundleShortVersionString'],'build':info['CFBundleVersion'],'archive':str(output/'Tarnax.xcarchive'),'ipa':str(ipa),'sha256':checksum,'distribution':'app-store-connect','uploaded':False},indent=2)+'\n')
|
|
print('Verified App Store IPA:',ipa)
|
|
PY
|