diff --git a/.github/workflows/buildandtest.yml b/.github/workflows/buildandtest.yml index 0717c08c76..734bc40485 100644 --- a/.github/workflows/buildandtest.yml +++ b/.github/workflows/buildandtest.yml @@ -49,6 +49,18 @@ jobs: cache-dependency-path: yarn.lock - name: Install Dependencies run: yarn install + + - name: Install Swiftly + run: | + SWIFTLY_FILE="swiftly-$(uname -m).tar.gz" + curl -sL https://download.swift.org/swiftly/linux/swiftly-x86_64.tar.gz -o $SWIFTLY_FILE + tar zxf $SWIFTLY_FILE + + ./swiftly init --quiet-shell-followup + . "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" + hash -r + sudo apt-get -y install libcurl4-openssl-dev + - name: Lint run: yarn lint diff --git a/.github/workflows/sample-application.yml b/.github/workflows/sample-application.yml index db60b6114e..1882fa03cb 100644 --- a/.github/workflows/sample-application.yml +++ b/.github/workflows/sample-application.yml @@ -43,6 +43,7 @@ jobs: build-type: ['dev', 'production'] include: - platform: ios + xcode-version: '16.2' runs-on: macos-14 - platform: macos runs-on: macos-15 @@ -84,6 +85,9 @@ jobs: - name: Gradle cache uses: gradle/gradle-build-action@v3 + - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer + if: ${{ matrix.platform == 'ios' }} + - name: Setup Global Xcode Tools if: ${{ matrix.platform == 'ios' }} run: which xcbeautify || brew install xcbeautify diff --git a/.gitignore b/.gitignore index 1870007b3f..0467cca1e1 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,6 @@ node_modules.bak # Sentry React Native Monorepo /packages/core/README.md .env.sentry-build-plugin + +# SwiftLint +swiftlint/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f0cbb74d..910d6c74a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ > make sure you follow our [migration guide](https://docs.sentry.io/platforms/react-native/migration/) first. +## Unreleased + +### Fixes + +- Vendor `metro/countLines` function to avoid issues with the private import ([#5185](https://github.com/getsentry/sentry-react-native/pull/5185)) +- Fix baseJSBundle and bundleToString TypeErrors with Metro 0.83.2 ([#5206](https://github.com/getsentry/sentry-react-native/pull/5206)) +- Fixes .env file loading in Expo sourcemap uploads ([#5210](https://github.com/getsentry/sentry-react-native/pull/5210)) + +### Dependencies + +- Bump Cocoa SDK from v8.53.2 to v8.56.2 ([#5214](https://github.com/getsentry/sentry-react-native/pull/5214)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8562) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.53.2...8.56.2) + ## 6.21.0 ### Important Changes diff --git a/package.json b/package.json index e08b41c76b..3c8fc221fe 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "set-version-samples": "lerna run set-version" }, "devDependencies": { - "@expo/swiftlint": "^0.57.1", "@naturalcycles/ktlint": "^1.13.0", "@sentry/cli": "2.53.0", "clang-format": "^1.8.0", diff --git a/packages/core/RNSentry.podspec b/packages/core/RNSentry.podspec index b78039a056..3548e17a6f 100644 --- a/packages/core/RNSentry.podspec +++ b/packages/core/RNSentry.podspec @@ -46,7 +46,7 @@ Pod::Spec.new do |s| s.compiler_flags = other_cflags - s.dependency 'Sentry/HybridSDK', '8.53.2' + s.dependency 'Sentry/HybridSDK', '8.56.2' if defined? install_modules_dependencies # Default React Native dependencies for 0.71 and above (new and legacy architecture) diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift index 3b8765547f..0d7ef3aa12 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift @@ -67,11 +67,7 @@ final class RNSentryReplayOptions: XCTestCase { ] as NSDictionary).mutableCopy() as! NSMutableDictionary RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.sessionSampleRate, 0.75) } @@ -82,11 +78,7 @@ final class RNSentryReplayOptions: XCTestCase { ] as NSDictionary).mutableCopy() as! NSMutableDictionary RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.onErrorSampleRate, 0.75) } @@ -116,11 +108,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllImages, true) assertContainsClass(classArray: actualOptions.sessionReplay.maskedViewClasses, stringClass: "RCTImageView") @@ -135,11 +123,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllImages, false) XCTAssertEqual(actualOptions.sessionReplay.maskedViewClasses.count, 0) @@ -154,11 +138,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllText, true) assertContainsClass(classArray: actualOptions.sessionReplay.maskedViewClasses, stringClass: "RCTTextView") @@ -182,11 +162,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllText, false) XCTAssertEqual(actualOptions.sessionReplay.maskedViewClasses.count, 0) @@ -200,11 +176,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertTrue(actualOptions.sessionReplay.enableViewRendererV2) } @@ -218,11 +190,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertTrue(actualOptions.sessionReplay.enableViewRendererV2) } @@ -236,11 +204,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertFalse(actualOptions.sessionReplay.enableViewRendererV2) } @@ -253,11 +217,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertFalse(actualOptions.sessionReplay.enableFastViewRendering) } @@ -271,11 +231,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertTrue(actualOptions.sessionReplay.enableFastViewRendering) } @@ -289,15 +245,11 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertFalse(actualOptions.sessionReplay.enableFastViewRendering) } - + func testReplayQualityDefault() { let optionsDict = ([ "dsn": "https://abc@def.ingest.sentry.io/1234567", @@ -306,11 +258,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.medium) } @@ -324,11 +272,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.low) } @@ -342,11 +286,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.medium) } @@ -360,11 +300,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.high) } @@ -378,11 +314,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.medium) } diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h index fa80410d6c..54e8504da0 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h @@ -3,12 +3,8 @@ @class SentryOptions; -#if CROSS_PLATFORM_TEST @interface SentrySDKInternal (PrivateTests) -#else -@interface -SentrySDK (PrivateTests) -#endif + + (nullable SentryOptions *)options; @end diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m index 2020a8e5a3..f6c989a455 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m @@ -465,13 +465,8 @@ - (void)prepareNativeFrameMocksWithLocalSymbolication:(BOOL)debug SentryOptions *sentryOptions = [[SentryOptions alloc] init]; sentryOptions.debug = debug; // no local symbolication -#if CROSS_PLATFORM_TEST id sentrySDKMock = OCMClassMock([SentrySDKInternal class]); OCMStub([(Class)sentrySDKMock options]).andReturn(sentryOptions); -#else - id sentrySDKMock = OCMClassMock([SentrySDK class]); - OCMStub([(Class)sentrySDKMock options]).andReturn(sentryOptions); -#endif id sentryDependencyContainerMock = OCMClassMock([SentryDependencyContainer class]); OCMStub(ClassMethod([sentryDependencyContainerMock sharedInstance])) diff --git a/packages/core/ios/RNSentry+fetchNativeStack.m b/packages/core/ios/RNSentry+fetchNativeStack.m index 3e1f37a3b8..72515ff235 100644 --- a/packages/core/ios/RNSentry+fetchNativeStack.m +++ b/packages/core/ios/RNSentry+fetchNativeStack.m @@ -22,11 +22,8 @@ - (NSDictionary *)fetchNativeStackFramesBy:(NSArray *)instructionsAddr symbolicate:(SymbolicateCallbackType)symbolicate { -#if CROSS_PLATFORM_TEST BOOL shouldSymbolicateLocally = [SentrySDKInternal.options debug]; -#else - BOOL shouldSymbolicateLocally = [SentrySDK.options debug]; -#endif + NSString *appPackageName = [[NSBundle mainBundle] executablePath]; NSMutableSet *_Nonnull imagesAddrToRetrieveDebugMetaImages = diff --git a/packages/core/ios/RNSentry.h b/packages/core/ios/RNSentry.h index c13773f6c2..074c8f270e 100644 --- a/packages/core/ios/RNSentry.h +++ b/packages/core/ios/RNSentry.h @@ -15,12 +15,7 @@ typedef int (*SymbolicateCallbackType)(const void *, Dl_info *); @class SentryOptions; @class SentryEvent; -#if CROSS_PLATFORM_TEST @interface SentrySDKInternal : NSObject -#else -@interface -SentrySDK (Private) -#endif @property (nonatomic, nullable, readonly, class) SentryOptions *options; @end diff --git a/packages/core/ios/RNSentry.mm b/packages/core/ios/RNSentry.mm index 8072422085..f57d03e601 100644 --- a/packages/core/ios/RNSentry.mm +++ b/packages/core/ios/RNSentry.mm @@ -28,16 +28,9 @@ #import #import #import -#import - -#if __has_include() -# define USE_SENTRY_OPTIONS 1 -# import -#else -# define USE_SENTRY_OPTIONS 0 -# import -#endif +#import #import +#import // This guard prevents importing Hermes in JSC apps #if SENTRY_PROFILING_ENABLED @@ -169,13 +162,8 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull) [RNSentryReplay updateOptions:mutableOptions]; #endif -#if USE_SENTRY_OPTIONS - SentryOptions *sentryOptions = [[SentryOptions alloc] initWithDict:mutableOptions - didFailWithError:errorPointer]; -#else SentryOptions *sentryOptions = [SentryOptionsInternal initWithDict:mutableOptions didFailWithError:errorPointer]; -#endif if (*errorPointer != nil) { return nil; } @@ -364,6 +352,58 @@ - (void)stopObserving return [self fetchNativeStackFramesBy:instructionsAddr symbolicate:dladdr]; } +RCT_EXPORT_METHOD(fetchNativeLogAttributes + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) +{ + __block NSMutableDictionary *result = [NSMutableDictionary new]; + + [SentrySDKWrapper configureScope:^(SentryScope *_Nonnull scope) { + // Serialize to get contexts dictionary + NSDictionary *serializedScope = [scope serialize]; + NSDictionary *allContexts = serializedScope[@"context"]; // It's singular here, annoyingly + + NSMutableDictionary *contexts = [NSMutableDictionary new]; + + NSDictionary *device = allContexts[@"device"]; + if ([device isKindOfClass:[NSDictionary class]]) { + contexts[@"device"] = device; + } + + NSDictionary *os = allContexts[@"os"]; + if ([os isKindOfClass:[NSDictionary class]]) { + contexts[@"os"] = os; + } + + NSString *releaseName = SentrySDKInternal.options.releaseName; + if (releaseName) { + contexts[@"release"] = releaseName; + } + // Merge extra context + NSDictionary *extraContext = [PrivateSentrySDKOnly getExtraContext]; + + if (extraContext) { + NSDictionary *extraDevice = extraContext[@"device"]; + if ([extraDevice isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *mergedDevice = + [contexts[@"device"] mutableCopy] ?: [NSMutableDictionary new]; + [mergedDevice addEntriesFromDictionary:extraDevice]; + contexts[@"device"] = mergedDevice; + } + + NSDictionary *extraOS = extraContext[@"os"]; + if ([extraOS isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *mergedOS = + [contexts[@"os"] mutableCopy] ?: [NSMutableDictionary new]; + [mergedOS addEntriesFromDictionary:extraOS]; + contexts[@"os"] = mergedOS; + } + } + result[@"contexts"] = contexts; + }]; + resolve(result); +} + RCT_EXPORT_METHOD(fetchNativeDeviceContexts : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) diff --git a/packages/core/scripts/expo-upload-sourcemaps.js b/packages/core/scripts/expo-upload-sourcemaps.js index b3783b5721..135ba8fc2c 100755 --- a/packages/core/scripts/expo-upload-sourcemaps.js +++ b/packages/core/scripts/expo-upload-sourcemaps.js @@ -131,7 +131,7 @@ try { const sentryBuildPluginPath = path.join(projectRoot, '.env.sentry-build-plugin'); if (fs.existsSync(sentryBuildPluginPath)) { - loadDotenv(); + loadDotenv(sentryBuildPluginPath); } let sentryOrg = getEnvVar(SENTRY_ORG); diff --git a/packages/core/src/js/tools/sentryMetroSerializer.ts b/packages/core/src/js/tools/sentryMetroSerializer.ts index b34e21a1e3..71b653f889 100644 --- a/packages/core/src/js/tools/sentryMetroSerializer.ts +++ b/packages/core/src/js/tools/sentryMetroSerializer.ts @@ -1,19 +1,12 @@ import * as crypto from 'crypto'; // eslint-disable-next-line import/no-extraneous-dependencies import type { MixedOutput, Module, ReadOnlyGraph } from 'metro'; -import type * as countLinesType from 'metro/private/lib/countLines'; import type { Bundle, MetroSerializer, MetroSerializerOutput, SerializedBundle, VirtualJSOutput } from './utils'; import { createDebugIdSnippet, createSet, determineDebugIdFromBundleSource, stringToUUID } from './utils'; +import countLines from './vendor/metro/countLines'; import { createDefaultMetroSerializer } from './vendor/metro/utils'; -let countLines: typeof countLinesType; -try { - countLines = require('metro/private/lib/countLines'); -} catch (e) { - countLines = require('metro/src/lib/countLines'); -} - type SourceMap = Record; const DEBUG_ID_PLACE_HOLDER = '__debug_id_place_holder__'; diff --git a/packages/core/src/js/tools/vendor/metro/countLines.ts b/packages/core/src/js/tools/vendor/metro/countLines.ts new file mode 100644 index 0000000000..6beb72bcfc --- /dev/null +++ b/packages/core/src/js/tools/vendor/metro/countLines.ts @@ -0,0 +1,33 @@ +// Vendored from @facebook/metro + +// https://github.com/facebook/metro/blob/93d68cca249202fd3eb52672217e725d90e44eb4/packages/metro/src/lib/countLines.js + +// MIT License + +// Copyright (c) Meta Platforms, Inc. and affiliates. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This is exported as private from `metro` and as such breaking changes can appear anytime. +// In the past these were related to the way the function is exported. Never to the function itself. +// Thus it should be more stable to vendor it. + +const newline = /\r\n?|\n|\u2028|\u2029/g; + +export default (string: string): number => (string.match(newline) || []).length + 1; diff --git a/packages/core/src/js/tools/vendor/metro/utils.ts b/packages/core/src/js/tools/vendor/metro/utils.ts index ba2817ee86..67460c3a96 100644 --- a/packages/core/src/js/tools/vendor/metro/utils.ts +++ b/packages/core/src/js/tools/vendor/metro/utils.ts @@ -30,13 +30,19 @@ import type * as baseJSBundleType from 'metro/private/DeltaBundler/Serializers/b import type * as sourceMapStringType from 'metro/private/DeltaBundler/Serializers/sourceMapString'; import type * as bundleToStringType from 'metro/private/lib/bundleToString'; -let baseJSBundle: typeof baseJSBundleType; +let baseJSBundleModule: any; try { - baseJSBundle = require('metro/private/DeltaBundler/Serializers/baseJSBundle'); -} catch (e) { - baseJSBundle = require('metro/src/DeltaBundler/Serializers/baseJSBundle'); + baseJSBundleModule = require('metro/private/DeltaBundler/Serializers/baseJSBundle'); +} catch { + baseJSBundleModule = require('metro/src/DeltaBundler/Serializers/baseJSBundle'); } +const baseJSBundle: typeof baseJSBundleType = + typeof baseJSBundleModule === 'function' + ? baseJSBundleModule + : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + baseJSBundleModule?.baseJSBundle ?? baseJSBundleModule?.default; + let sourceMapString: typeof sourceMapStringType; try { // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -51,14 +57,19 @@ try { } } -let bundleToString: typeof bundleToStringType; +let bundleToStringModule: any; try { - bundleToString = require('metro/private/lib/bundleToString'); -} catch (e) { - bundleToString = require('metro/src/lib/bundleToString'); + bundleToStringModule = require('metro/private/lib/bundleToString'); +} catch { + bundleToStringModule = require('metro/src/lib/bundleToString'); } import type { MetroSerializer } from '../../utils'; +const bundleToString: typeof bundleToStringType = + typeof bundleToStringModule === 'function' + ? bundleToStringModule + : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + bundleToStringModule?.bundleToString ?? bundleToStringModule?.default; type NewSourceMapStringExport = { // Since Metro v0.80.10 https://github.com/facebook/metro/compare/v0.80.9...v0.80.10#diff-1b836d1729e527a725305eef0cec22e44605af2700fa413f4c2489ea1a03aebcL28 diff --git a/performance-tests/metrics-ios.yml b/performance-tests/metrics-ios.yml index 6210780f8f..f29781987f 100644 --- a/performance-tests/metrics-ios.yml +++ b/performance-tests/metrics-ios.yml @@ -11,4 +11,4 @@ startupTimeTest: binarySizeTest: diffMin: 600 KiB - diffMax: 1300 KiB + diffMax: 1400 KiB diff --git a/samples/react-native/ios/SentryNativeInitializer.h b/samples/react-native/ios/SentryNativeInitializer.h new file mode 100644 index 0000000000..9d0c2b1d1a --- /dev/null +++ b/samples/react-native/ios/SentryNativeInitializer.h @@ -0,0 +1,7 @@ +#import + +@interface SentryNativeInitializer : NSObject + ++ (void)initializeSentry; + +@end diff --git a/samples/react-native/ios/SentryNativeInitializer.m b/samples/react-native/ios/SentryNativeInitializer.m new file mode 100644 index 0000000000..20a4bcaa44 --- /dev/null +++ b/samples/react-native/ios/SentryNativeInitializer.m @@ -0,0 +1,38 @@ +#import "SentryNativeInitializer.h" +@import Sentry; + +@implementation SentryNativeInitializer + ++ (void)initializeSentry +{ + [SentrySDK startWithConfigureOptions:^(SentryOptions *options) { + // Only options set here will apply to the iOS SDK + // Options from JS are not passed to the iOS SDK when initialized manually + options.dsn = @"https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561"; + options.debug = YES; // Enabled debug when first installing is always helpful + + options.beforeSend = ^SentryEvent *(SentryEvent *event) + { + // We don't want to send an event after startup that came from a Unhandled JS Exception + // of react native Because we sent it already before the app crashed. + if (nil != event.exceptions.firstObject.type && + [event.exceptions.firstObject.type rangeOfString:@"Unhandled JS Exception"].location + != NSNotFound) { + NSLog(@"Unhandled JS Exception"); + return nil; + } + + return event; + }; + + // Enable the App start and Frames tracking measurements + // If this is disabled the app start and frames tracking + // won't be passed from native to JS transactions + PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true; +#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST + PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = true; +#endif + }]; +} + +@end diff --git a/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj b/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj index 0d2f979cb0..d14c1bbb15 100644 --- a/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj +++ b/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 5ACADB1A9924EDD0850FACBA /* libPods-sentryreactnativesample-sentryreactnativesampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AFC2BCCFBDE2DC231B5C04E5 /* libPods-sentryreactnativesample-sentryreactnativesampleTests.a */; }; 723FB93385E08A7032AE82F4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 916F55A329D66130A4DFF319 /* PrivacyInfo.xcprivacy */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + AE3C91AE2E3BB9B400DC7C20 /* SentryNativeInitializer.m in Sources */ = {isa = PBXBuildFile; fileRef = AE3C91AD2E3BB9B000DC7C20 /* SentryNativeInitializer.m */; }; F998F5A3F1731876C4EDA235 /* libPods-sentryreactnativesample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CA08EE94AE203638B8C8B74B /* libPods-sentryreactnativesample.a */; }; /* End PBXBuildFile section */ @@ -49,6 +50,8 @@ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = sentryreactnativesample/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-sentryreactnativesample-sentryreactnativesampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-sentryreactnativesample-sentryreactnativesampleTests.release.xcconfig"; path = "Target Support Files/Pods-sentryreactnativesample-sentryreactnativesampleTests/Pods-sentryreactnativesample-sentryreactnativesampleTests.release.xcconfig"; sourceTree = ""; }; 916F55A329D66130A4DFF319 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = sentryreactnativesample/PrivacyInfo.xcprivacy; sourceTree = ""; }; + AE3C91AC2E3BB98B00DC7C20 /* SentryNativeInitializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryNativeInitializer.h; sourceTree = ""; }; + AE3C91AD2E3BB9B000DC7C20 /* SentryNativeInitializer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNativeInitializer.m; sourceTree = ""; }; AFC2BCCFBDE2DC231B5C04E5 /* libPods-sentryreactnativesample-sentryreactnativesampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-sentryreactnativesample-sentryreactnativesampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; CA08EE94AE203638B8C8B74B /* libPods-sentryreactnativesample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-sentryreactnativesample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -94,6 +97,8 @@ 13B07FAE1A68108700A75B9A /* sentryreactnativesample */ = { isa = PBXGroup; children = ( + AE3C91AD2E3BB9B000DC7C20 /* SentryNativeInitializer.m */, + AE3C91AC2E3BB98B00DC7C20 /* SentryNativeInitializer.h */, 338BBBC72B614FA10035844C /* NativePlatformSampleModule.h */, 338BBBC62B614FA10035844C /* NativePlatformSampleModule.mm */, 33E2D62929A7719600B5042B /* RCTAssetsModule.h */, @@ -427,6 +432,7 @@ 13B07FC11A68108700A75B9A /* main.m in Sources */, 338BBBC82B614FA10035844C /* NativePlatformSampleModule.mm in Sources */, 33E2D62A29A7719600B5042B /* RCTAssetsModule.m in Sources */, + AE3C91AE2E3BB9B400DC7C20 /* SentryNativeInitializer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -566,7 +572,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CC = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang.sh"; + CC = ""; CCACHE_BINARY = /opt/homebrew/bin/ccache; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; @@ -595,7 +601,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CXX = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang++.sh"; + CXX = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; @@ -626,8 +632,8 @@ "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", ); IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang.sh"; - LDPLUSPLUS = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang++.sh"; + LD = ""; + LDPLUSPLUS = ""; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", @@ -663,7 +669,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CC = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang.sh"; + CC = ""; CCACHE_BINARY = /opt/homebrew/bin/ccache; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; @@ -692,7 +698,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; - CXX = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang++.sh"; + CXX = ""; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; @@ -716,8 +722,8 @@ "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", ); IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang.sh"; - LDPLUSPLUS = "$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang++.sh"; + LD = ""; + LDPLUSPLUS = ""; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", diff --git a/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm b/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm index aedb222cbc..0df8934ff1 100644 --- a/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm +++ b/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm @@ -13,8 +13,7 @@ # import #endif -#import -#import +#import "SentryNativeInitializer.h" @interface AppDelegate () { @@ -23,44 +22,12 @@ @implementation AppDelegate -- (void)initializeSentry -{ - [SentrySDK startWithConfigureOptions:^(SentryOptions *options) { - // Only options set here will apply to the iOS SDK - // Options from JS are not passed to the iOS SDK when initialized manually - options.dsn = @"https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561"; - options.debug = YES; // Enabled debug when first installing is always helpful - - options.beforeSend = ^SentryEvent *(SentryEvent *event) - { - // We don't want to send an event after startup that came from a Unhandled JS Exception - // of react native Because we sent it already before the app crashed. - if (nil != event.exceptions.firstObject.type && - [event.exceptions.firstObject.type rangeOfString:@"Unhandled JS Exception"].location - != NSNotFound) { - NSLog(@"Unhandled JS Exception"); - return nil; - } - - return event; - }; - - // Enable the App start and Frames tracking measurements - // If this is disabled the app start and frames tracking - // won't be passed from native to JS transactions - PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true; -#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST - PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = true; -#endif - }]; -} - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // When the native init is enabled the `autoInitializeNativeSdk` // in JS has to be set to `false` - // [self initializeSentry]; + // [SentryNativeInitializer initializeSentry]; self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self]; self.dependencyProvider = [RCTAppDependencyProvider new]; diff --git a/scripts/swiftlint.sh b/scripts/swiftlint.sh index 9c7b2ec5a8..bd0cb23f3f 100755 --- a/scripts/swiftlint.sh +++ b/scripts/swiftlint.sh @@ -11,15 +11,57 @@ fi # Set the mode based on the first argument mode=$1 -DARWIN_PATH="$(dirname "$0")/../node_modules/@expo/swiftlint/bin/darwin-arm64/swiftlint" -LINUX_PATH="$(dirname "$0")/../node_modules/@expo/swiftlint/bin/linux-x64/swiftlint" +SWIFT_PATH=$(which swift 2>/dev/null || true) + +if [ -z "$SWIFT_PATH" ]; then + echo "SwiftLint requires swift, which is not installed or not found in PATH" + echo "To install Swift:" + echo " * ubuntu: follow steps here: https://www.swift.org/install/" + echo " * arch: yay -S swift-bin" + exit 1 +fi + +LINUX_BIN="https://github.com/realm/SwiftLint/releases/download/0.61.0/swiftlint_linux_amd64.zip" +LINUX_SHA="sha256:02f4f580bbb27fb618dbfa24ce2f14c926461c85c26941289f58340151b63ae4" +DARWIN_BIN="https://github.com/realm/SwiftLint/releases/download/0.61.0/portable_swiftlint.zip" +DARWIN_SHA="sha256:2342f3784307a02117e18f745fcd350c6acc6cab0e521c0c0e01c32a53a3b274" if [[ "$OSTYPE" == "darwin"* ]]; then - CMD="$DARWIN_PATH" + EXPECTED_SHA="$DARWIN_SHA" + EXPECTED_BIN="$DARWIN_BIN" else - CMD="$LINUX_PATH" + EXPECTED_SHA="$LINUX_SHA" + EXPECTED_BIN="$LINUX_BIN" fi +# Make ../swiftlint folder if it doesn't exist +SWIFTLINT_DIR="$(dirname "$0")/../swiftlint" +mkdir -p "$SWIFTLINT_DIR" + +# Skip download if sha256sum swiftlint.sha matches EXPECTED_SHA +SHA_FILE="$SWIFTLINT_DIR/swiftlint.sha" +if [ -f "$SHA_FILE" ] && [ "$(cat "$SHA_FILE")" = "$EXPECTED_SHA" ]; then + echo "SwiftLint already downloaded and verified." +else + echo "Clearing swiftlint folder..." + rm -rf "$SWIFTLINT_DIR"/* + + echo "Downloading SwiftLint..." + curl -L "$EXPECTED_BIN" -o "$SWIFTLINT_DIR/swiftlint.zip" + unzip "$SWIFTLINT_DIR/swiftlint.zip" -d "$SWIFTLINT_DIR" + # Save sha256sum of swiftlint.zip to ../swiftlint/swiftlint.sha + echo "$EXPECTED_SHA" > "$SHA_FILE" + # Remove swiftlint.zip + rm "$SWIFTLINT_DIR/swiftlint.zip" +fi + +if [ ! -f "$SHA_FILE" ] || [ "$(cat "$SHA_FILE")" != "$EXPECTED_SHA" ]; then + echo "Invalid SwiftLint, sha doesn't match the expected download." + exit 1 +fi + +CMD="$(dirname "$0")/../swiftlint/swiftlint" + if [ "$mode" = "fix" ]; then $CMD --fix elif [ "$mode" = "lint" ]; then diff --git a/yarn.lock b/yarn.lock index fa4927eee1..3bd3f887dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5916,15 +5916,6 @@ __metadata: languageName: node linkType: hard -"@expo/swiftlint@npm:^0.57.1": - version: 0.57.1 - resolution: "@expo/swiftlint@npm:0.57.1" - dependencies: - "@expo/spawn-async": ^1.5.0 - checksum: 87f744bb45cc3a4aa2a40424d21995547c138eef4d4918a3990859c9f143acd3ce463ff3f0c421c0b3e95a694abf7d8fcb8c8545dbe00d81767fc461a68c8378 - languageName: node - linkType: hard - "@expo/vector-icons@npm:^14.0.0": version: 14.0.2 resolution: "@expo/vector-icons@npm:14.0.2" @@ -27493,7 +27484,6 @@ __metadata: version: 0.0.0-use.local resolution: "sentry-react-native@workspace:." dependencies: - "@expo/swiftlint": ^0.57.1 "@naturalcycles/ktlint": ^1.13.0 "@sentry/cli": 2.53.0 clang-format: ^1.8.0