Fix TestFlight upload for Xcode 14+ (#5927)

Replace deprecated altool (removed in Xcode 14) with xcodebuild
-exportArchive using App Store Connect API authentication.

Changes:
- upload_testflight.sh: Use xcodebuild with destination=upload
  and API key authentication instead of altool
- ios_testflight.yml: Pass xcarchive path and use new API key secrets

Required new GitHub secrets:
- APP_STORE_CONNECT_API_KEY_ID
- APP_STORE_CONNECT_API_ISSUER_ID
- APP_STORE_CONNECT_API_KEY (contents of .p8 file)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 22:58:43 -08:00
committed by GitHub
co-authored by Claude Opus 4.5
parent e02afd112e
commit 7afbde69f1
2 changed files with 95 additions and 29 deletions
+15 -3
View File
@@ -184,11 +184,23 @@ jobs:
- name: Upload to TestFlight
if: ${{ github.event.inputs.skip_upload != 'true' }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
# App Store Connect API Key (replaces deprecated altool with Apple ID)
# Create at: https://appstoreconnect.apple.com/access/api
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
# Write API key to file (xcodebuild needs a file path)
API_KEY_PATH=$(mktemp)
echo "$APP_STORE_CONNECT_API_KEY" > "$API_KEY_PATH"
export APP_STORE_CONNECT_API_KEY_PATH="$API_KEY_PATH"
chmod +x ./ci/github_actions/upload_testflight.sh
./ci/github_actions/upload_testflight.sh "$EAGLE0_BUILD_DIR/archive/eagle0.ipa"
./ci/github_actions/upload_testflight.sh "$EAGLE0_BUILD_DIR/archive/eagle0.xcarchive" "$APPLE_TEAM_ID" "$PROFILE_UUID"
# Cleanup
rm -f "$API_KEY_PATH"
- name: Upload IPA artifact
# Only keep artifact if we skipped TestFlight upload (for debugging)
+80 -26
View File
@@ -1,48 +1,102 @@
#!/usr/bin/env bash
# Upload IPA to TestFlight using Apple ID credentials (same as Mac notarization)
# Upload xcarchive to TestFlight using App Store Connect API Key
# This replaces the deprecated altool which was removed in Xcode 14+
#
# Uses xcodebuild -exportArchive with destination=upload, which is Apple's
# recommended approach for CI/CD pipelines.
set -euxo pipefail
IPA_PATH=${1:?Usage: upload_testflight.sh <ipa_path>}
# Ensure xcodebuild uses Xcode.app, not Command Line Tools
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
if [ ! -f "$IPA_PATH" ]; then
echo "Error: IPA file not found: $IPA_PATH"
ARCHIVE_PATH=${1:?Usage: upload_testflight.sh <xcarchive_path> <team_id> <profile_uuid>}
TEAM_ID=${2:?Missing team ID}
PROFILE_UUID=${3:?Missing provisioning profile UUID}
if [ ! -d "$ARCHIVE_PATH" ]; then
echo "Error: xcarchive not found: $ARCHIVE_PATH"
exit 1
fi
echo "Uploading to TestFlight: $IPA_PATH"
echo "Uploading to TestFlight: $ARCHIVE_PATH"
# Uses same credentials as Mac notarization:
# - APPLE_ID: Your Apple ID email
# - APP_SPECIFIC_PASSWORD: App-specific password from appleid.apple.com
# Requires App Store Connect API Key:
# - APP_STORE_CONNECT_API_KEY_ID: Key ID from App Store Connect
# - APP_STORE_CONNECT_API_ISSUER_ID: Issuer ID from App Store Connect
# - APP_STORE_CONNECT_API_KEY_PATH: Path to .p8 private key file
#
# To create an API key:
# 1. Go to https://appstoreconnect.apple.com/access/api
# 2. Click the + button to create a new key
# 3. Give it a name and Admin or App Manager access
# 4. Download the .p8 file (you can only download it once!)
# 5. Note the Key ID and Issuer ID shown on the page
if [ -z "${APPLE_ID:-}" ]; then
echo "Error: APPLE_ID environment variable not set"
if [ -z "${APP_STORE_CONNECT_API_KEY_ID:-}" ]; then
echo "Error: APP_STORE_CONNECT_API_KEY_ID environment variable not set"
echo "Create an API key at https://appstoreconnect.apple.com/access/api"
exit 1
fi
if [ -z "${APP_SPECIFIC_PASSWORD:-}" ]; then
echo "Error: APP_SPECIFIC_PASSWORD environment variable not set"
if [ -z "${APP_STORE_CONNECT_API_ISSUER_ID:-}" ]; then
echo "Error: APP_STORE_CONNECT_API_ISSUER_ID environment variable not set"
exit 1
fi
echo "Uploading with xcrun altool..."
# Capture output to check for errors (altool may return 0 even on failure)
OUTPUT=$(xcrun altool --upload-app \
--type ios \
--file "$IPA_PATH" \
--username "$APPLE_ID" \
--password "$APP_SPECIFIC_PASSWORD" 2>&1) || true
echo "$OUTPUT"
# Check for error indicators in output
if echo "$OUTPUT" | grep -q "ERROR:"; then
echo "ERROR: Upload failed. See error messages above."
if [ -z "${APP_STORE_CONNECT_API_KEY_PATH:-}" ]; then
echo "Error: APP_STORE_CONNECT_API_KEY_PATH environment variable not set"
exit 1
fi
if [ ! -f "$APP_STORE_CONNECT_API_KEY_PATH" ]; then
echo "Error: API key file not found: $APP_STORE_CONNECT_API_KEY_PATH"
exit 1
fi
# Create export options plist with upload destination
EXPORT_OPTIONS_PLIST=$(mktemp)
trap "rm -f $EXPORT_OPTIONS_PLIST" EXIT
cat > "$EXPORT_OPTIONS_PLIST" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>upload</string>
<key>method</key>
<string>app-store-connect</string>
<key>teamID</key>
<string>$TEAM_ID</string>
<key>uploadSymbols</key>
<true/>
<key>signingStyle</key>
<string>manual</string>
<key>signingCertificate</key>
<string>Apple Distribution: Daniel Crosby (UWJ88DX8WQ)</string>
<key>provisioningProfiles</key>
<dict>
<key>net.eagle0.eagle</key>
<string>$PROFILE_UUID</string>
</dict>
</dict>
</plist>
EOF
echo "Export options:"
cat "$EXPORT_OPTIONS_PLIST"
echo "Uploading to App Store Connect..."
# Use xcodebuild to upload with App Store Connect API authentication
xcodebuild -exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportOptionsPlist "$EXPORT_OPTIONS_PLIST" \
-authenticationKeyPath "$APP_STORE_CONNECT_API_KEY_PATH" \
-authenticationKeyID "$APP_STORE_CONNECT_API_KEY_ID" \
-authenticationKeyIssuerID "$APP_STORE_CONNECT_API_ISSUER_ID"
echo "Upload complete! Check App Store Connect for processing status."
echo "The build should appear in TestFlight within 15-30 minutes after processing."