Configurando CI/CD para React Native com GitHub Actions e Fastlane
Builds manuais são o pesadelo de todo desenvolvedor. Cada release se torna um processo de várias horas: rodar testes, fazer build para iOS, fazer build para Android, assinar, subir para TestFlight, subir para Play Store, rezar para nada quebrar. Depois de configurar pipelines de CI/CD para mais de 20 apps React Native, estou compartilhando a configuração exata que funciona em produção.
Ao final deste guia, você terá um pipeline totalmente automatizado que faz build, testa e faz deploy do seu app React Native para iOS e Android com um único git push. Sem mais builds manuais. Nunca mais.
Por Que CI/CD Importa para React Native
Aqui está o que um CI/CD adequado te dá:
- Builds automatizados: Push para main, receba builds no TestFlight e Play Store
- Releases consistentes: Sem mais problemas de "funciona na minha máquina"
- Economia de tempo: 3-4 horas de trabalho manual → 0 horas
- Iteração mais rápida: Deploy de builds beta várias vezes por dia
- Capture bugs cedo: Testes automatizados rodam em cada PR
- Escalabilidade do time: Qualquer desenvolvedor pode disparar releases
A Stack: GitHub Actions + Fastlane
GitHub Actions: CI/CD gratuito integrado ao GitHub. 2.000 minutos grátis/mês para repos privados (suficiente para a maioria dos projetos).
Fastlane: Ferramenta de automação baseada em Ruby que lida com toda a complexidade iOS/Android (assinatura de código, screenshots, metadados, uploads).
Esta combinação é testada em batalha, gratuita para times pequenos e escala para apps enterprise com milhões de usuários.
Pré-requisitos
Antes de começar, você vai precisar de:
- App React Native (0.70+) com projetos iOS e Android
- Conta Apple Developer ($99/ano)
- Conta Google Play Developer ($25 única vez)
- Repositório GitHub
- Conhecimento básico de terminal/linha de comando
Parte 1: Configurando Fastlane
Instalar Fastlane
# Instalar Fastlane via RubyGems
sudo gem install fastlane -NV
# Ou usando Homebrew (macOS)
brew install fastlane
Inicializar Fastlane para iOS
cd ios
fastlane init
O Fastlane vai te fazer perguntas. Escolha:
- Para que você gostaria de usar o fastlane? → "Automatizar distribuição beta para TestFlight"
- Digite seu Apple ID e senha
- Selecione seu app da lista (ou crie um novo)
Isso cria ios/fastlane/Fastfile e ios/fastlane/Appfile.
Configurar Fastfile iOS
Edite ios/fastlane/Fastfile:
default_platform(:ios)
platform :ios do
desc "Enviar novo build beta para TestFlight"
lane :beta do
# Incrementar build number (baseado no mais recente do TestFlight)
increment_build_number(xcodeproj: "SeuApp.xcodeproj")
# Fazer build do app
build_app(
scheme: "SeuApp",
export_method: "app-store",
export_options: {
provisioningProfiles: {
"com.suaempresa.seuapp" => "match AppStore com.suaempresa.seuapp"
}
}
)
# Upload para TestFlight
upload_to_testflight(
skip_waiting_for_build_processing: true,
skip_submission: true,
distribute_external: false
)
end
desc "Rodar testes"
lane :test do
run_tests(
scheme: "SeuApp",
devices: ["iPhone 15 Pro"]
)
end
end
Inicializar Fastlane para Android
cd android
fastlane init
Escolha "Automatizar distribuição beta para Google Play" quando solicitado.
Configurar Fastfile Android
Edite android/fastlane/Fastfile:
default_platform(:android)
platform :android do
desc "Enviar novo build beta para Play Store"
lane :beta do
# Incrementar version code
gradle(
task: "clean bundleRelease",
properties: {
"android.injected.signing.store.file" => ENV["KEYSTORE_PATH"],
"android.injected.signing.store.password" => ENV["KEYSTORE_PASSWORD"],
"android.injected.signing.key.alias" => ENV["KEY_ALIAS"],
"android.injected.signing.key.password" => ENV["KEY_PASSWORD"],
}
)
# Upload para Play Store (track de teste interno)
upload_to_play_store(
track: 'internal',
release_status: 'draft',
aab: 'app/build/outputs/bundle/release/app-release.aab'
)
end
desc "Rodar testes"
lane :test do
gradle(task: "test")
end
end
Parte 2: Configuração de Assinatura de Código
Assinatura de Código iOS (Match)
O Fastlane Match armazena certificados em um repo Git privado (criptografado). Esta é a abordagem mais limpa para times.
# Inicializar Match
cd ios
fastlane match init
Escolha "git" e forneça uma URL de repo privado (crie um no GitHub para certificados).
# Gerar certificados e profiles
fastlane match appstore
fastlane match development
Atualize seu Fastfile para usar Match:
lane :beta do
# Sincronizar certificados
match(type: "appstore", readonly: true)
increment_build_number(xcodeproj: "SeuApp.xcodeproj")
build_app(scheme: "SeuApp", export_method: "app-store")
upload_to_testflight
end
Assinatura de Código Android
Gere um keystore para builds de release:
cd android/app
keytool -genkey -v -keystore release.keystore -alias release \
-keyalg RSA -keysize 2048 -validity 10000
IMPORTANTE: Nunca faça commit deste keystore no Git. Armazene-o de forma segura e referencie via variáveis de ambiente.
Atualize android/app/build.gradle:
android {
...
signingConfigs {
release {
if (project.hasProperty('RELEASE_STORE_FILE')) {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
signingConfig signingConfigs.release
...
}
}
}
Parte 3: Workflows do GitHub Actions
Criar Workflow iOS
Crie .github/workflows/ios-deploy.yml:
name: iOS Build & Deploy
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-ios:
runs-on: macos-14
steps:
- name: Checkout código
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Instalar dependências
run: yarn install --frozen-lockfile
- name: Instalar CocoaPods
run: |
cd ios
bundle install
bundle exec pod install
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: ios
- name: Rodar testes
run: |
cd ios
bundle exec fastlane test
- name: Build e deploy para TestFlight
if: github.ref == 'refs/heads/main'
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
run: |
cd ios
bundle exec fastlane beta
Criar Workflow Android
Crie .github/workflows/android-deploy.yml:
name: Android Build & Deploy
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- name: Checkout código
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: android
- name: Instalar dependências
run: yarn install --frozen-lockfile
- name: Rodar testes
run: |
cd android
bundle exec fastlane test
- name: Decodificar keystore
if: github.ref == 'refs/heads/main'
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore
- name: Build e deploy para Play Store
if: github.ref == 'refs/heads/main'
env:
KEYSTORE_PATH: ${{ github.workspace }}/android/app/release.keystore
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
PLAY_STORE_JSON_KEY_DATA: ${{ secrets.PLAY_STORE_JSON_KEY_DATA }}
run: |
cd android
bundle exec fastlane beta
Parte 4: Configuração de Secrets do GitHub
Navegue até repo GitHub → Settings → Secrets and variables → Actions. Adicione estes secrets:
Secrets iOS
- MATCH_PASSWORD: Senha para o repo de certificados criptografados
- MATCH_GIT_BASIC_AUTHORIZATION: PAT do GitHub codificado em Base64 para acesso ao repo Match
- FASTLANE_USER: Email do seu Apple ID
- FASTLANE_PASSWORD: Senha do seu Apple ID
- FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: Senha específica do app de appleid.apple.com
Secrets Android
- ANDROID_KEYSTORE_BASE64: Seu arquivo keystore codificado em base64
- KEYSTORE_PASSWORD: Senha do keystore
- KEY_ALIAS: Alias da chave
- KEY_PASSWORD: Senha da chave
- PLAY_STORE_JSON_KEY_DATA: JSON da conta de serviço do Google Play Console
Codificando Keystore para GitHub Secrets
# Codificar keystore para base64
base64 -i android/app/release.keystore -o keystore.txt
# Copie o conteúdo e adicione aos GitHub Secrets
cat keystore.txt
Obtendo JSON da Conta de Serviço Google Play
- Vá para Google Play Console → Setup → Acesso à API
- Crie conta de serviço (link para Google Cloud)
- Conceda papel de "Release Manager"
- Baixe a chave JSON
- Codifique e adicione aos GitHub Secrets
Parte 5: Testando Seu Pipeline
Teste Localmente Primeiro
# Testar build iOS localmente
cd ios
bundle exec fastlane beta
# Testar build Android localmente
cd android
bundle exec fastlane beta
Corrija quaisquer problemas antes de fazer push para GitHub Actions.
Teste no GitHub Actions
Faça push para uma branch de feature e crie um PR:
git checkout -b test-ci-cd
git add .
git commit -m "Adicionar pipeline CI/CD"
git push origin test-ci-cd
Isso dispara os workflows, mas não faz deploy (apenas roda testes). Verifique a aba Actions para resultados.
Deploy para Produção
Faça merge para main para disparar deploy completo:
git checkout main
git merge test-ci-cd
git push origin main
Assista a mágica acontecer na aba Actions. Em 20-30 minutos, você terá builds no TestFlight e track interno da Play Store.
Parte 6: Configuração Avançada
Incremento de Versão
Automatize incrementos de versão com Fastlane:
# iOS - no Fastfile
lane :bump_version do
increment_version_number(
bump_type: "patch" # ou "minor", "major"
)
end
# Android - no Fastfile
lane :bump_version do
increment_version_code(
gradle_file_path: "app/build.gradle"
)
end
Notificações Slack
Seja notificado quando builds completarem:
# Adicionar ao Fastfile
after_all do |lane|
slack(
message: "Deploy do novo build realizado com sucesso!",
channel: "#mobile-releases",
slack_url: ENV["SLACK_WEBHOOK_URL"]
)
end
error do |lane, exception|
slack(
message: "Build falhou: #{exception.message}",
channel: "#mobile-releases",
slack_url: ENV["SLACK_WEBHOOK_URL"],
success: false
)
end
Múltiplos Ambientes
Configure ambientes de staging e produção:
lane :staging do
match(type: "adhoc")
build_app(
scheme: "SeuApp-Staging",
export_method: "ad-hoc"
)
firebase_app_distribution(
app: ENV["FIREBASE_APP_ID_STAGING"]
)
end
lane :production do
match(type: "appstore")
build_app(
scheme: "SeuApp-Production",
export_method: "app-store"
)
upload_to_testflight
end
Parte 7: Solução de Problemas Comuns
iOS: Problemas de Certificado
Se você receber erros de certificado:
# Regenerar certificados
fastlane match nuke development
fastlane match nuke appstore
fastlane match development
fastlane match appstore
Android: Falha no Build Gradle
Aumente a memória do Gradle em android/gradle.properties:
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
GitHub Actions: Sem Minutos
Runners macOS usam 10x minutos. Otimize:
- Rodando apenas na branch
mainpara deploys - Usando cache para dependências
- Rodando testes em runners Linux mais rápidos quando possível
Resultados do Mundo Real
Depois de implementar este pipeline para nossos clientes:
- Tempo de build manual: 3-4 horas → 0 horas
- Frequência de release: Semanal → Diária
- Falhas de build: 30% → 5% (capturadas no CI antes do release)
- Tempo para corrigir bugs: 2-3 dias → 4 horas (iteração rápida)
- Velocidade do time: Melhoria de 2x (devs focam em features, não em builds)
Análise de Custos
Aqui está o custo mensal desta configuração:
- GitHub Actions: Grátis (2.000 minutos/mês para repos privados)
- Minutos adicionais: $0,08/minuto para runners macOS (se exceder o tier grátis)
- Fastlane: Grátis (open source)
- Apple Developer: $99/ano
- Google Play Developer: $25 única vez
Total: Efetivamente grátis para a maioria dos times pequenos e médios.
Melhores Práticas
- Sempre teste localmente primeiro - Não faça debug no CI/CD, é lento e gasta minutos
- Use cache agressivamente - Cache node_modules, Pods, Gradle
- Rode testes em cada PR - Capture bugs antes de chegarem na main
- Separe jobs de teste e deploy - Rode testes em cada PR, deploy apenas na main
- Monitore seus tempos de build - Otimize se builds excederem 15-20 minutos
- Mantenha secrets seguros - Use GitHub Secrets, nunca faça commit no Git
- Documente seu pipeline - Adicione README explicando a configuração para novos membros
Conclusão: Automatize Tudo
Configurar CI/CD para React Native leva um dia de trabalho, mas economiza centenas de horas durante a vida de um projeto. Builds manuais são propensos a erros, demorados e não escalam conforme seu time cresce.
Com esta configuração, você pode:
- Deploy para TestFlight e Play Store com um único
git push - Rodar testes automatizados em cada mudança de código
- Lançar correções de bugs em horas, não dias
- Integrar novos desenvolvedores sem ensiná-los o processo de build
- Dormir melhor sabendo que seus releases são consistentes e confiáveis
A configuração inicial pode parecer complexa, mas siga este guia passo a passo e você terá um pipeline CI/CD pronto para produção até o final do dia. Seu eu do futuro vai te agradecer.