Configurare da zero la CI/CD iOS su GitHub Actions
Questa è l’intera pipeline per un’app iOS su GitHub Actions, nell’ordine in cui la costruisci. Prima compilazione e test. Poi la firma. Poi archivio e upload. Ogni passo è piccolo e lo YAML è pronto da copiare. Funziona sui runner ospitati da GitHub e su un Mac dedicato. L’unica riga che cambia è runs-on.
1. La struttura
name: ios
on:
pull_request:
push:
branches: [main]
concurrency:
group: ios-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: macos-latest # or [self-hosted, macOS, your-label]
timeout-minutes: 30
steps:
- uses: actions/checkout@v42. Scegli Xcode
Le immagini ospitate includono diverse versioni di Xcode. Scegline una in modo esplicito, così un aggiornamento dell’immagine non cambia la build a tua insaputa:
- run: sudo xcode-select -s /Applications/Xcode_26.6.app
- run: xcodebuild -versionSu un Mac dedicato la versione si fissa una volta sulla macchina, e questo step sparisce.
3. Dipendenze
- uses: actions/cache@v4
with:
path: |
~/Library/Caches/org.swift.swiftpm
Pods
key: ${{ runner.os }}-deps-${{ hashFiles('**/Package.resolved', '**/Podfile.lock') }}
- run: pod install --repo-update # CocoaPods projects only4. Build e test su un simulatore
- run: |
xcodebuild test \
-scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 17' \
-resultBundlePath TestResults \
| xcpretty
- uses: actions/upload-artifact@v4
if: failure()
with: { name: test-results, path: TestResults }Per le pull request fermati qui. Tutto ciò che segue è lavoro di rilascio e va solo su main.
5. Firma del codice senza un login Mac condiviso
Usa una API key di App Store Connect invece di un Apple ID. Salva l’ID della chiave, l’issuer ID e il contenuto del file .p8 come secret del repository. Poi lascia che xcodebuild firmi con profili gestiti nel cloud:
- run: echo "${{ secrets.ASC_KEY_P8 }}" > AuthKey.p8
- run: |
xcodebuild archive \
-scheme MyApp -configuration Release \
-archivePath build/MyApp.xcarchive \
-allowProvisioningUpdates \
-authenticationKeyPath "$PWD/AuthKey.p8" \
-authenticationKeyID "${{ secrets.ASC_KEY_ID }}" \
-authenticationKeyIssuerID "${{ secrets.ASC_ISSUER_ID }}"6. Esporta e carica su TestFlight
- run: |
xcodebuild -exportArchive \
-archivePath build/MyApp.xcarchive \
-exportPath build/export \
-exportOptionsPlist ExportOptions.plist \
-allowProvisioningUpdates \
-authenticationKeyPath "$PWD/AuthKey.p8" \
-authenticationKeyID "${{ secrets.ASC_KEY_ID }}" \
-authenticationKeyIssuerID "${{ secrets.ASC_ISSUER_ID }}"
- run: |
xcrun altool --upload-app -f build/export/MyApp.ipa -t ios \
--apiKey "${{ secrets.ASC_KEY_ID }}" --apiIssuer "${{ secrets.ASC_ISSUER_ID }}"Molti team mettono i passi 5 e 6 dentro fastlane. I comandi sotto sono gli stessi.
7. Dove finiscono i minuti, e la scelta del runner
Su un runner ospitato, i passi 2 e 3 si ripetono a ogni job e si pagano circa da $0.062 a $0.102 al minuto. Su un Mac dedicato avvengono una volta e le cache restano calde. Per questo i team che superano circa 2,242 minuti al mese spostano l’intera pipeline su una macchina a prezzo fisso. Cambiano solo la riga runs-on. La guida alla pipeline su hardware dedicato copre quella parte.
Domande frequenti
Mi serve un Mac per la CI iOS su GitHub Actions?
+
Sì. Compilare e firmare app iOS richiede Xcode, che gira solo su macOS. Il job di build ha quindi bisogno di un runner macOS, ospitato da GitHub o un Mac che controlli tu.
Come firmo un’app iOS in CI senza il mio Apple ID?
+
Crea una API key di App Store Connect. Salva il suo ID, l’issuer ID e il contenuto del .p8 come secret, e passali a xcodebuild con -allowProvisioningUpdates. Sul runner non servono né un Apple ID né la password del portachiavi.
I test devono girare su ogni pull request?
+
Compila e testa su ogni pull request con un solo simulatore. Archivio, firma e upload su TestFlight tienili solo su main. Sono gli step lenti e costosi.
Cosa cambia quando sposto questa pipeline su un Mac dedicato?
+
La riga runs-on. Scelta di Xcode e cache delle dipendenze non servono più, perché la macchina le conserva tra un job e l’altro. E davanti alla build non c’è nessuna coda.