Build & release
Toolchain
| Tool | Version / note |
|---|---|
| Node | 20.x |
| React Native | 0.78.2 (Hermes) |
| Package manager | npm (package-lock.json) — a stray yarn.lock may exist; standardise on npm |
| iOS | Xcode + CocoaPods via Bundler (bundle exec pod install) |
| Android | Android SDK + NDK 27.1.12297006 (r27b) |
[!WARNING] Two recurring local-build traps:
- Partial NDK install →
[CXX1101] NDK … did not have a source.properties file. Reinstall the exact version:sdkmanager "ndk;27.1.12297006".- Stale Pods after a
node_moduleschange → missing React headers. Fix:rm -rf node_modules && npm install, thencd ios && pod install --repo-update.
Local development
npm install # also runs patch-package (postinstall)
cd ios && bundle exec pod install && cd .. # iOS only
npm start # Metro (port 8081)
npm run ios # build + run on a simulator
npm run android # build + run on an emulator/deviceFastlane
Release automation lives in fastlane/Fastfile. The main lanes:
| Lane | What it does |
|---|---|
ios build / android build_apk/build | Build only, no upload |
ios beta / android beta | Build + upload to TestFlight / Play internal |
ios release / android release | Bump patch version, build, submit to App Store review / Play production, tag + push |
bump_build / bump_patch | Version bumps (also exposed as npm run version:*) |
[!NOTE] The
releaselanes runbump_patchfirst (commit + tag + push), so don’t run them without all credentials present — a failure mid-lane still leaves a version bump committed. Production deploys are intended to run from CI.
Code signing
- iOS — App Store Connect API key (
.p8), via env:APP_STORE_CONNECT_API_KEY_KEY_ID,_ISSUER_ID,_KEY_FILEPATH. No interactive Apple 2FA needed. - Android — release keystore
android/app/release-keystore.iba.jks, unlocked byANDROID_APP_PRIVATE_KEY,ANDROID_APP_ALIAS, and the store/key passwords.
These secrets are not committed — they live in the build environment
(CI secrets, or fastlane/.env locally). See Configuration.
Versioning
package.json version is the source of truth, synced to the native projects
by scripts/version.js (npm run version:status / version:build /
version:patch). iOS CURRENT_PROJECT_VERSION and Android versionCode are the
build numbers.
Source maps
So production Sentry stack traces show real file/function names (not minified
?anon_0_ frames), source maps are uploaded on release builds:
- Metro (
metro.config.js) is wrapped withwithSentryConfig, which injects debug IDs into the bundle + source map so they match the exact build. - iOS uploads during the Xcode “Bundle React Native” phase
(
sentry-xcode.sh); Android via the@sentry/react-nativesentry.gradleintegration. - Both authenticate with a Sentry organization token (
org:ciscope) read fromSENTRY_AUTH_TOKENin the build environment.ios/sentry.propertiesandandroid/sentry.propertieshold the org/project only — never the token.
Without SENTRY_AUTH_TOKEN the build still succeeds but silently skips the
upload. Verify after a release that the new version shows uploaded artifacts in
Sentry and that fresh issues are symbolicated.