···2828#
2929#
30303131-# Sentry DSN for telemetry
3232-EXPO_PUBLIC_SENTRY_DSN=
3333-3431# Bitdrift API key. If undefined, Bitdrift will be disabled.
3532EXPO_PUBLIC_BITDRIFT_API_KEY=
3633
+1-10
Dockerfile
···3434# If not set by GitHub workflows, we're probably in Render
3535ENV EXPO_PUBLIC_BUNDLE_IDENTIFIER=${EXPO_PUBLIC_BUNDLE_IDENTIFIER:-$RENDER_GIT_COMMIT}
36363737-#
3838-# Sentry
3939-#
4040-ARG SENTRY_AUTH_TOKEN
4141-ENV SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN:-unknown}
4242-ARG EXPO_PUBLIC_SENTRY_DSN
4343-ENV EXPO_PUBLIC_SENTRY_DSN=$EXPO_PUBLIC_SENTRY_DSN
44374538#
4639# Copy everything into the container
···6457 echo "EXPO_PUBLIC_RELEASE_VERSION=$EXPO_PUBLIC_RELEASE_VERSION" >> .env && \
6558 echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$EXPO_PUBLIC_BUNDLE_IDENTIFIER" >> .env && \
6659 echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env && \
6767- echo "EXPO_PUBLIC_SENTRY_DSN=$EXPO_PUBLIC_SENTRY_DSN" >> .env && \
6860 npm install --global yarn && \
6961 yarn && \
7062 yarn intl:build 2>&1 | tee i18n.log && \
7171- if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compile errors!\n\n"; fi && \
7272- SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN SENTRY_RELEASE=$EXPO_PUBLIC_RELEASE_VERSION SENTRY_DIST=$EXPO_PUBLIC_BUNDLE_IDENTIFIER yarn build-web
6363+ if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compile errors!\n\n"; fi
73647465# DEBUG
7566RUN find ./bskyweb/static && find ./web-build/static
···62626363### Tips
64646565-- Copy the `.env.example` to `.env` and fill in any necessary tokens. (The Sentry token is NOT required; see instructions below if you want to enable Sentry.)
6565+- Copy the `.env.example` to `.env` and fill in any necessary tokens.
6666- To run on the device, add `--device` to the command (e.g. `yarn android --device`). To build in production mode (slower build, faster app), also add `--variant release` on Android or `--configuration Release` on iOS.
6767- If you want to use Expo EAS on your own builds without ejecting from Expo, make sure to change the `owner` and `extra.eas.projectId` properties. If you do not have an Expo account, you may remove these properties.
6868- `npx react-native info` Checks what has been installed.
···7878 - `yarn e2e:metro`
7979- Run once: `yarn e2e:build`
8080- Each test run: `yarn e2e:run`
8181-8282-### Adding Sentry
8383-8484-Adding Sentry is NOT required. You can keep `SENTRY_AUTH_TOKEN=` in `.env` which will build the app without Sentry.
8585-8686-However, if you're a part of the Bluesky team and want to enable Sentry, fill in `SENTRY_AUTH_TOKEN` in your `.env`. It can be created on the Sentry dashboard using [these instructions](https://docs.expo.dev/guides/using-sentry/#sign-up-for-a-sentry-account-and-create-a-project).
8787-8888-If you change `SENTRY_AUTH_TOKEN`, you need to do `yarn prebuild` before running `yarn ios` or `yarn android` again.
89819082### Adding and Updating Locales
9183
···11-# @sentry/react-native/scripts/expo-upload-sourcemaps.js patch
22-33-Lets us specify the output directory for the sourcemaps via an environment variable.
44-55-# @sentry/react-native/dist/js/tools/sentryMetroSerializer.js patch
66-77-Patch of this: https://github.com/getsentry/sentry-react-native/issues/5180#issuecomment-3311772038
88-99-Will be fixed in an upcoming release of @sentry/react-native - remove when available.
+1-3
src/App.native.tsx
···11-import '#/logger/sentry/setup'
21import '#/view/icons'
3243import React, {useEffect, useState} from 'react'
···1312import * as SystemUI from 'expo-system-ui'
1413import {msg} from '@lingui/macro'
1514import {useLingui} from '@lingui/react'
1616-import * as Sentry from '@sentry/react-native'
17151816import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController'
1917import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder'
···235233 )
236234}
237235238238-export default Sentry.wrap(App)
236236+export default App
+1-3
src/App.web.tsx
···11-import '#/logger/sentry/setup' // must be near top
21import '#/view/icons'
32import './style.css'
43···76import {SafeAreaProvider} from 'react-native-safe-area-context'
87import {msg} from '@lingui/macro'
98import {useLingui} from '@lingui/react'
1010-import * as Sentry from '@sentry/react-native'
1191210import {QueryProvider} from '#/lib/react-query'
1311import {Provider as StatsigProvider} from '#/lib/statsig/statsig'
···203201 )
204202}
205203206206-export default Sentry.wrap(App)
204204+export default App
-5
src/env/common.ts
···7575 process.env.EXPO_PUBLIC_CHAT_PROXY_DID || 'did:web:api.bsky.chat'
76767777/**
7878- * Sentry DSN for telemetry
7979- */
8080-export const SENTRY_DSN: string | undefined = process.env.EXPO_PUBLIC_SENTRY_DSN
8181-8282-/**
8378 * GCP project ID which is required for native device attestation. On web, this
8479 * should be unset and evaluate to 0.
8580 */
···1212} from '#/logger/types'
1313import {enabledLogLevels} from '#/logger/util'
1414import {ENV} from '#/env'
1515-import {sentryTransport} from './transports/sentry'
16151716const TRANSPORTS: Transport[] = (function configureTransports() {
1817 switch (ENV) {
1918 case 'production': {
2020- return [sentryTransport].filter(Boolean) as Transport[]
1919+ return []
2120 }
2221 case 'test': {
2322 return []
-1
src/logger/sentry/lib/index.ts
···11-export * as Sentry from '@sentry/react-native'
-1
src/logger/sentry/lib/index.web.ts
···11-export * as Sentry from '@sentry/react-native'
-32
src/logger/sentry/setup/index.ts
···11-import {init} from '@sentry/react-native'
22-33-import * as env from '#/env'
44-55-init({
66- enabled: !env.IS_DEV && !!env.SENTRY_DSN,
77- autoSessionTracking: false,
88- dsn: env.SENTRY_DSN,
99- debug: false, // If `true`, Sentry will try to print out useful debugging information if something goes wrong with sending the event. Set it to `false` in production
1010- environment: env.ENV,
1111- dist: env.BUNDLE_IDENTIFIER,
1212- release: env.RELEASE_VERSION,
1313- ignoreErrors: [
1414- /*
1515- * Unknown internals errors
1616- */
1717- `t is not defined`,
1818- `Can't find variable: t`,
1919- /*
2020- * Un-useful errors
2121- */
2222- `Network request failed`,
2323- ],
2424- /**
2525- * Does not affect traces of error events or other logs, just disables
2626- * automatically attaching stack traces to events. This helps us group events
2727- * and prevents explosions of separate issues.
2828- *
2929- * @see https://docs.sentry.io/platforms/react-native/configuration/options/#attach-stacktrace
3030- */
3131- attachStacktrace: false,
3232-})
-104
src/logger/transports/sentry.ts
···11-import {isNetworkError} from '#/lib/strings/errors'
22-import {Sentry} from '#/logger/sentry/lib'
33-import {LogLevel, type Transport} from '#/logger/types'
44-import {prepareMetadata} from '#/logger/util'
55-66-export const sentryTransport: Transport = (
77- level,
88- context,
99- message,
1010- {type, tags, ...metadata},
1111- timestamp,
1212-) => {
1313- // Skip debug messages entirely for now - esb
1414- if (level === LogLevel.Debug) return
1515-1616- const meta = {
1717- __context__: context,
1818- ...prepareMetadata(metadata),
1919- }
2020- let _tags = tags || {}
2121- _tags = {
2222- // use `category` to match breadcrumbs
2323- category: context,
2424- ...tags,
2525- }
2626-2727- /**
2828- * If a string, report a breadcrumb
2929- */
3030- if (typeof message === 'string') {
3131- const severity = (
3232- {
3333- [LogLevel.Debug]: 'debug',
3434- [LogLevel.Info]: 'info',
3535- [LogLevel.Log]: 'log', // Sentry value here is undefined
3636- [LogLevel.Warn]: 'warning',
3737- [LogLevel.Error]: 'error',
3838- } as const
3939- )[level]
4040-4141- Sentry.addBreadcrumb({
4242- category: context,
4343- message,
4444- data: meta,
4545- type: type || 'default',
4646- level: severity,
4747- timestamp: timestamp / 1000, // Sentry expects seconds
4848- })
4949-5050- // We don't want to send any network errors to sentry
5151- if (isNetworkError(message)) {
5252- return
5353- }
5454-5555- /**
5656- * Send all higher levels with `captureMessage`, with appropriate severity
5757- * level
5858- */
5959- if (level === 'error' || level === 'warn' || level === 'log') {
6060- // Defer non-critical messages so they're sent in a batch
6161- queueMessageForSentry(message, {
6262- level: severity,
6363- tags: _tags,
6464- extra: meta,
6565- })
6666- }
6767- } else {
6868- /**
6969- * It's otherwise an Error and should be reported with captureException
7070- */
7171- Sentry.captureException(message, {
7272- tags: _tags,
7373- extra: meta,
7474- })
7575- }
7676-}
7777-7878-const queuedMessages: [string, Parameters<typeof Sentry.captureMessage>[1]][] =
7979- []
8080-let sentrySendTimeout: ReturnType<typeof setTimeout> | null = null
8181-8282-function queueMessageForSentry(
8383- message: string,
8484- captureContext: Parameters<typeof Sentry.captureMessage>[1],
8585-) {
8686- queuedMessages.push([message, captureContext])
8787- if (!sentrySendTimeout) {
8888- // Throttle sending messages with a leading delay
8989- // so that we can get Sentry out of the critical path.
9090- sentrySendTimeout = setTimeout(() => {
9191- sentrySendTimeout = null
9292- sendQueuedMessages()
9393- }, 7000)
9494- }
9595-}
9696-9797-function sendQueuedMessages() {
9898- while (queuedMessages.length > 0) {
9999- const record = queuedMessages.shift()
100100- if (record) {
101101- Sentry.captureMessage(record[0], record[1])
102102- }
103103- }
104104-}