CI/CD Avançado para Projetos Swift: Otimizando Pipelines com GitHub Actions
A automação de build, testes e deploy é uma parte essencial do desenvolvimento moderno para iOS e macOS. Pipelines básicos de CI/CD resolvem tarefas simples, mas em projetos reais com Swift, Swift Package Manager (SPM), múltiplos targets e dependências complexas, é necessária uma abordagem avançada. Neste artigo, vamos explorar como elevar seu processo de CI/CD a um novo nível com GitHub Actions, usando builds matriciais, otimização de cache, testes paralelos e deploy automático no TestFlight / App Store.
1. Build Matricial (Matrix Builds) para Multiplataforma
Um dos principais recursos do GitHub Actions é a estratégia matricial. Ela permite executar o mesmo workflow com diferentes parâmetros (versão do Xcode, esquema de build, plataforma). Isso é especialmente útil para projetos Swift que suportam iOS, macOS, tvOS e watchOS.
Exemplo de configuração de matriz para um projeto Swift:
jobs: build: strategy: matrix: platform: [iOS, macOS, tvOS] xcode-version: ['15.0', '15.2'] exclude: - platform: tvOS xcode-version: '15.0' # Exclui combinação incompatível runs-on: macos-14 steps: - uses: actions/checkout@v4 - name: Select Xcode ${{ matrix.xcode-version }} run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode-version }}.app - name: Build for ${{ matrix.platform }} run: | xcodebuild clean build \ -scheme MyApp \ -destination "platform=${{ matrix.platform }}" \ CODE_SIGN_IDENTITY="" \ CODE_SIGNING_REQUIRED=NOEssa abordagem permite verificar o build em todas as plataformas e versões do Xcode suportadas com um único push, economizando tempo do desenvolvedor.
2. Cache Avançado: Acelerando o Build em 2 a 3 Vezes
O build de projetos Swift é um processo que consome muitos recursos. O cache das dependências do SPM e do DerivedData pode reduzir o tempo do pipeline em 50-70%. Em vez de um cache básico, use chaves personalizadas que considerem as versões das dependências.
Cache otimizado para SPM e Xcode:
- name: Cache SPM uses: actions/cache@v3 with: path: | .build SourcePackages key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | ${{ runner.os }}-spm-
- name: Cache DerivedData uses: actions/cache@v3 with: path: ~/Library/Developer/Xcode/DerivedData key: ${{ runner.os }}-derived-${{ hashFiles('**/*.swift') }} restore-keys: | ${{ runner.os }}-derived-Importante: Use hashFiles('**/Package.resolved') para invalidar o cache com precisão quando as dependências mudarem. Para o DerivedData, a chave pode ser o hash de todos os arquivos Swift — isso garante que o cache seja atualizado apenas quando houver mudanças reais no código.
3. Testes Paralelos e Relatórios
Para grandes projetos Swift com centenas de testes unitários, a execução sequencial pode levar de 20 a 30 minutos. A solução é dividir os testes em grupos e executá-los em paralelo em vários runners.
Implementação de testes paralelos:
test: strategy: matrix: test-group: [1, 2, 3, 4] runs-on: macos-14 steps: - uses: actions/checkout@v4 - name: Run tests (Group ${{ matrix.test-group }}) run: | xcodebuild test \ -scheme MyApp \ -destination "platform=iOS Simulator,name=iPhone 15 Pro" \ -only-testing:MyAppTests/TestGroup${{ matrix.test-group }} \ CODE_SIGN_IDENTITY="" \ CODE_SIGNING_REQUIRED=NO - name: Upload test results if: always() uses: actions/upload-artifact@v4 with: