Files

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