Skip to content

chore: integrate RNRepo prebuilt artifacts to reduce native build times#7382

Open
diegolmello wants to merge 2 commits into
developfrom
chore/rnrepo-prebuilds
Open

chore: integrate RNRepo prebuilt artifacts to reduce native build times#7382
diegolmello wants to merge 2 commits into
developfrom
chore/rnrepo-prebuilds

Conversation

@diegolmello
Copy link
Copy Markdown
Member

@diegolmello diegolmello commented Jun 3, 2026

Proposed changes

Integrate RNRepo (Software Mansion) to reduce native build times. RNRepo substitutes supported React Native libraries with prebuilt artifacts from packages.rnrepo.org during the native build — a Gradle plugin on Android and a CocoaPods plugin on iOS — and falls back to building from source whenever a prebuild isn't available. It's beta and New Architecture only; we're on RN 0.81.5 with New Arch enabled on both platforms, so it's supported.

We use the Standard React Native integration path (we have committed android/ + ios/, not Expo CNG):

  • Add the @rnrepo/build-tools dependency.
  • Android: RNRepo classpath in android/build.gradle; apply plugin: "org.rnrepo.tools.prebuilds-plugin" in android/app/build.gradle.
  • iOS: require for the CocoaPods plugin + rnrepo_post_install(installer) in ios/Podfile. Podfile.lock only changes its PODFILE CHECKSUM; the app Xcode project is untouched (RNRepo's build phases land in the generated Pods project).
  • Add rnrepo.config.json deny list.

Deny list rationale — prebuilt artifacts bypass library source compilation, so any library carrying a native (Objective-C/Java/Kotlin/podspec) patch-package patch must keep building from source, or the patch is silently lost. JS-only patches are unaffected. Entries key on the npm package name and are scoped to the platform whose native source the patch touches:

  • android: @lodev09/react-native-true-sheet, expo-asset, expo-file-system, expo-font, react-native-callkeep, react-native-webview
  • ios: react-native-callkeep, react-native-config-reader, react-native-gesture-handler, react-native-nitro-modules, react-native-webview

Two iOS entries are not patch-driven:

  • react-native-webview — its patch is Android-only, but the Podfile has a webview-specific HEADER_SEARCH_PATHS step and the pod graph uses static frameworks, so it stays source until proven safe.
  • react-native-nitro-modules — a prebuilt NitroModules is an incompatible Swift/C++ module pairing for its source-built consumer NitroMmkv (react-native-mmkv, which has no prebuild yet). Forcing the provider from source keeps it consistent with its consumers. Android prebuilds NitroModules without issue, so it stays enabled there.

Issue(s)

https://rocketchat.atlassian.net/browse/NATIVE-1248

How to test or reproduce

No app/runtime behavior changes — this only affects how native dependencies are built.

  • iOS: pnpm pod-install. Expect [📦 RNRepo] … using pre-built frameworks and [📦 RNRepo] ⊘ Denied from pre-builds listing the iOS deny entries.
  • Android: cd android && ./gradlew :app:assembleDebug --info. Expect [📦 RNRepo] RNRepo plugin is ENABLED and Package <name> is in deny list, skipping for each denied lib.
  • Opt out of RNRepo for a build (debugging): prefix with DISABLE_RNREPO=1.

CI build results

  • Android — ✅ built successfully with RNRepo (15+ libraries substituted with prebuilt AARs), in ~32.5 min vs a ~38 min (mean of 8 recent non-RNRepo runs) baseline — faster than every baseline run sampled (~15–18% / ~6 min).
  • iOS — the first run failed because a prebuilt NitroModules couldn't be linked against the source-built NitroMmkv. The deny-list entry above addresses it; an iOS build re-run is needed to confirm the fix and measure the iOS delta (iOS currently substitutes only ~6 libraries, so its win is expected to be smaller than Android's until the registry grows).

Screenshots

N/A — no UI changes.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

No JavaScript/TypeScript changed — only build configuration (*.gradle, Podfile, package.json, pnpm-lock.yaml) and the new rnrepo.config.json — so the JS unit tests and ESLint/tsc surface are unaffected.

When regenerating ios/Podfile.lock, use bundler (pnpm pod-install) so it stays on the pinned CocoaPods 1.15.2. Depending on the environment, RCT-Folly's spec checksum may harmlessly re-flip — that's pre-existing RN/Folly nondeterminism, not introduced here, and was kept out of this diff.

Prebuilt coverage is modest today (more on Android than iOS) because RNRepo is in beta and its registry is still growing; unsupported libraries fall back to source silently, so coverage — and the speedup — improves over time with no further changes on our side.

