Guia

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@v4

2. 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 -version

Num 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 only

4. 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.

Guias relacionados