Configurando CI/CD de iOS no GitHub Actions do zero
Este é o pipeline inteiro de um app iOS no GitHub Actions, na ordem em que você monta. Primeiro compilar e testar. Depois a assinatura. Depois arquivar e enviar. Cada passo é pequeno e o YAML está pronto para copiar. Ele funciona em runners hospedados pelo GitHub e num Mac dedicado. A única linha que muda é runs-on.
1. O esqueleto
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. Escolha o Xcode
As imagens hospedadas trazem várias versões do Xcode. Escolha uma de forma explícita para que uma atualização da imagem não mude o seu build sem você saber:
- run: sudo xcode-select -s /Applications/Xcode_26.6.app
- run: xcodebuild -versionNum Mac dedicado, a versão é fixada uma vez na máquina e este passo some.
3. Dependências
- 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 testes num simulador
- 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 }Para pull requests, pare aqui. Tudo o que vem abaixo é trabalho de release e fica só na main.
5. Assinatura de código sem um login de Mac compartilhado
Use uma API key do App Store Connect em vez de um Apple ID. Guarde o key ID, o issuer ID e o conteúdo do .p8 como secrets do repositório. E deixe o xcodebuild assinar com perfis gerenciados na nuvem:
- 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. Exporte e envie para o 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 }}"Muitas equipes colocam os passos 5 e 6 dentro do fastlane. Os comandos por baixo são os mesmos.
7. Para onde vão os minutos, e a escolha do runner
Num runner hospedado, os passos 2 e 3 se repetem em cada job e são cobrados a cerca de $0.062 a $0.102 o minuto. Num Mac dedicado, eles acontecem uma vez e os caches continuam aquecidos. Por isso equipes que passam de cerca de 2,242 minutos por mês levam o pipeline inteiro para uma máquina de preço fixo. Elas mudam só a linha runs-on.
Perguntas frequentes
Preciso de um Mac para rodar CI de iOS no GitHub Actions?
+
Sim. Compilar e assinar apps iOS exige o Xcode, que só roda no macOS. Então o job de build precisa de um runner macOS, hospedado pelo GitHub ou um Mac que você controla.
Como assino um app iOS no CI sem o meu Apple ID?
+
Crie uma API key do App Store Connect. Guarde o ID dela, o issuer ID e o conteúdo do .p8 como secrets, e passe tudo para o xcodebuild com -allowProvisioningUpdates. Não é preciso nenhum Apple ID nem senha de chaveiro no runner.
Os testes devem rodar em cada pull request?
+
Compile e teste em cada pull request num simulador. Deixe o arquivamento, a assinatura e o envio para o TestFlight só na main. Esses são os passos lentos e caros.
O que muda quando levo este pipeline para um Mac dedicado?
+
A linha runs-on. A escolha do Xcode e o cache de dependências deixam de ser necessários, porque a máquina guarda tudo entre jobs. E não há fila na frente do build.