Tema
Integração com Sentry
Quando um crash chega no Sentry, duas perguntas aparecem logo: em qual código ele aconteceu e qual update OTA o aparelho estava rodando. Esta página resolve as duas:
- Stack traces legíveis para cada update. O plugin oficial
@hot-updater/sentry-pluginenvia os source maps de cada bundle para o Sentry durante ohot-updater deploy. - Cada evento com a release do Pipa no Ar. O app grava o id da release, o id do artefato e o canal como tags do Sentry. Com isso dá para filtrar os crashes de uma release e abrir a mesma release no painel.
O Pipa no Ar não conversa com o Sentry e não recebe seus tokens. O upload acontece na máquina que roda o deploy (a sua ou o CI), direto para o Sentry. Os source maps também não vão para o Pipa no Ar: a CLI deixa os arquivos .map fora do pacote do update.
Como funciona
O withSentry() envolve o build adapter (bare, expo ou rock) no build: do hot-updater.config.ts. Depois que o bundle é gerado, ele:
- procura o bundle (
*.bundle) e o source map (*.bundle.map) na pasta do build; - com Hermes, copia o Debug ID do source map do JavaScript para o source map do Hermes e usa este no lugar;
- roda
sentry-cli sourcemaps upload --debug-id-referencecom o bundle e o source map.
O Sentry liga o crash ao source map pelo Debug ID, que o Metro grava no bundle. Por isso não é preciso criar uma release no Sentry para cada update nem mexer em release e dist no app.
O pipanoar() cuida só de database, storage e signing. O build: continua seu, então os dois se combinam sem nada especial.
Antes de começar
- O app já usa o
@sentry/react-nativee chamaSentry.init. - O projeto segue o guia rápido (ou o guia do Expo) com a versão
1.0.0-rc.24dos pacotes do hot-updater.
1. Instale o plugin
Use a mesma versão dos outros pacotes do hot-updater:
sh
npm install -D @hot-updater/sentry-plugin@1.0.0-rc.24O plugin traz o @sentry/cli (2.46.0). O binário vem nas dependências opcionais do @sentry/cli, então não desligue as dependências opcionais na instalação.
2. Configure o Metro para gerar Debug IDs
Sem isso, o Sentry não consegue ligar o crash ao source map.
React Native (bare), no metro.config.js:
js
const { getDefaultConfig } = require("@react-native/metro-config");
const { withSentryConfig } = require("@sentry/react-native/metro");
const config = getDefaultConfig(__dirname);
module.exports = withSentryConfig(config);Expo, no metro.config.js:
js
const { getSentryExpoConfig } = require("@sentry/react-native/metro");
module.exports = getSentryExpoConfig(__dirname);Se o projeto já tem outras customizações no Metro, aplique o withSentryConfig por último.
3. Envolva o build no hot-updater.config.ts
Ligue sourcemap: true no build adapter e envolva com withSentry(). O pipanoar() entra com ... como antes:
ts
import { bare } from "@hot-updater/bare";
import { withSentry } from "@hot-updater/sentry-plugin";
import { pipanoar } from "@pipanoar/hot-updater";
import { defineConfig } from "hot-updater";
export default defineConfig({
build: withSentry(bare({ enableHermes: true, sourcemap: true }), {
org: "minha-org",
project: "meu-app",
authToken: process.env.SENTRY_AUTH_TOKEN,
}),
...pipanoar({ appId: "app_xxxxxxxxxxxxxxxxxxxx", signing: true }),
updateStrategy: "appVersion",
});No Expo, troque bare(...) por expo({ sourcemap: true }), importado de @hot-updater/expo. No Rock, rock({ sourcemap: true }).
Opções do withSentry() (são as opções do @sentry/cli):
| Opção | Variável que substitui | O que é |
|---|---|---|
org | SENTRY_ORG | Slug da organização no Sentry. |
project | SENTRY_PROJECT | Slug do projeto no Sentry. |
authToken | SENTRY_AUTH_TOKEN | Token para enviar source maps. |
url | SENTRY_URL | Só para Sentry auto-hospedado. Padrão: https://sentry.io/. |
Todas são opcionais. O que ficar de fora, o sentry-cli lê das variáveis de ambiente. Então dá para deixar o config sem segredo nenhum e passar tudo pelo ambiente:
ts
build: withSentry(bare({ enableHermes: true, sourcemap: true }), {}),Para o token, crie um Organization Token no Sentry (Settings, Developer Settings, Organization Tokens). Um token pessoal também serve, com os escopos project:releases e org:read.
4. Marque os eventos com a release do Pipa no Ar
No arquivo de entrada do app, logo depois do Sentry.init:
tsx
import * as Sentry from "@sentry/react-native";
import { HotUpdater, insights } from "@hot-updater/react-native";
import { pipanoarClient } from "@pipanoar/hot-updater/client";
Sentry.init({
dsn: "https://...@o0.ingest.sentry.io/0",
});
// Lido uma vez, quando o JavaScript carrega: é o update que está rodando.
const releaseId = HotUpdater.getBundleId();
Sentry.setTags({
"ota.release_id": releaseId,
"ota.bundle_id": HotUpdater.getManifest().bundleId,
"ota.channel": HotUpdater.getChannel(),
"ota.embedded": String(releaseId === HotUpdater.getMinBundleId()),
});
function App() {
// ...
}
export default HotUpdater.wrap({
...pipanoarClient({ appId: "app_xxxxxxxxxxxxxxxxxxxx", apiKey: "COLE_A_API_KEY_AQUI" }),
updateStrategy: "appVersion",
plugins: [insights()],
})(Sentry.wrap(App));O que cada tag guarda:
| Tag | Valor | Onde aparece no painel |
|---|---|---|
ota.release_id | HotUpdater.getBundleId(): id da release que o aparelho escolheu. | Coluna Release em Releases e id no topo da release aberta. |
ota.bundle_id | HotUpdater.getManifest().bundleId: id do artefato, os arquivos do bundle. Continua o mesmo quando a release é promovida para outro canal. | Id do artefato, em Diagnóstico avançado da release. |
ota.channel | HotUpdater.getChannel(): canal do aparelho. | Coluna Canal em Releases. |
ota.embedded | true quando o app roda o bundle que veio na build nativa, sem update aplicado. |
Sem update aplicado, ota.release_id e ota.bundle_id trazem o id mínimo da build nativa (HotUpdater.getMinBundleId()), que não aparece em Releases. É para isso que serve ota.embedded.
Leia os ids na carga do JavaScript
Depois que o app baixa um update, HotUpdater.getBundleId() já passa a devolver a release nova, mesmo antes de o app reiniciar. Por isso o exemplo lê os ids no topo do arquivo, uma vez só. Não chame de novo depois de um updateBundle.
Não é preciso trocar release nem dist no Sentry.init. Mantenha o padrão do SDK do Sentry, que identifica a build nativa, e use as tags para separar os updates.
5. Variáveis no CI
Guarde o token do Sentry como segredo, junto do PIPANOAR_TOKEN. Exemplo para o workflow do Deploy pelo CI:
yaml
- name: Publicar iOS e Android
env:
PIPANOAR_TOKEN: ${{ secrets.PIPANOAR_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: minha-org
SENTRY_PROJECT: meu-app
run: |
npx hot-updater deploy -p ios -r 10
npx hot-updater deploy -p android -r 10SENTRY_ORG e SENTRY_PROJECT não são segredos; podem ficar no config, como no passo 3.
Do Sentry para o painel e de volta
De um crash para a release. No evento do Sentry, copie o valor de ota.release_id e abra a página Releases do app no painel com ?release= no fim do endereço:
https://app.pipanoar.scuderiatech.com.br/<organização>/apps/<app>?release=<ota.release_id>A release abre com rollout, cohorts, crashes medidos pelo Insights e o commit do deploy. Se o problema estiver nela, desative a release para os aparelhos voltarem à anterior.
Para ver todas as releases que usam o mesmo artefato (por exemplo, depois de um promote), use ?bundle=<ota.bundle_id>.
De uma release para os crashes. Abra a release no painel. Em Detalhes, a linha Busca no Sentry mostra a busca pronta, como ota.release_id:0190.... Clique para copiar e cole na busca de Issues do Sentry.
Problemas comuns
Source map not found. Please enable sourcemap in your build adapter. Falta sourcemap: true no build adapter dentro do withSentry().
debugId from Source map not found. O bundle saiu sem Debug ID. Confira o passo 2: o metro.config.js precisa do withSentryConfig (ou do getSentryExpoConfig no Expo). O erro aparece com Hermes; sem Hermes, o upload passa, mas o Sentry não consegue usar o source map.
O sentry-cli falha com 401 ou 403. Token errado, sem os escopos certos, ou org e project com o nome em vez do slug. Os slugs aparecem na URL do projeto no Sentry.
O stack trace continua minificado.
- O evento veio de um update publicado antes de ligar o plugin. Só os deploys seguintes têm source map.
- O evento veio do bundle da build nativa (
ota.embeddedigual atrue). Esse bundle não passa pelohot-updater deploy: os source maps dele são enviados pela integração do Sentry no build do Xcode e do Gradle. - Confira em Settings, Source Maps do projeto no Sentry se o artefato com o Debug ID chegou.
O deploy não acha o sentry-cli. A instalação pulou as dependências opcionais do @sentry/cli. Instale de novo sem --no-optional, ou aponte SENTRY_BINARY_PATH para um sentry-cli instalado na máquina.
As tags trazem o mesmo id em todas as versões. O app está sem update aplicado (ota.embedded igual a true) ou é uma build de debug, que carrega o JavaScript do Metro.
Limites
- O plugin envia um bundle por plataforma. Projetos com Re.Pack que dividem o código em vários chunks precisam enviar os outros source maps por conta própria.
- O plugin só envia source maps. Ele não cria releases nem deploys no Sentry.
- O Pipa no Ar não lê dados do Sentry. O rollback automático por crash é do SDK do hot-updater, que descarta um bundle que faz o app crashar ao abrir, e não depende desta integração.
Referências: plugin do Sentry na documentação do hot-updater e source maps com Hermes no Sentry.