Uploading Source Maps to Bugsnag | Stallion OTA Updates

Learn how to upload React Native source maps from Stallion OTA updates to Bugsnag for accurate error monitoring. Complete guide for Hermes and JavaScript Core builds, including source map composition, code bundle ID configuration, and Bugsnag integration with Stallion for production debugging.

CLI Requirement:

Source map upload to Bugsnag requires Stallion CLI version 2.4.0 or above. Make sure you're running the latest version. Check the CLI Installation guide if you need to update.

Publish Bundle

When publishing with --keep-artifacts=true and --sourcemap=true, Stallion performs the standard OTA publish flow and additionally persists all bundle and sourcemap outputs on disk for Bugsnag integration.

Example command:

stallion publish-bundle --upload-path=orgname/project-name/bucket-name --platform=android/ios --release-note="notes" --keep-artifacts=true --sourcemap=true

This enables deterministic Bugsnag integration without re-running any React Native bundling step.

  • Normal OTA publish is executed

    Stallion generates the platform bundles, applies Hermes compilation when enabled, uploads the OTA payload to the Stallion backend, and completes the rollout exactly as in a standard publish.

  • Source maps are generated

    With --sourcemap=true, Stallion ensures both standard JS sourcemaps and Hermes sourcemaps are produced for the publish.

    Instead of treating the bundle and sourcemap as temporary files, Stallion keeps the final outputs in a stable directory structure so you can reliably pick them up in CI and upload to Bugsnag.

  • Artifacts are persisted on disk

    With --keep-artifacts=true, Stallion does not delete any intermediate build outputs and writes the final bundle + sourcemap pairs into a stable, CI-friendly directory.

    The resulting structure is:

Stallion Artifacts Structure

stallion-artifacts/ ├── android/ │ ├── normal/ │ │ ├── index.android.bundle │ │ └── index.android.bundle.map │ └── hermes/ │ ├── index.android.bundle │ └── index.android.bundle.hbc.map └── ios/ ├── normal/ | ├── main.jsbundle | └── main.jsbundle.map └── hermes/ ├── main.jsbundle └── main.jsbundle.hbc.map

Prepare sourcemaps for Bugsnag

Javascript Core

You can upload normal JS sourcemap directly to Bugsnag. No additional steps are required.

Hermes

For Hermes builds, you need to compose the packager and Hermes sourcemaps. Use the following commands:

mv stallion-artifacts/android/normal/index.android.bundle.map stallion-artifacts/android/normal/index.android.bundle.packager.map
node \
  node_modules/react-native/scripts/compose-source-maps.js \
  stallion-artifacts/android/normal/index.android.bundle.packager.map \
  stallion-artifacts/android/hermes/index.android.bundle.hbc.map \
  -o stallion-artifacts/android/normal/index.android.bundle.map
rm -f stallion-artifacts/android/normal/index.android.bundle.packager.map

Upload to Bugsnag

Installation

Install the Bugsnag source maps uploader:

npm install --save-dev @bugsnag/source-maps
# or
yarn add --dev @bugsnag/source-maps

Extract Bundle Hash

When publishing a bundle, extract the bundle hash from the output. In your CI pipeline:

OUTPUT=$(stallion publish-bundle \
  --upload-path=$UPLOAD_PATH \
  --platform=$PLATFORM \
  --release-note="$RELEASE_NOTE" \
  --keep-artifacts=true \
  --sourcemap=true)

BUNDLE_HASH=$(echo "$OUTPUT" | grep -oE '[a-f0-9]{64}')
CODE_BUNDLE_ID=${BUNDLE_HASH:0:32}  # First 32 characters

The CODE_BUNDLE_ID (first 32 characters of the bundle hash) is used to associate source maps with the running bundle, similar to how CodePush uses bundle identifiers.

Upload Source Maps

Upload the bundle and source map to Bugsnag using the code bundle ID:

Javascript Core

Upload source maps for React Native JavaScript Core application.

npx bugsnag-source-maps upload-react-native \
  --api-key YOUR_API_KEY_HERE \
  --code-bundle-id $CODE_BUNDLE_ID \
  --platform android \
  --source-map stallion-artifacts/android/normal/index.android.bundle.map \
  --bundle stallion-artifacts/android/normal/index.android.bundle

Hermes

Upload source maps for React Native Hermes applications.