Summary by CodeRabbit

  • Chores
    • Updated Android build to integrate support for prebuilt native components via a build plugin.
    • Enhanced iOS Podfile to enable seamless installation/linking of prebuilt frameworks during install.
    • Added a new build-tools dependency to support the improved prebuild workflow.
    • Introduced a platform-specific deny list to restrict certain native packages on Android and iOS.

Wire Software Mansion's RNRepo (Gradle plugin + CocoaPods plugin) to
substitute supported React Native libraries with prebuilt artifacts during
the native build, falling back to source when a prebuild is unavailable.

Add rnrepo.config.json deny list so libraries carrying native (ObjC/Java/
Kotlin/podspec) patch-package patches keep building from source — prebuilt
artifacts would otherwise silently bypass those patches. JS-only patches are
unaffected.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f7d82ece-9c14-466d-84f5-751a9f76fb62

📥 Commits

Reviewing files that changed from the base of the PR and between bbc23c1 and aa4c904.

📒 Files selected for processing (1)
  • rnrepo.config.json
✅ Files skipped from review due to trivial changes (1)
  • rnrepo.config.json
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ESLint and Test / run-eslint-and-test

Walkthrough

This PR integrates RNRepo into Android and iOS build systems: adds the @rnrepo/build-tools dependency and rnrepo.config.json deny lists, registers a prebuilt Gradle plugin in the root buildscript, applies the plugin in the app module, and loads/invokes the RNRepo CocoaPods plugin in the Podfile.

Changes

RNRepo Build Tool Integration

Layer / File(s) Summary
Dependency and configuration
package.json, rnrepo.config.json
@rnrepo/build-tools version ^0.1.4 is added as a runtime dependency. rnrepo.config.json defines deny lists that restrict specific packages on Android (@lodev09/react-native-true-sheet, expo-asset, expo-file-system, expo-font, react-native-callkeep, react-native-webview) and iOS (react-native-callkeep, react-native-config-reader, react-native-gesture-handler, react-native-nitro-modules, react-native-webview).
Android build integration
android/build.gradle, android/app/build.gradle
The root Gradle script uses Node to resolve @rnrepo/build-tools and registers its prebuilt plugin JAR in the buildscript classpath. The app module applies the org.rnrepo.tools.prebuilds-plugin plugin.
iOS build integration
ios/Podfile
The Podfile loads the RNRepo CocoaPods plugin at the top via Node resolution, and the post_install hook calls rnrepo_post_install(installer) to perform xcframework linking for substituted libraries.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested labels

type: chore

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: integrating RNRepo prebuilt artifacts to reduce native build times, which aligns with all the file modifications (Gradle plugin setup, CocoaPods plugin integration, deny list configuration).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • NATIVE-1248: Request failed with status code 401

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@diegolmello diegolmello deployed to ios_build June 3, 2026 18:19 — with GitHub Actions Active
@diegolmello diegolmello deployed to android_build June 3, 2026 18:19 — with GitHub Actions Active
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
package.json (1)

50-50: ⚡ Quick win

Consider pinning @rnrepo/build-tools to 0.1.4 for reproducible native installs.

android/build.gradle and ios/Podfile already use require.resolve to locate the documented artifacts (gradle-plugin/build/libs/prebuilds-plugin.jar and cocoapods-plugin/lib/plugin.rb), so staying on later 0.1.x releases shouldn’t break builds as long as those files remain in the same locations. Pinning to an exact version is still safer if you want to prevent any future ^0.1.4 updates from introducing changes to those internal paths.

