Skip to content
This repository was archived by the owner on Jan 14, 2026. It is now read-only.

Build & Publish to App Store #13

Build & Publish to App Store

Build & Publish to App Store #13

name: Build & Publish to App Store
on:
push:
tags:
- 'v*.*.*' # Triggers on version tags like v1.0.0
workflow_dispatch:
jobs:
publish-ios:
runs-on: macos-latest
defaults:
run:
working-directory: ./object_detect_test
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Install Flutter dependencies
run: flutter pub get
- name: Update CocoaPods repo
run: |
cd ios
pod repo update
cd ..
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Setup App Store Connect API Key
env:
API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}
API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
run: |
mkdir -p ~/private_keys
echo "$API_KEY_BASE64" | base64 --decode > ~/private_keys/AuthKey_$API_KEY_ID.p8
chmod 600 ~/private_keys/AuthKey_$API_KEY_ID.p8
- name: Import Distribution Certificate
env:
CERTIFICATE_BASE64: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_BASE64 }}
CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
run: |
# Create keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Import certificate
CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
echo "$CERTIFICATE_BASE64" | base64 --decode > $CERTIFICATE_PATH
security import $CERTIFICATE_PATH \
-P "$CERTIFICATE_PASSWORD" \
-A \
-t cert \
-f pkcs12 \
-k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
- name: Import App Store Provisioning Profile
env:
PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_APPSTORE_PROVISIONING_PROFILE_BASE64 }}
run: |
PP_PATH=$RUNNER_TEMP/profile.mobileprovision
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode > $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Create ExportOptions.plist for App Store
env:
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
BUNDLE_ID: ${{ secrets.IOS_BUNDLE_ID }}
PROVISIONING_PROFILE_NAME: ${{ secrets.IOS_APPSTORE_PROVISIONING_PROFILE_NAME }}
run: |
cat > ios/ExportOptions.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>method</key>
<string>app-store</string>
<key>teamID</key>
<string>$TEAM_ID</string>
<key>uploadSymbols</key>
<true/>
<key>uploadBitcode</key>
<false/>
<key>signingStyle</key>
<string>manual</string>
<key>provisioningProfiles</key>
<dict>
<key>$BUNDLE_ID</key>
<string>$PROVISIONING_PROFILE_NAME</string>
</dict>
</dict>
</plist>
EOF
- name: Build IPA for App Store
run: |
flutter build ipa --release \
--export-options-plist=ios/ExportOptions.plist
- name: Upload to App Store Connect using Transporter
env:
API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
run: |
# Transporter uses the API key from the private_keys folder
xcrun iTMSTransporter -m upload \
-f build/ios/ipa/*.ipa \
-k ~/private_keys/AuthKey_$API_KEY_ID.p8 \
-i $API_KEY_ID \
-u $API_ISSUER_ID \
-t Signiant
- name: Wait for Processing (Optional)
env:
API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
run: |
echo "Build uploaded! Check App Store Connect for processing status."
echo "You can monitor at: https://appstoreconnect.apple.com"
- name: Upload IPA as Artifact
uses: actions/upload-artifact@v4
with:
name: appstore-release-ipa
path: build/ios/ipa/*.ipa
retention-days: 30
- name: Cleanup
if: always()
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
rm -rf ~/private_keys || true
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision || true