Skip to content

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-plugin envia os source maps de cada bundle para o Sentry durante o hot-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:

  1. procura o bundle (*.bundle) e o source map (*.bundle.map) na pasta do build;
  2. com Hermes, copia o Debug ID do source map do JavaScript para o source map do Hermes e usa este no lugar;
  3. roda sentry-cli sourcemaps upload --debug-id-reference com 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 ​

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

O 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çãoVariável que substituiO que é
orgSENTRY_ORGSlug da organização no Sentry.
projectSENTRY_PROJECTSlug do projeto no Sentry.
authTokenSENTRY_AUTH_TOKENToken para enviar source maps.
urlSENTRY_URLSó 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:

TagValorOnde aparece no painel
ota.release_idHotUpdater.getBundleId(): id da release que o aparelho escolheu.Coluna Release em Releases e id no topo da release aberta.
ota.bundle_idHotUpdater.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.channelHotUpdater.getChannel(): canal do aparelho.Coluna Canal em Releases.
ota.embeddedtrue 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 10

SENTRY_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.embedded igual a true). Esse bundle não passa pelo hot-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.