ReactNativeでSentryのソースマップを利用する

この記事は207 Advent Calendar 2022 19日目 の記事です。

### はじめに

11月から207でフロントエンドエンジニアをしているalternacrowです。
ここ１ヶ月ほど業務の片手間でSentryに投げたエラーとソースマップがうまく紐付かない問題の調査をしていました。
アプリはExpo Bare Workflowで開発しており、いつからかSentryのエラーに該当コードが表示されない状態になっていました。
Sentryについては公式に書いてあるとおり設定する程度の経験しかなく、深堀りする機会も無かったので色々と調査したことを備忘録も兼ねてまとめます。

### 設定について

基本的には公式のとおりに設定していけば間違い無いです。

Bare React Native: https://docs.sentry.io/platforms/react-native/
Expo(Bare/Managed): https://docs.expo.dev/guides/using-sentry/
マニュアルセットアップ: https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/

Expo Managed Workflowは特に迷うこともなく、sentry-expoと依存関係をインストールし、expo.hooks.postPublishの設定を追加するだけです。

Bare React NativeやExpo Bare Workflowの場合はsentry-wizardの実行以外にもマニュアルセットアップが必要になります。
特にAndroidは[この設定](https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/#enable-gradle-integration)が無いとソースマップがアップロードされないため、注意が必要です。
```gradle
// android/app/build.gradle
apply from: "../../node_modules/@sentry/react-native/sentry.gradle"
```

Bare React Nativeの場合は上記の設定でビルド時にソースマップがアップロードされるようになります。
ExpoのようにOTAが無いので他に気にすることが無いのが良いですね。

Expo Bare Workflowについては、sentry-wizardやマニュアルセットアップ以外はsentry-expoをそのまま利用できますし、OTAにexpo publishを利用していればapp.jsonのexpo.hooks.postPublishもそのまま利用できます。

もし、releaseやdistributionの指定を行いたい場合は、`SENTRY_RELEASE`と`SENTRY_DIST`の環境変数を利用できます。
（expo.hooks.postPublishにもrelease、distributionは設定可能）
Expo/EASのリリースチャンネルやアプリバージョンと合わせておくと紐付きが分かりやすくて良いかもしれません。

### EASによるOTA

Expoのbuild/publishはEASのbuild/updateへの移行を推奨しているようです。
(eas.jsonにeas update用の設定があると、expo publishでeas updateを使えと怒られる）
EASへの移行は、channelとbranchの紐付けやランタイムバージョンについて理解しておく必要はありますが、複雑なことをしようとしなければ難しいことは無さそうです。

ただ、app.jsonのexpo.hooks.postPublishはexpo publishをした場合にhookされるものなので、eas updateではhookしないため対応が必要になります。
（Expo Managedのeas buildはexpo.hooks.postPublishで対応してくれる）
https://docs.expo.dev/guides/using-sentry/#uploading-sourcemaps-at-build-time

eas update時にバンドルされたファイルがdist/bundles配下に設置されるので、下記のようなスクリプトでsentryにアップロードします。

```shell
cd $_PROJECT_ROOT/dist/bundles

cp ios-*.js      $_IOS_BUNDLE
cp ios-*.map     $_IOS_BUNDLE_MAP
cp android-*.js  $_ANDROID_BUNDLE
cp android-*.map $_ANDROID_BUNDLE_MAP

SENTRY_PROPERTIES=sentry.properties \ # orgやproject,auth.tokenを設定する
npx sentry-cli releases \
  files $_IOS_RELEASE \
  upload-sourcemaps \
  --dist $_IOS_DISTRIBUTION \
  $_IOS_BUNDLE $_IOS_BUNDLE_MAP

SENTRY_PROPERTIES=sentry.properties \
npx sentry-cli releases \
  files $_ANDROID_RELEASE \
  upload-sourcemaps \
  --dist $_ANDROID_DISTRIBUTION \
  $_ANDROID_BUNDLE $_ANDROID_BUNDLE_MAP
```

クライアントでSentryの初期化時にreleaseとdistributionの指定をしていない場合、発行される iOS/Android Update IDをdistributionに指定する必要があります。
詳細はドキュメントを確認してください。

### 手動でソースマップを作成する

ブラックボックスだとよく分かんないし、ソースマップ作成/アップロードのスクリプトを作ってCI/CDに設定すればコントロール出来てみんな幸せじゃん、そんなふうに考えていた時期が俺にもありました。

手動バンドルについては公式に記載されています。
https://docs.sentry.io/platforms/react-native/sourcemaps/

しかし、OTA時のソースマップについては勝手が違うようで、手動バンドルしたものでは機能しませんでした。
（※ビルド時のソースマップについてはドキュメント記載の手順で動作確認できた。）
現在公式では手動でソースマップをバンドルするのは非推奨としています。
https://docs.sentry.io/platforms/react-native/manual-setup/hermes/
> Currently, there is an issue when bundling the hermes bundle and source maps manually. We recommend not bundling these manually until this is fixed. Follow the [issue](https://github.com/facebook/react-native/issues/34043) for more details.

これに気づかず、今回の調査では大半の時間をここで無駄にしたように思います。
はじめにドキュメントを隅から隅まで読む、大事ですね。

余談ですが、下記のようにbundleのオプションに`--sourcemap-sources-root`のオプションを指定すれば、sentry-cliでアップロードする際に`--strip-prefix`でパスを調整する必要も無くなります。

```shell
npx react-native bundle \
--platform ios \
--dev false \
--entry-file index.js \
--sourcemap-sources-root /path/to/project/root \
--reset-cache \
--bundle-output main.jsbundle \
--sourcemap-output main.jsbundle.map \
--minify true
```

### さいごに

207株式会社では、配達員向け効率化アプリ「TODOCUサポーター」や配送管理システム「TODOCUクラウド」を開発しています。
207では開発メンバーを大絶賛募集中です:+1:
もし少しでもご興味がありましたら以下のnotionをご覧ください:pray:

https://207-inc.super.site/recruit

https://207-inc.super.site