Suggested diff
-		"`@rnrepo/build-tools`": "^0.1.4",
+		"`@rnrepo/build-tools`": "0.1.4",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 50, Change the `@rnrepo/build-tools` dependency spec in
package.json from the caret range "^0.1.4" to the exact version "0.1.4"; then
run your package manager (npm install or yarn install) to update the lockfile
(package-lock.json or yarn.lock) and commit both the edited package.json and the
updated lockfile so the pinned version is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@android/build.gradle`:
- Around line 25-32: Wrap the rnrepo resolution and classpath registration so
they are skipped when DISABLE_RNREPO is set: surround the providers.exec(...)
call that sets rnrepoDir and the subsequent classpath
fileTree("${rnrepoDir}/gradle-plugin/build/libs", ...) line with a guard like if
(!System.getenv('DISABLE_RNREPO')) { ... } (or the equivalent Gradle/Groovy
null-check) so require.resolve('`@rnrepo/build-tools/package.json`') is not
invoked and the prebuilds-plugin.jar is not added to the buildscript classpath
when DISABLE_RNREPO is present.

---

Nitpick comments:
In `@package.json`:
- Line 50: Change the `@rnrepo/build-tools` dependency spec in package.json from
the caret range "^0.1.4" to the exact version "0.1.4"; then run your package
manager (npm install or yarn install) to update the lockfile (package-lock.json
or yarn.lock) and commit both the edited package.json and the updated lockfile
so the pinned version is preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1a0a8ac0-e324-4219-a9bc-db63e30dc056

📥 Commits

Reviewing files that changed from the base of the PR and between 80989f5 and bbc23c1.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • android/app/build.gradle
  • android/build.gradle
  • ios/Podfile
  • package.json
  • rnrepo.config.json
📜 Review details
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6930
File: package.json:101-101
Timestamp: 2026-02-05T13:55:06.688Z
Learning: The RocketChat/Rocket.Chat.ReactNative repository uses a fork of react-native-image-crop-picker (RocketChat/react-native-image-crop-picker) with custom Android edge-to-edge fixes, not the upstream ivpusic/react-native-image-crop-picker package. Dependencies should reference commit pins from the RocketChat fork.
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: android/build.gradle:3-8
Timestamp: 2026-03-31T11:59:31.061Z
Learning: In the RocketChat/Rocket.Chat.ReactNative repository, the React Native upgrade helper (https://react-native-community.github.io/upgrade-helper/?from=0.79.4&to=0.81.5) recommends kotlinVersion = "2.1.20", compileSdkVersion = 36, targetSdkVersion = 36, and buildToolsVersion = "36.0.0" in android/build.gradle for the RN 0.79.4 → 0.81.5 upgrade. These are the sanctioned values for this upgrade path and should not be flagged as compatibility concerns.
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: android/gradle/wrapper/gradle-wrapper.properties:3-3
Timestamp: 2026-03-31T11:58:54.608Z
Learning: In the RocketChat/Rocket.Chat.ReactNative repository, the React Native upgrade helper (https://react-native-community.github.io/upgrade-helper/) recommends gradle-8.14.3-bin for the React Native 0.79.4 → 0.81.5 upgrade. This is the sanctioned Gradle version for RN 0.81.5 even though it is above Kotlin 2.1.20's "fully supported" Gradle range (≤8.11); Kotlin 2.1.20 docs explicitly allow using newer Gradle versions with a caveat that deprecation warnings may appear.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-05-18T14:40:38.892Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use rocket.chat/eslint-config base with React, React Native, TypeScript, Jest plugins
📚 Learning: 2026-02-05T13:55:00.974Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6930
File: package.json:101-101
Timestamp: 2026-02-05T13:55:00.974Z
Learning: In this repository, the dependency on react-native-image-crop-picker should reference the RocketChat fork (RocketChat/react-native-image-crop-picker) with explicit commit pins, not the upstream ivpusic/react-native-image-crop-picker. Update package.json dependencies (and any lockfile) to point to the fork URL and a specific commit, ensuring edge-to-edge Android fixes are included. This pattern should apply to all package.json files in the repo that declare this dependency.

Applied to files:

  • package.json
📚 Learning: 2026-03-31T11:59:31.061Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: android/build.gradle:3-8
Timestamp: 2026-03-31T11:59:31.061Z
Learning: In the RocketChat/Rocket.Chat.ReactNative repository, the React Native upgrade helper (https://react-native-community.github.io/upgrade-helper/?from=0.79.4&to=0.81.5) recommends kotlinVersion = "2.1.20", compileSdkVersion = 36, targetSdkVersion = 36, and buildToolsVersion = "36.0.0" in android/build.gradle for the RN 0.79.4 → 0.81.5 upgrade. These are the sanctioned values for this upgrade path and should not be flagged as compatibility concerns.

Applied to files:

  • package.json
  • android/app/build.gradle
  • android/build.gradle
📚 Learning: 2026-03-31T11:58:54.608Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: android/gradle/wrapper/gradle-wrapper.properties:3-3
Timestamp: 2026-03-31T11:58:54.608Z
Learning: In the RocketChat/Rocket.Chat.ReactNative repository, the React Native upgrade helper (https://react-native-community.github.io/upgrade-helper/) recommends gradle-8.14.3-bin for the React Native 0.79.4 → 0.81.5 upgrade. This is the sanctioned Gradle version for RN 0.81.5 even though it is above Kotlin 2.1.20's "fully supported" Gradle range (≤8.11); Kotlin 2.1.20 docs explicitly allow using newer Gradle versions with a caveat that deprecation warnings may appear.

Applied to files:

  • package.json
  • android/app/build.gradle
  • android/build.gradle
📚 Learning: 2026-05-18T14:40:38.892Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-05-18T14:40:38.892Z
Learning: React 19.1, React Native 0.81, Expo 54 versions should be used

Applied to files:

  • package.json
📚 Learning: 2026-05-18T14:40:38.892Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-05-18T14:40:38.892Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use rocket.chat/eslint-config base with React, React Native, TypeScript, Jest plugins

Applied to files:

  • package.json
📚 Learning: 2026-03-30T15:49:30.957Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6875
File: app/containers/RoomItem/Actions.tsx:12-12
Timestamp: 2026-03-30T15:49:30.957Z
Learning: In RocketChat/Rocket.Chat.ReactNative, `react-native-worklets` version 0.6.1 does NOT export a built-in Jest mock (e.g., no `react-native-worklets/lib/module/mock`). The correct Jest mock approach for this version is to add a manual mock in `jest.setup.js`: `jest.mock('react-native-worklets', () => ({ scheduleOnRN: jest.fn((fn, ...args) => fn(...args)) }))`.

Applied to files:

  • package.json
📚 Learning: 2026-05-07T17:47:14.516Z
Learnt from: diegolmello
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7303
File: package.json:5-5
Timestamp: 2026-05-07T17:47:14.516Z
Learning: When reviewing pnpm `packageManager` version pins in any `package.json` (e.g., `"packageManager": "pnpm@<version>"`), don’t rely solely on web-search results to determine whether a version exists. For very recently published versions, cross-check the target version against the official pnpm release page (https://github.com/pnpm/pnpm/releases) and the npm registry page for pnpm (https://www.npmjs.com/package/pnpm) before flagging the pinned version as non-existent.

Applied to files:

  • package.json

Comment thread android/build.gradle
Comment on lines +25 to +32
// RNRepo: substitute supported libraries with prebuilt artifacts
def rnrepoDir = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('@rnrepo/build-tools/package.json')")
}.standardOutput.asText.get().trim()
).getParentFile().absolutePath
classpath fileTree(dir: "${rnrepoDir}/gradle-plugin/build/libs", include: ["prebuilds-plugin.jar"])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does @rnrepo/build-tools0.1.4 honorDISABLE_RNREPO=1 before Gradle/CocoaPods plugin initialization, or only after the plugins have already been resolved/applied?

💡 Result:

Answer: Based on the official rnrepo documentation, DISABLE_RNREPO is checked to “completely skip the RNRepo plugin setup” for a given build, rather than only disabling individual downloads later. The docs indicate that when DISABLE_RNREPO is set (to any value), the plugin will not execute at all for that Gradle/CocoaPods run—so it prevents the RNRepo plugin’s behavior from running during that build lifecycle, not merely after the plugin has already been resolved/applied. Evidence: - The troubleshooting guide states: “If this variable is set to ANY value, the plugin will not execute.” [1] - It further says: “This environment variable takes precedence and will completely skip the RNRepo plugin setup regardless of the Gradle task being executed.” [1] Important nuance: - The documentation confirms “skip setup/execute” behavior, but it does not spell out the exact implementation order relative to Gradle plugin resolution/application phases (i.e., whether the code path avoids all work immediately upon init vs. after the Gradle/CocoaPods plugin entrypoint is invoked). It only guarantees that the plugin won’t execute / setup when DISABLE_RNREPO is set. [1] For reference, official setup/testing commands show using DISABLE_RNREPO with Gradle commands like assembleDebug. [2]

Citations:


Gate rnrepo build-tools resolution/classpath wiring behind DISABLE_RNREPO
@rnrepo/build-tools docs state that when DISABLE_RNREPO is set to any value, the plugin “will not execute / completely skip the RNRepo plugin setup.” However, android/build.gradle still unconditionally runs require.resolve('@rnrepo/build-tools/package.json') and adds prebuilds-plugin.jar to the buildscript classpath during Gradle configuration. That means the documented opt-out may skip plugin behavior, but it does not avoid requiring @rnrepo/build-tools/the jar to be present and resolvable at configuration time—potentially undermining the “source-only” escape hatch.
Wrap the rnrepoDir resolution and classpath fileTree(...) registration in a DISABLE_RNREPO check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/build.gradle` around lines 25 - 32, Wrap the rnrepo resolution and
classpath registration so they are skipped when DISABLE_RNREPO is set: surround
the providers.exec(...) call that sets rnrepoDir and the subsequent classpath
fileTree("${rnrepoDir}/gradle-plugin/build/libs", ...) line with a guard like if
(!System.getenv('DISABLE_RNREPO')) { ... } (or the equivalent Gradle/Groovy
null-check) so require.resolve('`@rnrepo/build-tools/package.json`') is not
invoked and the prebuilds-plugin.jar is not added to the buildscript classpath
when DISABLE_RNREPO is present.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

The iOS build failed compiling NitroMmkv (react-native-mmkv) because its
provider, NitroModules, was substituted with a prebuilt xcframework while
NitroMmkv itself builds from source (no prebuild available). Prebuilt
provider + source consumer is an incompatible Swift/C++ module pairing.
Force NitroModules from source on iOS so it matches its source-built
consumers. Android prebuilds NitroModules without issue, so it is left
enabled there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant