Rock (formerly RNEF) integration

How to integrate your workflows with Rock using codemagic.yaml

Rock (formerly published as RNEF — React Native Enterprise Framework) is a modular CLI toolkit from Callstack designed for enterprise React Native teams. Its headline feature for CI is a remote build cache: native artifacts (APK, AAB, APP, IPA) are fingerprinted, stored in S3-compatible storage, and automatically reused across machines and CI runs, skipping full native builds for commits that only touch JavaScript. Callstack reports cache hits of up to 96% of builds on large codebases.

Rock’s default CI templates target GitHub Actions (the callstackincubator/ios and callstackincubator/android actions). On Codemagic, you install the Rock CLI via npm and invoke its commands directly in your codemagic.yaml scripts — no GitHub Actions wrapper required. The remote cache is pointed at AWS S3 or Cloudflare R2, both of which Codemagic can reach via encrypted environment variables.

This guide covers:


Prerequisites

  • A React Native project already configured with Rock. If you are migrating from Community CLI, follow the migration guide to initialise rock.config.mjs before proceeding.
  • An AWS S3 bucket (or Cloudflare R2 bucket) accessible with static credentials. For S3, the IAM user needs s3:PutObject, s3:GetObject, s3:DeleteObject, and s3:ListBucket permissions on the target bucket.
  • For Android release builds: a keystore file uploaded to Codemagic (see Step 2 below).
  • For iOS device/distribution builds: an Apple Developer certificate and provisioning profile uploaded to Codemagic (see Step 2 below).

How the Remote Cache Works

Rock computes a fingerprint of your native project using @expo/fingerprint. This hash captures everything that would require a native rebuild — native dependencies, Podfile.lock, build configuration, Gradle files, and so on. Pure JavaScript changes do not affect the fingerprint.

On each CI run, Rock:

  1. Computes the current native fingerprint.
  2. Looks for a cached artifact in your S3 bucket matching that fingerprint.
  3. If found, downloads and uses it — skipping the native build entirely.
  4. If not found, performs a full native build and uploads the resulting artifact to S3 for future use.

Only the first build after a native dependency change pays the full build-time cost. All subsequent runs on the same native state — including builds across branches and pull requests — reuse the cached artifact.


Step 1 — Configure rock.config.mjs for S3

Rock’s S3 provider is a separate package:

npm install @rock-js/provider-s3

Open (or create) your rock.config.mjs and add the remoteCacheProvider field. Reference credentials from environment variables so they remain out of source control:

// rock.config.mjs
import { platformIOS } from '@rock-js/platform-ios';
import { platformAndroid } from '@rock-js/platform-android';
import { pluginMetro } from '@rock-js/plugin-metro';
import { providerS3 } from '@rock-js/provider-s3';

export default {
  platforms: {
    ios: platformIOS(),
    android: platformAndroid(),
  },
  bundler: pluginMetro(),
  remoteCacheProvider: providerS3({
    bucket: process.env.ROCK_S3_BUCKET,
    region: process.env.ROCK_S3_REGION,
    accessKeyId: process.env.ROCK_S3_ACCESS_KEY_ID,
    secretAccessKey: process.env.ROCK_S3_SECRET_ACCESS_KEY,
  }),
};

Using Cloudflare R2 Instead of S3

R2 exposes an S3-compatible API. Add an endpoint option pointing to your R2 account and set region to auto:

remoteCacheProvider: providerS3({
  endpoint: `https://${process.env.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`,
  bucket: process.env.ROCK_S3_BUCKET,
  region: 'auto',
  accessKeyId: process.env.ROCK_S3_ACCESS_KEY_ID,
  secretAccessKey: process.env.ROCK_S3_SECRET_ACCESS_KEY,
}),

Step 2 — Add Signing Assets and Environment Variables in Codemagic

Remote Cache Credentials

