CI/CD
CI/CD Overview
For CI/CD, the main difference is authentication.
Instead of relying on a local interactive CLI session, you usually pass a Personal Access Token with --token.
Why --token Exists
CI environments do not go through bundle-drop login, so token-based auth is the normal approach for automated uploads.
Typical CI Upload
npx bundle-drop upload android \
--version 1.2.3 \
--channel develop \
--token $BUNDLE_DROP_PAT \
--author "CI Pipeline"Using --token with Other Commands
You can also use a token when bootstrapping setup without an interactive login:
npx bundle-drop init --token bdp_pat_xxxArtifacts and the Result File
When you pass --artifact-dir, Bundle Drop writes a bundle-drop-result.json file into that directory after a successful upload. This file contains all the metadata about the uploaded bundle:
{
"platform": "android",
"appVersion": "4.7.3",
"channel": "develop",
"orgSlug": "my-org",
"projectSlug": "my-app",
"runtimeVersion": "4.0.9",
"packageVersion": "0.4.5",
"bundleId": "...",
"bundleVersion": 339,
"bundleVersionLabel": "v339",
"hash": "a970a7a5f65b9fa5ff645386d4b3cbc1034a8d7d...",
"releaseNotes": "Fix login screen crash",
"author": "CI Pipeline",
"bundlePath": "/artifacts/bundledrop/android/main.jsbundle",
"sourceMapPath": "/artifacts/bundledrop/android/main.jsbundle.map"
}Note that packageVersion is the installed @gfean/react-native-bundle-drop SDK version, while appVersion is your app's version — they are different values.
You can read any field from this file with jq to use in later pipeline steps — setting variables, uploading source maps, posting notifications, tagging releases, etc.
Full CI Example with Artifact Variables
Here is a real-world pattern that uploads an OTA bundle for a given platform, retains artifacts, and extracts the result into CI pipeline variables. This example uses Azure DevOps variable syntax, but the same approach works with GitHub Actions, GitLab CI, Bitrise, or any CI system that supports shell scripts.
deploy_bundledrop() {
PLATFORM_NAME=$1
PLATFORM_LC=$(echo "$PLATFORM_NAME" | tr '[:upper:]' '[:lower:]')
ARTIFACT_DIR="$STAGING_DIR/bundledrop/$PLATFORM_LC"
mkdir -p "$ARTIFACT_DIR"
echo "Releasing OTA for $PLATFORM_NAME to channel $CHANNEL..."
if [ "$PLATFORM_LC" = "ios" ]; then
npx bundle-drop upload ios \
--plist-file ios/MyApp/Info.plist \
--channel "$CHANNEL" \
--token "$BUNDLE_DROP_TOKEN" \
--release-notes "$RELEASE_NOTES" \
--author "$BUILD_REQUESTER" \
--sourcemap \
--artifact-dir "$ARTIFACT_DIR"
else
npx bundle-drop upload android \
--buildgradle-path android/app/build.gradle \
--channel "$CHANNEL" \
--token "$BUNDLE_DROP_TOKEN" \
--release-notes "$RELEASE_NOTES" \
--author "$BUILD_REQUESTER" \
--sourcemap \
--artifact-dir "$ARTIFACT_DIR"
fi
BD_RESULT="$ARTIFACT_DIR/bundle-drop-result.json"
if [ ! -f "$BD_RESULT" ]; then
echo "Result file not found at $BD_RESULT"
exit 1
fi
# Extract values for downstream steps
BD_HASH=$(jq -r '.hash // empty' "$BD_RESULT")
BD_VERSION=$(jq -r '.bundleVersion // empty' "$BD_RESULT")
BD_LABEL=$(jq -r '.bundleVersionLabel // empty' "$BD_RESULT")
BD_APP_VERSION=$(jq -r '.appVersion // empty' "$BD_RESULT")
BD_RUNTIME=$(jq -r '.runtimeVersion // empty' "$BD_RESULT")
BD_BUNDLE=$(jq -r '.bundlePath // empty' "$BD_RESULT")
BD_SOURCEMAP=$(jq -r '.sourceMapPath // empty' "$BD_RESULT")
echo "Uploaded $PLATFORM_NAME bundle v$BD_VERSION ($BD_LABEL) — hash: $BD_HASH"
}Source Map Upload for Error Tracking
After extracting the hash, bundle path, and source-map path from the result
file, upload the matching artifacts to your error tracking provider using the
hash as the dist identifier:
# Sentry
sentry-cli sourcemaps upload \
--release "$RELEASE" \
--dist "$BD_HASH" \
--bundle "$BD_BUNDLE" \
--bundle-sourcemap "$BD_SOURCEMAP" \
--validate
# Bugsnag
bugsnag-sourcemaps upload \
--source-map "$BD_SOURCEMAP" \
--code-bundle-id "$BD_HASH" \
--app-version "$BD_APP_VERSION"
# Datadog
datadog-ci sourcemaps upload "$BD_SOURCEMAP" \
--service my-app \
--release-version "$BD_APP_VERSION" \
--minified-path-prefix /Without --sourcemap, the upload works exactly as before — no source map is generated, and artifacts are cleaned up after upload. See Observability for runtime integration details.
If the app uses @sentry/react-native 7.8.x with Hermes, apply the one-time
Sentry and Hermes OTA Metro configuration
so content-derived Debug IDs do not destabilize binary patch sizes.
What the Client Needs to Know
- use
bundle-drop loginfor local development - use
--tokenfor automated environments - keep the token in your CI secret store, not in source control
Expo projects use the same token flow and option-free platform uploads. If the project opts into strict Expo fingerprint authority, import the exact local or EAS build receipt before uploading. See Expo.
Related Docs
- For general CLI setup, see CLI.
- For upload command details, see Uploading.
- For error tracking integration, see Observability.