npx bugsnag-source-maps upload-react-native \
  --api-key YOUR_API_KEY_HERE \
  --code-bundle-id $CODE_BUNDLE_ID \
  --platform android \
  --source-map stallion-artifacts/android/normal/index.android.bundle.map \
  --bundle stallion-artifacts/android/hermes/index.android.bundle

Configure Bugsnag in Your App

In your React Native app, configure Bugsnag to use the code bundle ID from the currently running Stallion bundle:

import Bugsnag from '@bugsnag/react-native';
import { useStallionUpdate } from 'react-native-stallion';

// Get the current bundle ID from Stallion
const { currentlyRunningBundle } = useStallionUpdate();

Bugsnag.start({
  codeBundleId: currentlyRunningBundle?.id, // First 32 chars of bundle hash
  // ... other Bugsnag configuration
});

The currentlyRunningBundle.id from Stallion SDK matches the first 32 characters of the bundle hash, which is the same CODE_BUNDLE_ID used when uploading source maps. This ensures Bugsnag can correctly associate stack traces with the uploaded source maps.

Complete CI Example

Here's a complete GitHub Actions example that publishes, extracts the hash, and uploads to Bugsnag:

- name: Publish Bundle and Extract Hash
  id: publish
  run: |
    echo "Publishing bundle..."
    OUTPUT=$(stallion publish-bundle \
      --upload-path=$UPLOAD_PATH \
      --platform=$PLATFORM \
      --release-note="$RELEASE_NOTE" \
      --keep-artifacts=true \
      --sourcemap=true)

    echo "$OUTPUT"

    BUNDLE_HASH=$(echo "$OUTPUT" | grep -oE '[a-f0-9]{64}')
    CODE_BUNDLE_ID=${BUNDLE_HASH:0:32}
    
    echo "Bundle hash: $BUNDLE_HASH"
    echo "Code bundle ID: $CODE_BUNDLE_ID"
    
    echo "BUNDLE_HASH=$BUNDLE_HASH" >> $GITHUB_ENV
    echo "CODE_BUNDLE_ID=$CODE_BUNDLE_ID" >> $GITHUB_ENV

- name: Prepare Hermes Source Maps (if using Hermes)
  run: |
    if [ "$PLATFORM" = "android" ]; then
      mv stallion-artifacts/android/normal/index.android.bundle.map stallion-artifacts/android/normal/index.android.bundle.packager.map
      node node_modules/react-native/scripts/compose-source-maps.js \
        stallion-artifacts/android/normal/index.android.bundle.packager.map \
        stallion-artifacts/android/hermes/index.android.bundle.hbc.map \
        -o stallion-artifacts/android/normal/index.android.bundle.map
      rm -f stallion-artifacts/android/normal/index.android.bundle.packager.map
    else
      mv stallion-artifacts/ios/normal/main.jsbundle.map stallion-artifacts/ios/normal/main.jsbundle.packager.map
      node node_modules/react-native/scripts/compose-source-maps.js \
        stallion-artifacts/ios/normal/main.jsbundle.packager.map \
        stallion-artifacts/ios/hermes/main.jsbundle.hbc.map \
        -o stallion-artifacts/ios/normal/main.jsbundle.map
      rm -f stallion-artifacts/ios/normal/main.jsbundle.packager.map
    fi

- name: Upload Source Maps to Bugsnag
  run: |
    if [ "$PLATFORM" = "android" ]; then
      npx bugsnag-source-maps upload-react-native \
        --api-key $BUGSNAG_API_KEY \
        --code-bundle-id $CODE_BUNDLE_ID \
        --platform android \
        --source-map stallion-artifacts/android/normal/index.android.bundle.map \
        --bundle stallion-artifacts/android/hermes/index.android.bundle
    else
      npx bugsnag-source-maps upload-react-native \
        --api-key $BUGSNAG_API_KEY \
        --code-bundle-id $CODE_BUNDLE_ID \
        --platform ios \
        --source-map stallion-artifacts/ios/normal/main.jsbundle.map \
        --bundle stallion-artifacts/ios/hermes/main.jsbundle
    fi

- name: Release Bundle
  run: |
    stallion release-bundle \
      --project-id=$PROJECT_ID \
      --hash=$BUNDLE_HASH \
      --app-version=$APP_VERSION \
      --release-note="$RELEASE_NOTE" \
      --ci-token=$CI_TOKEN

Reference