In the Codemagic UI, navigate to Teams → your team → Global variables and secrets (or per-application under App settings → Environment variables). Create a group called rock_s3_cache and add the following variables, marking each as Secret:

VariableDescription
ROCK_S3_BUCKETS3 or R2 bucket name
ROCK_S3_REGIONAWS region (e.g. eu-west-1; use auto for R2)
ROCK_S3_ACCESS_KEY_IDIAM or R2 access key ID
ROCK_S3_SECRET_ACCESS_KEYIAM or R2 secret access key
CF_ACCOUNT_IDCloudflare account ID (R2 only)

Android Keystore

Upload your release keystore to Codemagic once and reference it by name in every workflow — no manual base64 encoding or decode steps required.

In the Codemagic UI, go to Teams → your team → Code signing identities → Android keystores and upload your .jks or .keystore file. Give it a reference name, for example rock_release_keystore. Codemagic will prompt for the store password, key alias, and key password at upload time and store them encrypted.

When the android_signing block references this keystore by name in codemagic.yaml, Codemagic automatically injects four environment variables at build time:

VariableContent
CM_KEYSTORE_PATHAbsolute path to the keystore file on the build machine
CM_KEYSTORE_PASSWORDKeystore store password
CM_KEY_ALIASKey alias
CM_KEY_PASSWORDKey password

iOS Certificate and Provisioning Profile

In the Codemagic UI, go to Teams → your team → Code signing identities → iOS certificates and upload your .p12 distribution certificate. Give it a reference name, for example distribution_cert. Then go to iOS provisioning profiles and upload your .mobileprovision file with a reference name such as distribution_profile.

When the ios_signing block references these by name in codemagic.yaml, Codemagic places the files on the build machine, installs the certificate into a temporary keychain automatically, and exposes the file paths via whatever environment_variable names you specify in the YAML.

App Store Connect API Key

In the Codemagic UI, go to Teams → your team → Team integrations → Developer Portal and click Connect. Upload your .p8 API key file and provide the associated Key ID and Issuer ID. Give the integration a name, for example app_store_connect_key. Once connected, reference it in your workflow with the integrations: block — no API key environment variables are needed in the

  integrations:
    app_store_connect: app_store_connect_key

This single reference covers both publishing to TestFlight/App Store and any Codemagic CLI tooling that needs to communicate with Apple’s APIs (such as automatic build number incrementing).


Step 3 — create codemagic.yaml

The following workflow installs Rock, checks for a cached native artifact in S3, runs a full build only on a cache miss, and produces a signed AAB ready for Google Play.
workflows:
  rock-android-release:
    name: Rock — Android Release
    max_build_duration: 60
    instance_type: linux_x2

    environment:
      groups:
        - rock_s3_cache        # ROCK_S3_BUCKET, ROCK_S3_REGION,
                               # ROCK_S3_ACCESS_KEY_ID, ROCK_S3_SECRET_ACCESS_KEY
      android_signing:
        - rock_release_keystore  # reference name set in Codemagic UI
                                 # injects: CM_KEYSTORE_PATH, CM_KEYSTORE_PASSWORD,
                                 #          CM_KEY_ALIAS, CM_KEY_PASSWORD
      vars:
        APP_MODULE: app
        BUILD_FLAVOR: ""         # leave empty if your project has no product flavors
      node: latest
      java: 17

    cache:
      cache_paths:
        - $HOME/.gradle/caches
        - $CM_BUILD_DIR/node_modules

    scripts:
      - name: Install Node dependencies
        script: npm ci

      - name: Install Rock CLI
        script: npm install -g @rock-js/cli

      # Rock checks the S3 cache before building. A cache hit skips Gradle
      # compilation entirely; a miss builds from source and uploads the artifact.
      # CM_KEYSTORE_PATH and related variables are injected automatically by
      # Codemagic from the android_signing block above.
      - name: Build Android (with remote cache)
        script: | 
          rock remote-cache download --platform android --variant release || true

          if rock remote-cache is-cached --platform android --variant release; then
            echo "Cache hit — skipping native build"
          else
            echo "No cache hit — building from source"
            rock build:android \
              --variant release \
              --aab \
              -P ROCK_UPLOAD_STORE_FILE="$CM_KEYSTORE_PATH" \
              -P ROCK_UPLOAD_STORE_PASSWORD="$CM_KEYSTORE_PASSWORD" \
              -P ROCK_UPLOAD_KEY_ALIAS="$CM_KEY_ALIAS" \
              -P ROCK_UPLOAD_KEY_PASSWORD="$CM_KEY_PASSWORD"

            rock remote-cache upload --platform android --variant release
          fi

    artifacts:
      - android/app/build/outputs/**/*.aab
      - android/app/build/outputs/**/*.apk

    publishing:
      google_play:
        credentials: $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS
        track: internal
      email:
        recipients:
          - mobile@yourcompany.com
        notify:
          success: true
          failure: true
