React Native na Play Store
Publicando um app Expo/React Native na Google Play com AAB local
Um app Expo/React Native pode chegar à Google Play sem EAS, mas um AAB local só merece confiança quando os insumos de release são explícitos e reproduzíveis.
Publicar um app Expo/React Native na Google Play parece simples até o primeiro envio real: gerar um AAB, subir no Play Console e esperar a faixa interna ficar disponível.
Na prática, o AAB é só o pacote. O que decide se a build está pronta é o conjunto de validações ao redor dele: assinatura, Firebase, Sentry, anúncios, ambiente de produção, versionamento e instalação pela própria Play Store.
Este artigo resume um fluxo local usado em um app real, o Daily Sudoku: Offline Puzzle, sem depender de EAS Build.
O que o build local precisa provar
Um AAB de loja não deve ser apenas “um arquivo que compila”. Ele precisa provar que o app, instalado pela Play Store, usa os serviços corretos em produção.
No nosso caso, o checklist mínimo era:
- O pacote Android correto:
com.sunstoneapps.dailysudoku. versionCodenovo a cada upload.- Upload key local válida.
- Play App Signing ativo.
- Firebase apontando para o projeto oficial.
- Sentry inicializando com
releaseedistcoerentes. - Source maps enviados ao Sentry.
- AdMob com IDs reais de produção.
- RevenueCat com chave Android real.
- Instalação vinda da Play Store, não sideload.
Essa última parte é importante. Uma build instalada localmente pode funcionar, mas ainda não prova que a Play entregou o APK split correto, com assinatura, recursos e configuração equivalentes ao que testadores receberão.
Estrutura do comando de build
O fluxo local ficou concentrado em um script de apoio, chamado a partir da raiz:
pnpm sudoku:aab:clean
Por baixo, o comando faz o que normalmente viraria passos manuais espalhados:
carregar apps/sudoku/.env
gerar config production tipada
gerar google-services.json
validar envs obrigatórios do Sentry
validar IDs reais do AdMob
validar upload signing local
rodar expo prebuild --platform android --no-install
ajustar build.gradle nativo necessário
rodar gradlew bundleRelease --rerun-tasks
enviar source maps ao Sentry
gerar app-release.aab
O detalhe do --rerun-tasks virou obrigatório para AAB de loja depois de um bug real de cache. Para iteração local,
pnpm sudoku:aab continua útil. Para arquivo que vai ao Play Console, a versão limpa é mais segura.
O que precisa ficar fora do Git
O build exige segredos e arquivos que não devem ser versionados:
- Caminho da upload key.
- Alias da upload key.
- Senha da keystore.
- Senha da chave.
- Token do Sentry.
- Chaves de API carregadas por ambiente local.
Um .env local ignorado pelo Git é suficiente para desenvolvimento individual. Para equipe ou CI, esses valores devem ir
para um gerenciador de segredos.
Não confunda segredo com configuração pública. Alguns IDs, como app IDs públicos de SDKs, podem viver em YAML versionado se não forem secretos. Senhas, tokens e arquivos de chave ficam fora do repositório.
O upload no Play Console também precisa de evidência
Depois que o AAB existe, ainda há uma etapa que merece o mesmo cuidado do build: o upload para a faixa correta. No teste
interno do Daily Sudoku, o Console criou uma nova versão de rascunho, recebeu o app-release.aab, otimizou o bundle para
distribuição e só então liberou a revisão final.
A evidência útil não era “o upload terminou”. Era a linha da faixa interna dizendo:
Última versão: 8 (0.1.0)
Esse detalhe evita uma confusão comum em releases pequenos: achar que um arquivo enviado é automaticamente uma versão disponível. No Play Console, ainda é preciso preencher as notas, revisar alertas, confirmar a publicação e voltar à página da faixa para ver qual versão está ativa.
Um aviso não bloqueante apareceu nesse fluxo: ausência de arquivo de desofuscação. Para um app React Native/Expo com Sentry source maps enviados, isso não impediu a faixa interna, mas o aviso não deve ser ignorado para sempre. Se o projeto passar a depender mais de R8/ProGuard para reduzir tamanho ou proteger bytecode Java/Kotlin, o arquivo de mapping passa a entrar no checklist de release.
Firebase: valide pelo app instalado pela Play
No Android, o Firebase depende do google-services.json correto. Para build local, esse arquivo pode ser gerado a partir
do .env, evitando edição manual.
Depois de instalar a build pela Play Store, confirme por adb logcat ou DebugView:
gmp_app_id: 1:832961368149:android:043d6431c9c975f7fbc4dc
E confirme eventos reais:
app_opened
shop_viewed
session_start
O erro mais comum aqui é validar uma build local enquanto o app da Play ainda aponta para outro projeto Firebase. A evidência
precisa vir da build instalada por com.android.vending.
Sentry: release e dist precisam bater com o build
Para React Native, enviar source maps ao Sentry não basta. O runtime precisa inicializar com o mesmo release e dist que
foram enviados.
O formato usado foi:
[email protected]+4
dist=4
No log de inicialização, a evidência esperada era:
[crash-reporting] initialize {
"enabled": true,
"hasDsn": true,
"environment": "production",
"release": "[email protected]+4",
"dist": "4"
}
Se o Sentry receber eventos, mas com release antigo, você não tem um problema de monitoramento. Você tem um problema de build.
AdMob: diferencie no-fill de falha de integração
Em testes reais, AdMob pode retornar no-fill. Isso significa falta de inventário, não necessariamente integração quebrada.
O que procuramos no primeiro smoke não foi anúncio perfeito, mas sinais de inicialização e request:
ad_query(_aq)
Esse evento mostra que o SDK tentou consultar anúncio. Ainda é preciso validar visualmente banner, interstitial e UMP antes
de ampliar o teste, mas o ad_query já separa “SDK nem inicializou” de “não houve inventário agora”.
RevenueCat: chave não é produto
Depois de configurar a chave pública Android do RevenueCat, o erro antigo desapareceu:
RevenueCat API key is missing.
Mas apareceu outro erro:
ConfigurationError: there are no Play Store products registered in the RevenueCat dashboard for your offerings
Esse segundo erro é melhor. Ele diz que o SDK está configurado, mas a parte comercial ainda não existe: produtos Play Billing, offerings e service account do Google Play no RevenueCat.
Checklist final antes de ampliar teste interno
Antes de chamar mais testadores, eu validaria:
versionCodeinstalado pela Play Store.installerPackageName=com.android.vending.- Firebase no projeto oficial.
- Sentry com
releaseedistcorretos. - Source maps processados no Sentry.
- Eventos mínimos de analytics.
- Ausência de crash no cold start.
- AdMob emitindo ao menos request.
- UMP não bloqueando a experiência.
- RevenueCat sem erro de chave.
- Produtos/ofertas cadastrados antes de testar compras reais.
Local não significa informal
Uma build AAB local pode ser perfeitamente válida para a Google Play, mas precisa da mesma disciplina de qualquer pipeline hospedado. A máquina deve ter a versão esperada de Java, Android SDK, entradas de assinatura, variáveis de ambiente e config gerada. Se essas entradas são implícitas, o release não é reproduzível.
Para times pequenos, a resposta prática é um comando de release scriptado que falha claramente quando algum valor obrigatório está ausente. O comando deve gerar config, buildar o bundle, manter o caminho de saída previsível e evitar atalhos de debug. Um release feito de memória fica difícil de confiar depois.
O que checar depois que o AAB existe
Gerar o arquivo é só metade do trabalho. Antes de subir, inspecione os pontos que mais divergem entre local e loja:
- package name e version code;
- configuração de assinatura e expectativa do Play App Signing;
- chaves e IDs públicos de produção;
- release/dist do Sentry ou metadados equivalentes de crash reporting;
- providers de ads, billing, analytics e consent para o ambiente escolhido.
Uma build local é útil porque é rápida de repetir. Use essa vantagem para rodar um checklist pequeno de release toda vez, em vez de confiar em um único upload bem-sucedido.
Quando EAS ou CI começa a valer a pena
Build local é um bom começo quando o app é jovem e a pessoa responsável pelo release entende o ambiente. Ela fica frágil quando mais gente precisa lançar, quando credenciais passam entre máquinas ou quando auditabilidade importa mais que velocidade.
Esse é o ponto em que EAS, CI ou outro ambiente controlado deixa de ser conveniência e vira redução de variância. O objetivo não é abandonar builds locais para sempre. É saber quando o estado local virou uma dependência escondida.
Separe instalações de release e debug
Uma instalação debug é útil para desenvolvimento, mas substitui mal uma validação de release. Ela pode usar assinatura diferente, configurações de runtime diferentes, comportamento de rede diferente e caminhos de integração diferentes para billing ou ads. Se o objetivo é validar comportamento de loja, instale o artefato de release.
Essa distinção fica importante quando há vários devices conectados. Um script deve instalar o mesmo APK ou artefato derivado do AAB de forma consistente, e qualquer override por device deve ser explícito. Caso contrário, um tester pode validar o binário errado sem perceber e fazer um release real parecer mais seguro do que está.
Conclusão
Build local não é inferior a build gerenciada, mas exige disciplina. O ganho é controle: você sabe exatamente qual env foi usado, qual arquivo foi gerado, qual release foi enviado ao Sentry e qual AAB chegou no Play Console.
O custo é que o processo precisa ser reproduzível. Se a sequência depende de passos manuais escondidos, ela vai falhar no pior momento: quando você já estiver comparando logs de um app instalado pela Play Store.