invertase / react-native-firebase

πŸ”₯ A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.64k stars 2.2k forks source link

πŸ”₯[πŸ›] Flaky builds in GitHub Actions #7640

Closed roryabraham closed 6 months ago

roryabraham commented 6 months ago

Issue

We are seeing a number of flaky iOS builds in our CI failing with:

Error: declaration of 'RCTConvert' must be imported from module 'React.RCTConvert' before it is required
      @([RCTConvert BOOL:@([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled])]);

Project Files

Javascript

Click To Expand

#### `package.json`: ```json { "name": "new.expensify", "version": "1.4.43-7", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", "license": "MIT", "private": true, "scripts": { "configure-mapbox": "scripts/setup-mapbox-sdk-walkthrough.sh", "setupNewDotWebForEmulators": "scripts/setup-newdot-web-emulators.sh", "startAndroidEmulator": "scripts/start-android.sh", "postinstall": "scripts/postInstall.sh", "clean": "npx react-native clean-project-auto", "android": "scripts/set-pusher-suffix.sh && npx react-native run-android --mode=developmentDebug --appId=com.expensify.chat.dev", "ios": "scripts/set-pusher-suffix.sh && npx react-native run-ios --list-devices --mode=\"DebugDevelopment\" --scheme=\"New Expensify Dev\"", "pod-install": "cd ios && bundle exec pod install", "ipad": "concurrently \"npx react-native run-ios --simulator=\\\"iPad Pro (12.9-inch) (6th generation)\\\" --mode=\\\"DebugDevelopment\\\" --scheme=\\\"New Expensify Dev\\\"\"", "ipad-sm": "concurrently \"npx react-native run-ios --simulator=\\\"iPad Pro (11-inch) (4th generation)\\\" --mode=\\\"DebugDevelopment\\\" --scheme=\\\"New Expensify Dev\\\"\"", "start": "npx react-native start", "web": "scripts/set-pusher-suffix.sh && concurrently npm:web-proxy npm:web-server", "web-proxy": "ts-node web/proxy.js", "web-server": "webpack-dev-server --open --config config/webpack/webpack.dev.js", "build": "webpack --config config/webpack/webpack.common.js --env envFile=.env.production", "build-staging": "webpack --config config/webpack/webpack.common.js --env envFile=.env.staging", "build-adhoc": "webpack --config config/webpack/webpack.common.js --env envFile=.env.adhoc", "desktop": "scripts/set-pusher-suffix.sh && ts-node desktop/start.js", "desktop-build": "scripts/build-desktop.sh production", "desktop-build-staging": "scripts/build-desktop.sh staging", "createDocsRoutes": "ts-node .github/scripts/createDocsRoutes.js", "desktop-build-adhoc": "scripts/build-desktop.sh adhoc", "ios-build": "fastlane ios build", "android-build": "fastlane android build", "android-build-e2e": "bundle exec fastlane android build_e2e", "android-build-e2edelta": "bundle exec fastlane android build_e2edelta", "test": "TZ=utc jest", "typecheck": "tsc", "lint": "eslint . --max-warnings=0 --cache --cache-location=node_modules/.cache/eslint", "lint-changed": "eslint --fix $(git diff --diff-filter=AM --name-only main -- \"*.js\" \"*.ts\" \"*.tsx\")", "lint-watch": "npx eslint-watch --watch --changed", "shellcheck": "./scripts/shellCheck.sh", "prettier": "prettier --write .", "prettier-watch": "onchange \"**/*.{js,ts,tsx}\" -- prettier --write --ignore-unknown {{changed}}", "print-version": "echo $npm_package_version", "storybook": "start-storybook -p 6006", "storybook-build": "ENV=production build-storybook -o dist/docs", "storybook-build-staging": "ENV=staging build-storybook -o dist/docs", "gh-actions-build": "./.github/scripts/buildActions.sh", "gh-actions-validate": "./.github/scripts/validateActionsAndWorkflows.sh", "analyze-packages": "ANALYZE_BUNDLE=true webpack --config config/webpack/webpack.common.js --env envFile=.env.production", "symbolicate:android": "npx metro-symbolicate android/app/build/generated/sourcemaps/react/release/index.android.bundle.map", "symbolicate:ios": "npx metro-symbolicate main.jsbundle.map", "test:e2e": "ts-node tests/e2e/testRunner.js --development --skipCheckout --skipInstallDeps --buildMode none", "test:e2e:dev": "ts-node tests/e2e/testRunner.js --development --skipCheckout --config ./config.dev.js --buildMode skip --skipInstallDeps", "gh-actions-unused-styles": "./.github/scripts/findUnusedKeys.sh", "workflow-test": "./workflow_tests/scripts/runWorkflowTests.sh", "workflow-test:generate": "ts-node workflow_tests/utils/preGenerateTest.js", "setup-https": "mkcert -install && mkcert -cert-file config/webpack/certificate.pem -key-file config/webpack/key.pem dev.new.expensify.com localhost 127.0.0.1" }, "dependencies": { "@dotlottie/react-player": "^1.6.3", "@expensify/react-native-live-markdown": "0.1.5", "@expo/metro-runtime": "~3.1.1", "@formatjs/intl-datetimeformat": "^6.10.0", "@formatjs/intl-getcanonicallocales": "^2.2.0", "@formatjs/intl-listformat": "^7.2.2", "@formatjs/intl-locale": "^3.3.0", "@formatjs/intl-numberformat": "^8.5.0", "@formatjs/intl-pluralrules": "^5.2.2", "@gorhom/portal": "^1.0.14", "@invertase/react-native-apple-authentication": "^2.2.2", "@kie/act-js": "^2.6.0", "@kie/mock-github": "^1.0.0", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@onfido/react-native-sdk": "8.3.0", "@react-native-async-storage/async-storage": "1.21.0", "@react-native-camera-roll/camera-roll": "7.4.0", "@react-native-clipboard/clipboard": "^1.13.2", "@react-native-community/geolocation": "^3.0.6", "@react-native-community/netinfo": "11.2.1", "@react-native-firebase/analytics": "^12.3.0", "@react-native-firebase/app": "^12.3.0", "@react-native-firebase/crashlytics": "^12.3.0", "@react-native-firebase/perf": "^12.3.0", "@react-native-google-signin/google-signin": "^10.0.1", "@react-native-picker/picker": "2.5.1", "@react-navigation/material-top-tabs": "^6.6.3", "@react-navigation/native": "6.1.8", "@react-navigation/stack": "6.3.16", "@react-ng/bounds-observer": "^0.2.1", "@rnmapbox/maps": "^10.1.11", "@shopify/flash-list": "^1.6.3", "@ua/react-native-airship": "^15.3.1", "@vue/preload-webpack-plugin": "^2.0.0", "awesome-phonenumber": "^5.4.0", "babel-polyfill": "^6.26.0", "canvas-size": "^1.2.6", "core-js": "^3.32.0", "date-fns": "^2.30.0", "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#a8ed0f8e1be3a1e09016e07a74cfd13c85bbc167", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.10.1", "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", "jest-expo": "50.0.1", "jest-when": "^3.5.2", "lodash": "4.17.21", "lottie-react-native": "6.4.1", "mapbox-gl": "^2.15.0", "onfido-sdk-ui": "13.6.1", "patch-package": "^8.0.0", "process": "^0.11.10", "prop-types": "^15.7.2", "pusher-js": "8.3.0", "react": "18.2.0", "react-beautiful-dnd": "^13.1.1", "react-collapse": "^5.1.0", "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", "react-map-gl": "^7.1.3", "react-native": "0.73.2", "react-native-android-location-enabler": "^2.0.1", "react-native-blob-util": "0.19.4", "react-native-collapsible": "^1.6.1", "react-native-config": "^1.4.5", "react-native-dev-menu": "^4.1.1", "react-native-device-info": "^10.3.0", "react-native-document-picker": "^9.1.1", "react-native-draggable-flatlist": "^4.0.1", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.14.1", "react-native-google-places-autocomplete": "2.5.6", "react-native-haptic-feedback": "^2.2.0", "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#8393b7e58df6ff65fd41f60aee8ece8822c91e2b", "react-native-key-command": "^1.0.6", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", "react-native-onyx": "2.0.2", "react-native-pager-view": "6.2.2", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", "react-native-permissions": "^3.9.3", "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#da50d2c5c54e268499047f9cc98b8df4196c1ddf", "react-native-plaid-link-sdk": "10.8.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-sqlite": "^8.0.0-beta.2", "react-native-reanimated": "^3.6.1", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.8.2", "react-native-screens": "3.29.0", "react-native-sound": "^0.11.2", "react-native-svg": "14.1.0", "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", "react-native-vision-camera": "2.16.8", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-web-sound": "^0.1.3", "react-native-webview": "13.6.3", "react-pdf": "7.3.3", "react-plaid-link": "3.3.2", "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", "react-window": "^1.8.9", "save": "^2.4.0", "semver": "^7.5.2", "shim-keyboard-event-key": "^1.0.3", "underscore": "^1.13.1" }, "devDependencies": { "@actions/core": "1.10.0", "@actions/github": "5.1.1", "@babel/core": "^7.20.0", "@babel/parser": "^7.22.16", "@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/preset-env": "^7.20.0", "@babel/preset-flow": "^7.12.13", "@babel/preset-react": "^7.10.4", "@babel/preset-typescript": "^7.21.5", "@babel/runtime": "^7.20.0", "@babel/traverse": "^7.22.20", "@babel/types": "^7.22.19", "@dword-design/eslint-plugin-import-alias": "^4.0.8", "@electron/notarize": "^2.1.0", "@jest/globals": "^29.5.0", "@ngneat/falso": "^7.1.1", "@octokit/core": "4.0.4", "@octokit/plugin-paginate-rest": "3.1.0", "@octokit/plugin-throttling": "4.1.0", "@react-native-community/eslint-config": "3.0.0", "@react-native/babel-preset": "^0.73.19", "@react-native/metro-config": "^0.73.3", "@react-navigation/devtools": "^6.0.10", "@storybook/addon-a11y": "^6.5.9", "@storybook/addon-essentials": "^7.0.0", "@storybook/addon-react-native-web": "0.0.19--canary.37.cb55428.0", "@storybook/addons": "^6.5.9", "@storybook/builder-webpack5": "^6.5.10", "@storybook/manager-webpack5": "^6.5.10", "@storybook/react": "^6.5.9", "@storybook/theming": "^6.5.9", "@svgr/webpack": "^6.0.0", "@testing-library/jest-native": "5.4.1", "@testing-library/react-native": "11.5.1", "@trivago/prettier-plugin-sort-imports": "^4.2.0", "@types/canvas-size": "^1.2.2", "@types/concurrently": "^7.0.0", "@types/jest": "^29.5.2", "@types/jest-when": "^3.5.2", "@types/js-yaml": "^4.0.5", "@types/lodash": "^4.14.195", "@types/mapbox-gl": "^2.7.13", "@types/node": "^20.11.5", "@types/pusher-js": "^5.1.0", "@types/react": "18.2.45", "@types/react-beautiful-dnd": "^13.1.4", "@types/react-collapse": "^5.0.1", "@types/react-dom": "^18.2.4", "@types/react-test-renderer": "^18.0.0", "@types/semver": "^7.5.4", "@types/setimmediate": "^1.0.2", "@types/underscore": "^1.11.5", "@typescript-eslint/eslint-plugin": "^6.13.2", "@typescript-eslint/parser": "^6.13.2", "@vercel/ncc": "0.38.1", "@welldone-software/why-did-you-render": "7.0.1", "ajv-cli": "^5.0.0", "babel-eslint": "^10.1.0", "babel-jest": "29.4.1", "babel-loader": "^9.1.3", "babel-plugin-module-resolver": "^5.0.0", "babel-plugin-react-native-web": "^0.18.7", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-remove-console": "^6.9.4", "clean-webpack-plugin": "^3.0.0", "concurrently": "^5.3.0", "copy-webpack-plugin": "^6.4.1", "css-loader": "^6.7.2", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", "electron": "^26.6.8", "electron-builder": "24.6.4", "eslint": "^7.6.0", "eslint-config-airbnb-typescript": "^17.1.0", "eslint-config-expensify": "^2.0.43", "eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jest": "^24.1.0", "eslint-plugin-jsdoc": "^46.2.6", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-native-a11y": "^3.3.0", "eslint-plugin-storybook": "^0.5.13", "eslint-plugin-you-dont-need-lodash-underscore": "^6.12.0", "html-webpack-plugin": "^5.5.0", "jest": "29.4.1", "jest-circus": "29.4.1", "jest-cli": "29.4.1", "jest-environment-jsdom": "^29.4.1", "jest-transformer-svg": "^2.0.1", "memfs": "^4.6.0", "onchange": "^7.1.0", "portfinder": "^1.0.28", "prettier": "^2.8.8", "pusher-js-mock": "^0.3.3", "react-native-clean-project": "^4.0.0-alpha4.0", "react-native-performance-flipper-reporter": "^2.0.0", "react-test-renderer": "18.2.0", "reassure": "^0.10.1", "setimmediate": "^1.0.5", "shellcheck": "^1.1.0", "style-loader": "^2.0.0", "time-analytics-webpack-plugin": "^0.1.17", "ts-node": "^10.9.2", "type-fest": "^3.12.0", "typescript": "^5.3.2", "wait-port": "^0.2.9", "webpack": "^5.76.0", "webpack-bundle-analyzer": "^4.5.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.9.3", "webpack-merge": "^5.8.0", "yaml": "^2.2.1" }, "overrides": { "react-native": "$react-native", "expo": "$expo", "react-native-svg": "$react-native-svg" }, "expo": { "autolinking": { "exclude": [ "expo-constants", "expo-file-system", "expo-font", "@react-native-google-signin/google-signin", "expo-keep-awake" ] } }, "electronmon": { "patterns": [ "!node_modules", "!node_modules/**/*", "!**/*.map", "!ios/**", "!android/**", "*.test.*", "*.spec.*" ] }, "engines": { "node": "20.10.0", "npm": "10.2.3" } } ``` #### `firebase.json` for react-native-firebase v6: ```json { "react-native": { "crashlytics_disable_auto_disabler": true, "crashlytics_debug_enabled": false, "crashlytics_ndk_enabled": true } } ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") # Set the type of Mapbox SDK to use # This value is used by $RNMapboxMaps $RNMapboxMapsImpl = 'mapbox' def node_require(script) # Resolve script with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', "require.resolve( '#{script}', {paths: [process.argv[1]]}, )", __dir__]).strip end node_require('react-native/scripts/react_native_pods.rb') node_require('react-native-permissions/scripts/setup.rb') # Our min supported iOS version is higher than the default (min_ios_version_supported) to support libraires such as Airship platform :ios, 13.4 prepare_react_native_project! setup_permissions([ 'Camera', 'LocationAccuracy', 'LocationAlways', 'LocationWhenInUse' ]) # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded # # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` # ```js # module.exports = { # dependencies: { # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), # ``` flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled(['DebugProduction', 'DebugDevelopment', 'DebugAdHoc']) linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green use_frameworks! :linkage => linkage.to_sym end # Force pods to match minimum iOS version for React Native # Fixes build issue on Xcode Cloud where some pods # Use iOS 12 calls despite being set as iOS 11 def __apply_Xcode_14_3_RC_post_install_workaround(installer) installer.pods_project.targets.each do |target| target.build_configurations.each do |config| current_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] minimum_target = min_ios_version_supported if current_target.to_f < minimum_target.to_f config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = minimum_target end end end end # Configure Mapbox before installing dependencies pre_install do |installer| $RNMapboxMaps.pre_install(installer) end target 'NewExpensify' do use_expo_modules! post_integrate do |installer| begin expo_patch_react_imports!(installer) rescue => e Pod::UI.warn e end end permissions_path = '../node_modules/react-native-permissions/ios' project 'NewExpensify', 'DebugDevelopment' => :debug, 'DebugAdHoc' => :debug, 'DebugProduction' => :debug, 'ReleaseDevelopment' => :release, 'ReleaseAdHoc' => :release, 'ReleaseProduction' => :release config = use_native_modules! use_react_native!( :path => config[:reactNativePath], # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable the next line. :flipper_configuration => flipper_config, # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) target 'NewExpensifyTests' do inherit! :complete # Pods for testing end post_install do |installer| # Configure Mapbox after installation $RNMapboxMaps.post_install(installer) # https://github.com/facebook/react-native/blob/main/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false ) __apply_Xcode_14_3_RC_post_install_workaround(installer) installer.pods_project.targets.each do |target| if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle" target.build_configurations.each do |config| config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' end end end end end target 'NotificationServiceExtension' do pod 'AirshipServiceExtension' end ``` #### `AppDelegate.m`: ```objc #import "AppDelegate.h" #import #import #import #import #import #import "RCTBootSplash.h" #import "RCTStartupTimer.h" #import @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.moduleName = @"NewExpensify"; // You can add your custom initial props in the dictionary below. // They will be passed down to the ViewController used by React Native. self.initialProps = @{}; // Configure firebase [FIRApp configure]; // Force the app to LTR mode. [[RCTI18nUtil sharedInstance] allowRTL:NO]; [[RCTI18nUtil sharedInstance] forceRTL:NO]; [super application:application didFinishLaunchingWithOptions:launchOptions]; [RCTBootSplash initWithStoryboard:@"BootSplash" rootView:(RCTRootView *)self.window.rootViewController.view]; // <- initialization using the storyboard file name // Define UNUserNotificationCenter UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; // Start the "js_load" custom performance tracing metric. This timer is // stopped by a native module in the JS so we can measure total time starting // in the native layer and ending in the JS layer. [RCTStartupTimer start]; return YES; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options: (NSDictionary *)options { return [RCTLinkingManager application:application openURL:url options:options]; } - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler: (nonnull void (^)(NSArray> *_Nullable)) restorationHandler { return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { return [self getBundleURL]; } - (NSURL *)getBundleURL { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } // This methods is needed to support the hardware keyboard shortcuts - (NSArray *)keyCommands { return [HardwareShortcuts sharedInstance].keyCommands; } - (void)handleKeyCommand:(UIKeyCommand *)keyCommand { [[HardwareShortcuts sharedInstance] handleKeyCommand:keyCommand]; } @end ```


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ ] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { buildToolsVersion = "34.0.0" minSdkVersion = 21 compileSdkVersion = 34 targetSdkVersion = 34 ndkVersion = "25.1.8937393" androidXCore = "1.0.2" multiDexEnabled = true googlePlayServicesVersion = "17.0.0" kotlinVersion = '1.8.10' // This property configures the type of Mapbox SDK used by the @rnmapbox/maps library. // "mapbox" indicates the usage of the Mapbox SDK. RNMapboxMapsImpl = "mapbox" } repositories { google() mavenCentral() } dependencies { classpath("com.android.tools.build:gradle") classpath("com.facebook.react:react-native-gradle-plugin") classpath("com.google.gms:google-services:4.3.4") classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") classpath("com.google.firebase:perf-plugin:1.4.1") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") } } allprojects { configurations.all { resolutionStrategy { force 'org.xerial:sqlite-jdbc:3.34.0' // Manually set the react-native version to resolve this upstream issue: https://github.com/facebook/react-native/issues/35210 def REACT_NATIVE_VERSION = new File(['node', '--print',"JSON.parse(require('fs').readFileSync(require.resolve('react-native/package.json'), 'utf-8')).version"].execute(null, rootDir).text.trim()) force "com.facebook.react:react-native:" + REACT_NATIVE_VERSION force "com.facebook.react:hermes-engine:" + REACT_NATIVE_VERSION } } repositories { maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url("$rootDir/../node_modules/react-native/android") } maven { // Mapbox SDK requires authentication to download from Mapbox's private Maven repository. url 'https://api.mapbox.com/downloads/v2/releases/maven' authentication { basic(BasicAuthentication) } credentials { // 'mapbox' is the fixed username for Mapbox's Maven repository. username = 'mapbox' // The value for password is read from the 'MAPBOX_DOWNLOADS_TOKEN' gradle property. // Run "npm run setup-mapbox-sdk" to set this property in Β«USER_HOMEΒ»/.gradle/gradle.properties // Example gradle.properties entry: // MAPBOX_DOWNLOADS_TOKEN=YOUR_SECRET_TOKEN_HERE password = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: "" } } } } apply plugin: "com.facebook.react.rootproject" ``` #### `android/app/build.gradle`: ```groovy apply plugin: "com.android.application" apply plugin: "org.jetbrains.kotlin.android" apply plugin: "com.facebook.react" apply plugin: "com.google.firebase.firebase-perf" apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" /** * This is the configuration block to customize your React Native Android app. * By default you don't need to apply any configuration, just uncomment the lines you need. */ react { /* Folders */ // The root of your project, i.e. where "package.json" lives. Default is '..' // root = file("../") // The folder where the react-native NPM package is. Default is ../node_modules/react-native // reactNativeDir = file("../node_modules/react-native") // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen // codegenDir = file("../node_modules/@react-native/codegen") // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js // cliFile = file("../node_modules/react-native/cli.js") /* Variants */ // The list of variants to that are debuggable. For those we're going to // skip the bundling of the JS bundle and the assets. By default is just 'debug'. // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. debuggableVariants = ["developmentDebug", "productionDebug"] /* Bundling */ // A list containing the node command and its flags. Default is just 'node'. // nodeExecutableAndArgs = ["node"] // // The command to run when bundling. By default is 'bundle' // bundleCommand = "ram-bundle" // // The path to the CLI configuration file. Default is empty. // bundleConfig = file(../rn-cli.config.js) // // The name of the generated asset file containing your JS bundle // bundleAssetName = "MyApplication.android.bundle" // // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' // entryFile = file("../js/MyApplication.android.js") // // A list of extra flags to pass to the 'bundle' commands. // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle // extraPackagerArgs = [] /* Hermes Commands */ // The hermes compiler command to run. By default it is 'hermesc' // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" // // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" // hermesFlags = ["-O", "-output-source-map"] // // Added by install-expo-modules entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", rootDir.getAbsoluteFile().getParentFile().getAbsolutePath(), "android", "absolute"].execute(null, rootDir).text.trim()) cliFile = new File(["node", "--print", "require.resolve('@expo/cli')"].execute(null, rootDir).text.trim()) bundleCommand = "export:embed" } project.ext.envConfigFiles = [ productionDebug: ".env.production", productionRelease: ".env.production", adhocRelease: ".env.adhoc", developmentRelease: ".env", developmentDebug: ".env", e2eRelease: "tests/e2e/.env.e2e", e2edeltaRelease: "tests/e2e/.env.e2edelta" ] /** * Set this to true to Run Proguard on Release builds to minify the Java bytecode. */ def enableProguardInReleaseBuilds = true /** * The preferred build flavor of JavaScriptCore (JSC) * * For example, to use the international variant, you can use: * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ def jscFlavor = 'org.webkit:android-jsc:+' android { ndkVersion rootProject.ext.ndkVersion buildToolsVersion rootProject.ext.buildToolsVersion compileSdkVersion rootProject.ext.compileSdkVersion namespace "com.expensify.chat" defaultConfig { applicationId "com.expensify.chat" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled versionCode 1001044307 versionName "1.4.43-7" } flavorDimensions "default" productFlavors { // we need to define a production flavor but since it has default config, we can leave it empty production e2e { // If are building a version that won't be uploaded to the play store, we don't have to use production keys // applies all non-production flavors applicationIdSuffix ".e2e" signingConfig signingConfigs.debug resValue "string", "build_config_package", "com.expensify.chat" } e2edelta { // If are building a version that won't be uploaded to the play store, we don't have to use production keys // applies all non-production flavors applicationIdSuffix ".e2edelta" signingConfig signingConfigs.debug resValue "string", "build_config_package", "com.expensify.chat" } adhoc { applicationIdSuffix ".adhoc" signingConfig signingConfigs.debug resValue "string", "build_config_package", "com.expensify.chat" } development { applicationIdSuffix ".dev" signingConfig signingConfigs.debug resValue "string", "build_config_package", "com.expensify.chat" } } signingConfigs { release { storeFile file(MYAPP_UPLOAD_STORE_FILE) storePassword System.getenv('MYAPP_UPLOAD_STORE_PASSWORD') keyAlias MYAPP_UPLOAD_KEY_ALIAS keyPassword System.getenv('MYAPP_UPLOAD_KEY_PASSWORD') } debug { storeFile file('debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } } buildTypes { debug { signingConfig signingConfigs.debug } release { productFlavors.production.signingConfig signingConfigs.release shrinkResources enableProguardInReleaseBuilds minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" signingConfig null // buildTypes take precedence over productFlavors when it comes to the signing configuration, // thus we need to manually set the signing config, so that the e2e uses the debug config again. // In other words, the signingConfig setting above will be ignored when we build the flavor in release mode. productFlavors.all { flavor -> // All release builds should be signed with the release config ... flavor.signingConfig signingConfigs.release } // ... except for the e2e flavor, which we maybe want to build locally: productFlavors.e2e.signingConfig signingConfigs.debug productFlavors.e2edelta.signingConfig signingConfigs.debug } } // since we don't need variants adhocDebug and e2eDebug, we can force gradle to ignore them variantFilter { variant -> if (variant.name == "adhocDebug" || variant.name == "e2eDebug" || variant.name == "e2edeltaDebug") { setIgnore(true) } } } dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") implementation("com.facebook.react:flipper-integration") if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") } else { implementation jscFlavor } // Firebase libraries (using the Firebase BoM for consistency - see https://firebase.google.com/docs/android/learn-more#bom) implementation platform("com.google.firebase:firebase-bom:29.0.3") implementation "com.google.firebase:firebase-perf" implementation "com.google.firebase:firebase-crashlytics" // GIF support implementation 'com.facebook.fresco:fresco:2.5.0' implementation 'com.facebook.fresco:animated-gif:2.5.0' // Android support library implementation 'com.android.support:support-core-utils:28.0.0' // Multi Dex Support: https://developer.android.com/studio/build/multidex#mdex-gradle implementation 'com.android.support:multidex:1.0.3' // Plaid SDK implementation project(':react-native-plaid-link-sdk') // Fixes a version conflict between airship and react-native-plaid-link-sdk // This may be fixed by a newer version of the plaid SDK (not working as of 10.0.0) implementation "androidx.work:work-runtime-ktx:2.8.0" // This okhttp3 dependency prevents the app from crashing - See https://github.com/plaid/react-native-plaid-link-sdk/issues/74#issuecomment-648435002 implementation "com.squareup.okhttp3:okhttp-urlconnection:4.+" implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0") } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) apply plugin: 'com.google.gms.google-services' // Google Play services Gradle plugin apply plugin: 'com.google.firebase.crashlytics' ``` #### `android/settings.gradle`: ```groovy rootProject.name = 'NewExpensify' include ':react-native-webview' project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android') include ':react-native-webview' project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android') include ':react-native-webview' project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android') include ':react-native-image-size' project(':react-native-image-size').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-size/android') include ':react-native-config' project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android') include ':react-native-plaid-link-sdk' project(':react-native-plaid-link-sdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-plaid-link-sdk/android') include ':react-native-dev-menu' project(':react-native-dev-menu').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-dev-menu/android') apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app' includeBuild('../node_modules/@react-native/gradle-plugin') apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle") useExpoModules() ``` #### `MainApplication.java`: ```kt package com.expensify.chat import android.content.res.Configuration import android.database.CursorWindow import androidx.multidex.MultiDexApplication import com.expensify.chat.bootsplash.BootSplashPackage import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.modules.i18nmanager.I18nUtil import com.facebook.soloader.SoLoader import com.google.firebase.crashlytics.FirebaseCrashlytics import expo.modules.ApplicationLifecycleDispatcher import expo.modules.ReactNativeHostWrapper class MainApplication : MultiDexApplication(), ReactApplication { override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(this, object : DefaultReactNativeHost(this) { override fun getUseDeveloperSupport() = BuildConfig.DEBUG override fun getPackages(): List = PackageList(this).packages.apply { // Packages that cannot be autolinked yet can be added manually here, for example: // add(MyReactNativePackage()); add(BootSplashPackage()) add(ExpensifyAppPackage()) add(RNTextInputResetPackage()) } override fun getJSMainModuleName() = ".expo/.virtual-metro-entry" override val isNewArchEnabled: Boolean get() = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED override val isHermesEnabled: Boolean get() = BuildConfig.IS_HERMES_ENABLED }) override fun onCreate() { super.onCreate() SoLoader.init(this, /* native exopackage */false) if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. load() } if (BuildConfig.DEBUG) { FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false) } // Force the app to LTR mode. val sharedI18nUtilInstance = I18nUtil.getInstance() sharedI18nUtilInstance.allowRTL(applicationContext, false) // Start the "js_load" custom performance tracing metric. This timer is stopped by a native // module in the JS so we can measure total time starting in the native layer and ending in // the JS layer. StartupTimer.start() // Increase SQLite DB write size try { val field = CursorWindow::class.java.getDeclaredField("sCursorWindowSize") field.isAccessible = true field[null] = 100 * 1024 * 1024 //the 100MB is the new size } catch (e: Exception) { e.printStackTrace() } ApplicationLifecycleDispatcher.onApplicationCreate(this); } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) } } ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` info Fetching system and libraries information... System: OS: macOS 14.3.1 CPU: (12) arm64 Apple M2 Max Memory: 4.37 GB / 96.00 GB Shell: version: "5.9" path: /bin/zsh Binaries: Node: version: 20.10.0 path: ~/.nvm/versions/node/v20.10.0/bin/node Yarn: Not Found npm: version: 10.2.3 path: ~/.nvm/versions/node/v20.10.0/bin/npm Watchman: Not Found Managers: CocoaPods: version: 1.12.1 path: /Users/roryabraham/.rbenv/shims/pod SDKs: iOS SDK: Platforms: - DriverKit 23.0 - iOS 17.0 - macOS 14.0 - tvOS 17.0 - visionOS 1.0 - watchOS 10.0 Android SDK: Not Found IDEs: Android Studio: Not Found Xcode: version: 15.1/15C5028h path: /usr/bin/xcodebuild Languages: Java: version: 17.0.9 path: /Users/roryabraham/.jenv/shims/javac Ruby: version: 2.7.5 path: /Users/roryabraham/.rbenv/shims/ruby npmPackages: "@react-native-community/cli": Not Found react: installed: 18.2.0 wanted: 18.2.0 react-native: installed: 0.73.2 wanted: 0.73.2 react-native-macos: Not Found npmGlobalPackages: "*react-native*": Not Found Android: hermesEnabled: true newArchEnabled: false iOS: hermesEnabled: true newArchEnabled: false info React Native v0.73.4 is now available (your project is running on v0.73.2). info Changelog: https://github.com/facebook/react-native/releases/tag/v0.73.4 info Diff: https://react-native-community.github.io/upgrade-helper/?from=0.73.4 info For more info, check out "https://reactnative.dev/docs/upgrading?os=macos". ``` - **Platform that you're experiencing the issue on**: - [x] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [ ] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - 12.3.0 - **`Firebase` module(s) you're using that has the issue:** - Crashlytics - **Are you using `TypeScript`?** - Y, 5.3.2


mikehardy commented 6 months ago

I must say, I'm shocked this even builds:


    "@react-native-firebase/analytics": "^12.3.0",
    "@react-native-firebase/app": "^12.3.0",
    "@react-native-firebase/crashlytics": "^12.3.0",
    "@react-native-firebase/perf": "^12.3.0",

Please let me know if you still reproduce when you are using current react-native-firebase

roryabraham commented 6 months ago

Hey @mikehardy, thanks for responding. I believe I figured out the root cause of our problems, and it wasn't a πŸ› inreact-native-firebase. Apologies for the noise, but it was a tough problem to Google.

For posterity, in case anyone else finds this issue researching a similar problem, the PR that fixed these builds for us was https://github.com/Expensify/App/pull/37059.

I'm still not 100% confident in my explanation, but my understanding of the problem is:

Hopefully that helps someone someday, sorry for the false bug report.

mikehardy commented 6 months ago

Hey @roryabraham glad you found a root cause - I can pretty confidently state that if any packages change or patches change you will need a pod install run to be sure, as the podspec files that run during pod install may change as a result of package updates or patches and without a pod install run you won't get the changes

Additionally, you might be interested to know that the firebase.json file we use for configuration here is processed by a script here that is only run during pod install, where the contents of the file are merged into the processed Info.plist for the app in DerivedData - so you won't see firebase.json changes if you don't run pod install either. I don't know how Expo configuration works but they may have something similar with their app properties files. I'd consider adding config files to the list of things that would trigger a pod install

roryabraham commented 6 months ago

Thanks for the tip @mikehardy, I'll add a hash of firebase.json to our cache key πŸ”‘