iOS builds must run on a macOS instance. The workflow below uses Codemagic's built-in signing management: the certificate is installed into a temporary keychain automatically and the paths to the certificate and provisioning profile are injected as environment variables — no manual `security import` or keychain setup required.
workflows:
  rock-ios-release:
    name: Rock — iOS Release
    max_build_duration: 90
    instance_type: mac_mini_m2

    integrations:
      app_store_connect: app_store_connect_key # name given when connecting in Team UI
    environment:
      groups:
        - rock_s3_cache        # ROCK_S3_BUCKET, ROCK_S3_REGION,
                               # ROCK_S3_ACCESS_KEY_ID, ROCK_S3_SECRET_ACCESS_KEY
      ios_signing:
        provisioning_profiles:
          - profile: distribution_profile   # reference name set in Codemagic UI
            environment_variable: PROVISIONING_PROFILE_PATH
        certificates:
          - certificate: distribution_cert  # reference name set in Codemagic UI
            environment_variable: CERTIFICATE_PATH
      vars:
        XCODE_WORKSPACE: YourApp.xcworkspace
        XCODE_SCHEME: YourApp
        BUNDLE_ID: com.example.yourapp
      node: latest
      xcode: latest
      cocoapods: default

    cache:
      cache_paths:
        - $HOME/Library/Caches/CocoaPods
        - $CM_BUILD_DIR/node_modules

    scripts:
      - name: Install Node dependencies
        script: npm ci

      - name: Install Rock CLI
        script: npm install -g @rock-js/cli

      - name: Install CocoaPods dependencies
        script: cd ios && pod install

      # Rock checks the S3 cache before building. A cache hit skips Xcode
      # compilation entirely; a miss builds from source and uploads the artifact.
      # CERTIFICATE_PATH and PROVISIONING_PROFILE_PATH are injected automatically
      # by Codemagic from the ios_signing block above.
      - name: Build iOS (with remote cache)
        script: | 
          rock remote-cache download \
            --platform ios \
            --destination device \
            --configuration Release || true

          if rock remote-cache is-cached \
            --platform ios \
            --destination device \
            --configuration Release; then
            echo "Cache hit — skipping native build"
          else
            echo "No cache hit — building from source"
            rock build:ios \
              --destination device \
              --configuration Release \
              --extra-params "CODE_SIGN_IDENTITY='iPhone Distribution' \
                 PROVISIONING_PROFILE_SPECIFIER='$BUNDLE_ID'"

            rock remote-cache upload \
              --platform ios \
              --destination device \
              --configuration Release
          fi

    artifacts:
      - build/ios/ipa/*.ipa
      - /tmp/xcodebuild_logs/*.log
      - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.dSYM

    publishing:
      app_store_connect:
        # Use referenced App Store Connect API key to authenticate binary upload
        auth: integration 
        submit_to_testflight: true
      email:
        recipients:
          - mobile@yourcompany.com
        notify:
          success: true
          failure: true

Caching Behaviour and Cache Management

Rock stores each artifact under a key derived from the native fingerprint, platform, variant/configuration, and destination. The .rock/ directory in your project root holds locally downloaded cache entries; you do not need to include this in Codemagic’s cache_paths since remote artifacts are fetched fresh on every run.

Cache entries in S3 accumulate over time. If bucket size becomes a concern, apply an S3 lifecycle policy to expire objects older than 30–60 days. Because fingerprints are deterministic, expiring old entries only means the next CI run on that native state pays the full build cost once before re-populating the cache.


Monorepo Layout

If your React Native app lives inside a monorepo sub-directory (e.g. packages/mobile), run all Rock commands from that directory:

scripts:
  - name: Install dependencies
    script: cd packages/mobile && npm ci

  - name: Build Android
    script: | 
      cd packages/mobile
      rock remote-cache download --platform android --variant release || true
      rock build:android --variant release --aab
      rock remote-cache upload --platform android --variant release

The rock.config.mjs file must live in the sub-package root alongside package.json, not at the repository root.


Optional: Re.Pack and Super App Builds

Rock supports Re.Pack as an alternative bundler, enabling Module Federation–based Super App and microfrontend architectures. To switch from Metro to Re.Pack:

npm install @rock-js/plugin-repack
// rock.config.mjs
import { pluginRepack } from '@rock-js/plugin-repack';

export default {
  // ...
  bundler: pluginRepack(),
};

No changes to the codemagic.yaml build scripts are required — rock build:android and rock build:ios invoke Re.Pack automatically once the bundler plugin is configured.


Optional: Brownfield — Packaging React Native as a Native Library

Rock can package your React Native app as a native library (.xcframework for iOS, .aar for Android) for embedding into an existing native host application, using the @rock-js/plugin-brownfield-ios and @rock-js/plugin-brownfield-android plugins.

Add the plugins to your config:

import { pluginBrownfieldIos } from '@rock-js/plugin-brownfield-ios';
import { pluginBrownfieldAndroid } from '@rock-js/plugin-brownfield-android';

export default {
  plugins: [
    pluginBrownfieldIos(),
    pluginBrownfieldAndroid(),
  ],
  // ...
};

Then add packaging steps to your Codemagic build:

scripts:
  - name: Package React Native as iOS framework
    script: rock package:ios --configuration Release

  - name: Package React Native as Android AAR
    script: rock package:android --variant release

Outputs are placed under .rock/cache/ios/package/ (.xcframework) and .rock/cache/android/package/ (.aar) respectively. Include these paths in your artifacts: block to make them available as Codemagic build artifacts.


Environment Variable Reference

VariableSet inRequired forDescription
ROCK_S3_BUCKETenv groupRemote cacheS3 or R2 bucket name
ROCK_S3_REGIONenv groupRemote cacheAWS region (auto for R2)
ROCK_S3_ACCESS_KEY_IDenv groupRemote cacheS3/R2 access key ID
ROCK_S3_SECRET_ACCESS_KEYenv groupRemote cacheS3/R2 secret key
CF_ACCOUNT_IDenv groupRemote cache (R2)Cloudflare account ID
CM_KEYSTORE_PATHauto (android_signing)Android releasePath to keystore on build machine
CM_KEYSTORE_PASSWORDauto (android_signing)Android releaseKeystore store password
CM_KEY_ALIASauto (android_signing)Android releaseKey alias
CM_KEY_PASSWORDauto (android_signing)Android releaseKey password
CERTIFICATE_PATHauto (ios_signing)iOS releasePath to .p12 on build machine
PROVISIONING_PROFILE_PATHauto (ios_signing)iOS releasePath to .mobileprovision
GCLOUD_SERVICE_ACCOUNT_CREDENTIALSenv groupAndroid publishingGoogle Play service account JSON

Further Reading