From ee1962717c211492abe880d4f217ddad8eed6185 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Wed, 20 May 2026 12:15:03 +0200 Subject: [PATCH 1/4] feat(core): Add OTA SDK version to native sdk.packages when versions differ Pass the JS runtime SDK version to native during init. When it differs from the built-in version (indicating an OTA update changed the JS bundle), add an extra `npm:@sentry/react-native:ota` entry to `sdk.packages` with the actual runtime version. Closes https://github.com/getsentry/sentry-react-native/issues/4385 Co-Authored-By: Claude Opus 4.6 --- .../java/io/sentry/react/RNSentryStart.java | 17 +++++++++++++++- packages/core/ios/RNSentryStart.m | 20 ++++++++++++++++++- packages/core/src/js/wrapper.ts | 4 ++++ packages/core/test/wrapper.test.ts | 17 ++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java index cb4cae4309..358058b927 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java @@ -68,8 +68,11 @@ static void startWithOptions( @Nullable Activity currentActivity, @NotNull Sentry.OptionsConfiguration configuration, @NotNull ILogger logger) { + @Nullable + String jsSdkVersion = + rnOptions.hasKey("sdkVersion") ? rnOptions.getString("sdkVersion") : null; Sentry.OptionsConfiguration defaults = - options -> updateWithReactDefaults(options, currentActivity); + options -> updateWithReactDefaults(options, currentActivity, jsSdkVersion); Sentry.OptionsConfiguration rnConfigurationOptions = options -> getSentryAndroidOptions(options, rnOptions, logger); RNSentryCompositeOptionsConfiguration compositeConfiguration = @@ -319,6 +322,13 @@ private static void configureAndroidProfiling( */ static void updateWithReactDefaults( @NotNull SentryAndroidOptions options, @Nullable Activity currentActivity) { + updateWithReactDefaults(options, currentActivity, null); + } + + static void updateWithReactDefaults( + @NotNull SentryAndroidOptions options, + @Nullable Activity currentActivity, + @Nullable String jsSdkVersion) { @Nullable SdkVersion sdkVersion = options.getSdkVersion(); if (sdkVersion == null) { sdkVersion = new SdkVersion(RNSentryVersion.ANDROID_SDK_NAME, BuildConfig.VERSION_NAME); @@ -328,6 +338,11 @@ static void updateWithReactDefaults( sdkVersion.addPackage( RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_NAME, RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_VERSION); + if (jsSdkVersion != null + && !jsSdkVersion.equals(RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_VERSION)) { + sdkVersion.addPackage( + RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_NAME + ":ota", jsSdkVersion); + } options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion()); options.setNativeSdkName(RNSentryVersion.NATIVE_SDK_NAME); diff --git a/packages/core/ios/RNSentryStart.m b/packages/core/ios/RNSentryStart.m index 6114f57f62..c8237c1371 100644 --- a/packages/core/ios/RNSentryStart.m +++ b/packages/core/ios/RNSentryStart.m @@ -16,16 +16,34 @@ + (void)startWithOptions:(NSDictionary *_Nonnull)javascriptOptions error:errorPointer]; [self updateWithReactDefaults:options]; [self updateWithReactFinals:options]; - [self startWithOptions:options]; + NSString *jsSdkVersion = nil; + id jsSdkVersionValue = javascriptOptions[@"sdkVersion"]; + if ([jsSdkVersionValue isKindOfClass:[NSString class]]) { + jsSdkVersion = jsSdkVersionValue; + } + [self startWithOptions:options jsSdkVersion:jsSdkVersion]; } + (void)startWithOptions:(SentryOptions *)options NS_SWIFT_NAME(start(options:)) +{ + [self startWithOptions:options jsSdkVersion:nil]; +} + ++ (void)startWithOptions:(SentryOptions *)options + jsSdkVersion:(NSString *_Nullable)jsSdkVersion { NSString *sdkVersion = [PrivateSentrySDKOnly getSdkVersionString]; [PrivateSentrySDKOnly setSdkName:NATIVE_SDK_NAME andVersionString:sdkVersion]; [PrivateSentrySDKOnly addSdkPackage:REACT_NATIVE_SDK_PACKAGE_NAME version:REACT_NATIVE_SDK_PACKAGE_VERSION]; + if (jsSdkVersion != nil + && ![jsSdkVersion isEqualToString:REACT_NATIVE_SDK_PACKAGE_VERSION]) { + NSString *otaPackageName = + [REACT_NATIVE_SDK_PACKAGE_NAME stringByAppendingString:@":ota"]; + [PrivateSentrySDKOnly addSdkPackage:otaPackageName version:jsSdkVersion]; + } + [SentrySDK startWithOptions:options]; #if SENTRY_TARGET_REPLAY_SUPPORTED diff --git a/packages/core/src/js/wrapper.ts b/packages/core/src/js/wrapper.ts index c9d5236610..5117996a8f 100644 --- a/packages/core/src/js/wrapper.ts +++ b/packages/core/src/js/wrapper.ts @@ -35,6 +35,7 @@ import { isTurboModuleEnabled } from './utils/environment'; import { convertToNormalizedObject } from './utils/normalize'; import { ReactNativeLibraries } from './utils/rnlibraries'; import { base64StringFromByteArray } from './vendor'; +import { SDK_VERSION } from './version'; /** * Returns the RNSentry module. Dynamically resolves if NativeModule or TurboModule is used. @@ -60,6 +61,7 @@ export type NativeSdkOptions = Partial & { ignoreErrorsRegex?: string[] | undefined; } & { mobileReplayOptions: MobileReplayOptions | undefined; + sdkVersion?: string | undefined; profilingOptions?: ProfilingOptions | undefined; /** @deprecated Use `profilingOptions` instead. */ androidProfilingOptions?: ProfilingOptions | undefined; @@ -317,6 +319,8 @@ export const NATIVE: SentryNativeWrapper = { }; } + filteredOptions.sdkVersion = SDK_VERSION; + const nativeIsReady = await RNSentry.initNativeSdk(filteredOptions); this.nativeIsReady = nativeIsReady; diff --git a/packages/core/test/wrapper.test.ts b/packages/core/test/wrapper.test.ts index e7a3f164cd..a5d3b327fb 100644 --- a/packages/core/test/wrapper.test.ts +++ b/packages/core/test/wrapper.test.ts @@ -245,6 +245,23 @@ describe('Tests Native Wrapper', () => { expect(NATIVE.enableNative).toBe(true); }); + test('passes sdkVersion to native SDK', async () => { + await NATIVE.initNativeSdk({ + dsn: VALID_DSN, + enableNative: true, + autoInitializeNativeSdk: true, + devServerUrl: undefined, + defaultSidecarUrl: undefined, + mobileReplayOptions: undefined, + }); + + expect(RNSentry.initNativeSdk).toHaveBeenCalled(); + // @ts-expect-error mock value + const initParameter = RNSentry.initNativeSdk.mock.calls[0][0]; + expect(initParameter).toHaveProperty('sdkVersion'); + expect(typeof initParameter.sdkVersion).toBe('string'); + }); + test('passes attachAllThreads to native SDK', async () => { await NATIVE.initNativeSdk({ dsn: VALID_DSN, From 41fa4814d7d699d6191e4ec1d9e95c693a72ecc9 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Wed, 20 May 2026 12:18:15 +0200 Subject: [PATCH 2/4] docs: Add changelog entry for OTA SDK version feature Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 090a346215..51cb9e25b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Add `textComponentNames` option to `annotateReactComponents` for custom text components ([#6169](https://github.com/getsentry/sentry-react-native/pull/6169)) - Expose `addConsoleInstrumentationFilter` from `@sentry/core` ([#6180](https://github.com/getsentry/sentry-react-native/pull/6180)) - Expose experimental `captureSurfaceViews` option for Android Session Replay ([#6175](https://github.com/getsentry/sentry-react-native/pull/6175)) +- Add OTA SDK version to native `sdk.packages` when JS bundle version differs from built-in version ([#6191](https://github.com/getsentry/sentry-react-native/pull/6191)) ### Fixes From a7ca1137291d41c339eaf3dd4486549dbc06eab0 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Wed, 20 May 2026 12:28:59 +0200 Subject: [PATCH 3/4] style(android): Fix Java formatting for CI google-java-format Co-Authored-By: Claude Opus 4.6 --- .../src/main/java/io/sentry/react/RNSentryStart.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java index 358058b927..4f792521ac 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java @@ -69,8 +69,7 @@ static void startWithOptions( @NotNull Sentry.OptionsConfiguration configuration, @NotNull ILogger logger) { @Nullable - String jsSdkVersion = - rnOptions.hasKey("sdkVersion") ? rnOptions.getString("sdkVersion") : null; + String jsSdkVersion = rnOptions.hasKey("sdkVersion") ? rnOptions.getString("sdkVersion") : null; Sentry.OptionsConfiguration defaults = options -> updateWithReactDefaults(options, currentActivity, jsSdkVersion); Sentry.OptionsConfiguration rnConfigurationOptions = @@ -340,8 +339,7 @@ static void updateWithReactDefaults( RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_VERSION); if (jsSdkVersion != null && !jsSdkVersion.equals(RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_VERSION)) { - sdkVersion.addPackage( - RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_NAME + ":ota", jsSdkVersion); + sdkVersion.addPackage(RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_NAME + ":ota", jsSdkVersion); } options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion()); From 247160867628a43adacd9a1e628189faede41cb8 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Wed, 20 May 2026 12:52:52 +0200 Subject: [PATCH 4/4] style(ios): Fix clang-format violations in RNSentryStart.m Co-Authored-By: Claude Opus 4.6 --- packages/core/ios/RNSentryStart.m | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/core/ios/RNSentryStart.m b/packages/core/ios/RNSentryStart.m index c8237c1371..fa463f116e 100644 --- a/packages/core/ios/RNSentryStart.m +++ b/packages/core/ios/RNSentryStart.m @@ -29,18 +29,15 @@ + (void)startWithOptions:(SentryOptions *)options NS_SWIFT_NAME(start(options:)) [self startWithOptions:options jsSdkVersion:nil]; } -+ (void)startWithOptions:(SentryOptions *)options - jsSdkVersion:(NSString *_Nullable)jsSdkVersion ++ (void)startWithOptions:(SentryOptions *)options jsSdkVersion:(NSString *_Nullable)jsSdkVersion { NSString *sdkVersion = [PrivateSentrySDKOnly getSdkVersionString]; [PrivateSentrySDKOnly setSdkName:NATIVE_SDK_NAME andVersionString:sdkVersion]; [PrivateSentrySDKOnly addSdkPackage:REACT_NATIVE_SDK_PACKAGE_NAME version:REACT_NATIVE_SDK_PACKAGE_VERSION]; - if (jsSdkVersion != nil - && ![jsSdkVersion isEqualToString:REACT_NATIVE_SDK_PACKAGE_VERSION]) { - NSString *otaPackageName = - [REACT_NATIVE_SDK_PACKAGE_NAME stringByAppendingString:@":ota"]; + if (jsSdkVersion != nil && ![jsSdkVersion isEqualToString:REACT_NATIVE_SDK_PACKAGE_VERSION]) { + NSString *otaPackageName = [REACT_NATIVE_SDK_PACKAGE_NAME stringByAppendingString:@":ota"]; [PrivateSentrySDKOnly addSdkPackage:otaPackageName version:jsSdkVersion]; }