From 92f8465c8bf63383e80db0db5a291563bdd02af4 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 19 Dec 2025 13:16:49 +0100 Subject: [PATCH 1/5] fix(e2e): Fix sed illegal byte sequence error in log redaction The sed command was failing with 'illegal byte sequence' on macOS when trying to redact sensitive data from all files in maestro-logs, including binary files (like replay videos). Changes: - Add LC_ALL=C environment variable for macOS to handle any byte sequence - Limit sed processing to text files only (.log, .txt, .yml, .yaml, .json) - Add error suppression (2>/dev/null || true) to prevent script failures This fix will allow us to see the actual test failure logs instead of the redaction script masking the real errors. --- dev-packages/e2e-tests/cli.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/cli.mjs b/dev-packages/e2e-tests/cli.mjs index c3ee22f3f7..e558795279 100755 --- a/dev-packages/e2e-tests/cli.mjs +++ b/dev-packages/e2e-tests/cli.mjs @@ -296,12 +296,14 @@ if (actions.includes('test')) { ); } finally { // Always redact sensitive data, even if the test fails + // Use LC_ALL=C to handle any byte sequence, and only process text files const redactScript = ` if [[ "$(uname)" == "Darwin" ]]; then - find ./maestro-logs -type f -exec sed -i '' "s/${sentryAuthToken}/[REDACTED]/g" {} + + export LC_ALL=C + find ./maestro-logs -type f \\( -name "*.log" -o -name "*.txt" -o -name "*.yml" -o -name "*.yaml" -o -name "*.json" \\) -exec sed -i '' "s/${sentryAuthToken}/[REDACTED]/g" {} + 2>/dev/null || true echo 'Redacted sensitive data from logs on MacOS' else - find ./maestro-logs -type f -exec sed -i "s/${sentryAuthToken}/[REDACTED]/g" {} + + find ./maestro-logs -type f \\( -name "*.log" -o -name "*.txt" -o -name "*.yml" -o -name "*.yaml" -o -name "*.json" \\) -exec sed -i "s/${sentryAuthToken}/[REDACTED]/g" {} + 2>/dev/null || true echo 'Redacted sensitive data from logs on Ubuntu' fi `; From 80b0457625ec6c051cb9570f52ca5fb7f599e311 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 19 Dec 2025 14:37:48 +0100 Subject: [PATCH 2/5] fix(e2e): Fix captureReplay test on iOS Hermes The test was failing because: 1. It was using the deprecated _experiments.replaysOnErrorSampleRate option 2. The sentryApi.js script didn't handle missing replay_id gracefully Changes: - Updated E2E test to use replaysOnErrorSampleRate directly (non-deprecated) - Added null check and better error message in sentryApi.js for missing replay_id Fixes the captureReplay test failure on: - Test RN 0.83.0 new hermes ios production no - Test RN 0.83.0 legacy hermes ios production no --- dev-packages/e2e-tests/maestro/utils/sentryApi.js | 6 +++++- dev-packages/e2e-tests/patch-scripts/rn.patch.app.js | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dev-packages/e2e-tests/maestro/utils/sentryApi.js b/dev-packages/e2e-tests/maestro/utils/sentryApi.js index 39f10ed298..53f6e3ca1e 100644 --- a/dev-packages/e2e-tests/maestro/utils/sentryApi.js +++ b/dev-packages/e2e-tests/maestro/utils/sentryApi.js @@ -63,7 +63,11 @@ switch (fetch) { } case 'replay': { const event = json(fetchFromSentry(`${baseUrl}/events/${eventId}/json/`)); - const replayId = event._dsc.replay_id.replace(/\-/g, ''); + const replayIdRaw = event._dsc?.replay_id; + if (!replayIdRaw) { + throw new Error(`Event ${eventId} does not have a replay_id in DSC. Replay might not be supported on this configuration.`); + } + const replayId = replayIdRaw.replace(/\-/g, ''); const replay = json(fetchFromSentry(`${baseUrl}/replays/${replayId}/`)); const segment = fetchFromSentry(`${baseUrl}/replays/${replayId}/videos/0/`); diff --git a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js index 2a6ac3b14d..18cffce38f 100755 --- a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js +++ b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js @@ -27,9 +27,7 @@ Sentry.init({ release: '${SENTRY_RELEASE}', dist: '${SENTRY_DIST}', dsn: 'https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561', - _experiments: { - replaysOnErrorSampleRate: LaunchArguments.value().replaysOnErrorSampleRate, - }, + replaysOnErrorSampleRate: LaunchArguments.value().replaysOnErrorSampleRate, integrations: [ Sentry.mobileReplayIntegration(), Sentry.feedbackIntegration({ From e89c38c03b0482dee9c45defcec068c8b7f8c497 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 19 Dec 2025 14:42:59 +0100 Subject: [PATCH 3/5] Revert "fix(e2e): Fix sed illegal byte sequence error in log redaction" This reverts commit 92f8465c8bf63383e80db0db5a291563bdd02af4. --- dev-packages/e2e-tests/cli.mjs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev-packages/e2e-tests/cli.mjs b/dev-packages/e2e-tests/cli.mjs index e558795279..c3ee22f3f7 100755 --- a/dev-packages/e2e-tests/cli.mjs +++ b/dev-packages/e2e-tests/cli.mjs @@ -296,14 +296,12 @@ if (actions.includes('test')) { ); } finally { // Always redact sensitive data, even if the test fails - // Use LC_ALL=C to handle any byte sequence, and only process text files const redactScript = ` if [[ "$(uname)" == "Darwin" ]]; then - export LC_ALL=C - find ./maestro-logs -type f \\( -name "*.log" -o -name "*.txt" -o -name "*.yml" -o -name "*.yaml" -o -name "*.json" \\) -exec sed -i '' "s/${sentryAuthToken}/[REDACTED]/g" {} + 2>/dev/null || true + find ./maestro-logs -type f -exec sed -i '' "s/${sentryAuthToken}/[REDACTED]/g" {} + echo 'Redacted sensitive data from logs on MacOS' else - find ./maestro-logs -type f \\( -name "*.log" -o -name "*.txt" -o -name "*.yml" -o -name "*.yaml" -o -name "*.json" \\) -exec sed -i "s/${sentryAuthToken}/[REDACTED]/g" {} + 2>/dev/null || true + find ./maestro-logs -type f -exec sed -i "s/${sentryAuthToken}/[REDACTED]/g" {} + echo 'Redacted sensitive data from logs on Ubuntu' fi `; From 8aef8f0708c753371cc0b4d2332d9fb22db38a42 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 19 Dec 2025 15:39:07 +0100 Subject: [PATCH 4/5] fix --- dev-packages/e2e-tests/patch-scripts/rn.patch.app.js | 11 ++++++++++- packages/core/ios/RNSentryStart.m | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js index 18cffce38f..03408862dc 100755 --- a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js +++ b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js @@ -23,11 +23,17 @@ import * as Sentry from '@sentry/react-native'; import { EndToEndTestsScreen } from 'sentry-react-native-e2e-tests'; import { LaunchArguments } from "react-native-launch-arguments"; +const launchArgs = LaunchArguments.value(); +const replaysOnErrorSampleRate = launchArgs.replaysOnErrorSampleRate !== undefined + ? launchArgs.replaysOnErrorSampleRate + : undefined; + Sentry.init({ release: '${SENTRY_RELEASE}', dist: '${SENTRY_DIST}', dsn: 'https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561', - replaysOnErrorSampleRate: LaunchArguments.value().replaysOnErrorSampleRate, + replaysOnErrorSampleRate: replaysOnErrorSampleRate, + replaysSessionSampleRate: launchArgs.replaysSessionSampleRate, integrations: [ Sentry.mobileReplayIntegration(), Sentry.feedbackIntegration({ @@ -35,6 +41,9 @@ Sentry.init({ }), ], }); + +console.log('[E2E] LaunchArguments:', launchArgs); +console.log('[E2E] Replay error sample rate:', replaysOnErrorSampleRate); `; const e2eComponentPatch = ''; const lastImportRex = /^([^]*)(import\s+[^;]*?;$)/m; diff --git a/packages/core/ios/RNSentryStart.m b/packages/core/ios/RNSentryStart.m index d40ac63ac0..36d28916e5 100644 --- a/packages/core/ios/RNSentryStart.m +++ b/packages/core/ios/RNSentryStart.m @@ -40,7 +40,16 @@ + (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull) NSMutableDictionary *mutableOptions = [options mutableCopy]; #if SENTRY_TARGET_REPLAY_SUPPORTED - [RNSentryReplay updateOptions:mutableOptions]; + // Log replay configuration for debugging + NSLog(@"[RNSentry] Replay config before updateOptions - replaysOnErrorSampleRate: %@, replaysSessionSampleRate: %@", + mutableOptions[@"replaysOnErrorSampleRate"], + mutableOptions[@"replaysSessionSampleRate"]); + + BOOL replayEnabled = [RNSentryReplay updateOptions:mutableOptions]; + + NSLog(@"[RNSentry] Replay enabled: %d, sessionReplay config: %@", + replayEnabled, + mutableOptions[@"sessionReplay"]); #endif SentryOptions *sentryOptions = [SentryOptionsInternal initWithDict:mutableOptions From 9fa4adc5b77be88994115e42d82195a977161f86 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 19 Dec 2025 15:41:49 +0100 Subject: [PATCH 5/5] Temporarily test only iOS Hermes 0.83.0 for replay debugging --- .github/workflows/e2e-v2.yml | 181 ++--------------------------------- 1 file changed, 9 insertions(+), 172 deletions(-) diff --git a/.github/workflows/e2e-v2.yml b/.github/workflows/e2e-v2.yml index 6b9c445ab5..c6bdd9f1d0 100644 --- a/.github/workflows/e2e-v2.yml +++ b/.github/workflows/e2e-v2.yml @@ -33,137 +33,8 @@ jobs: auth_token_check: uses: ./.github/workflows/skip-ci-noauth.yml secrets: inherit - metrics: - runs-on: ${{ matrix.runs-on }} - needs: [diff_check, auth_token_check] - if: ${{ needs.diff_check.outputs.skip_ci != 'true' && needs.auth_token_check.outputs.skip_ci != 'true' && !startsWith(github.ref, 'refs/heads/release/') }} - env: - SENTRY_DISABLE_AUTO_UPLOAD: 'true' - strategy: - # we want that the matrix keeps running, default is to cancel them if it fails. - fail-fast: false - matrix: - rn-architecture: ['legacy', 'new'] - platform: ["ios", "android"] - include: - - platform: ios - runs-on: macos-26 - name: iOS - appPlain: performance-tests/test-app-plain.ipa - - platform: android - # Not using the latest version due to a known issue: https://github.com/getsentry/sentry-react-native/issues/4418 - runs-on: ubuntu-22.04 - name: Android - appPlain: performance-tests/TestAppPlain/android/app/build/outputs/apk/release/app-release.apk - steps: - - uses: actions/checkout@v6 - - - uses: ./.github/actions/disk-cleanup - if: ${{ matrix.platform == 'android' }} - - - run: sudo xcode-select -s /Applications/Xcode_26.1.1.app/Contents/Developer - if: ${{ matrix.platform == 'ios' }} - - - run: npm i -g corepack - - uses: actions/setup-node@v6 - with: - package-manager-cache: false - node-version: 20 - cache: 'yarn' - cache-dependency-path: yarn.lock - - - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'adopt' - - - name: Gradle cache - uses: gradle/gradle-build-action@v3 - - - name: Install Global Dependencies - run: npm i -g react-native-cli @sentry/cli - - - name: Install Dependencies - run: yarn install - - - name: Build SDK - run: yarn build - - - uses: actions/cache@v5 - id: app-plain-cache - with: - path: ${{ matrix.appPlain }} - # if the whole plain app folder is hashed the cache is never hit as there are files generated in the folder - # the cache key is calculated both at cache retrieval and save time - # hashFiles fails when there are rn android new arch generated files in the folder (exact reason unknown) - # we removed the lock file of the app due to monorepo changes, we use the package.json instead - # to avoid frequent rebuilds of the app - key: ${{ github.workflow }}-${{ github.job }}-appplain-${{ matrix.platform }}-${{ matrix.rn-architecture }}-${{ hashFiles('performance-tests/TestAppSentry/package.json') }} - - - name: Build app plain - if: steps.app-plain-cache.outputs['cache-hit'] != 'true' - working-directory: ./performance-tests/TestAppPlain - run: | - cd ${{ matrix.platform }} - if [[ "${{ matrix.platform }}" == "android" ]]; then - if [[ ${{ matrix.rn-architecture }} == 'new' ]]; then - perl -i -pe's/newArchEnabled=false/newArchEnabled=true/g' gradle.properties - fi - ./gradlew assembleRelease - else - export PRODUCTION=1 - if [[ ${{ matrix.rn-architecture }} == 'new' ]]; then - export RCT_NEW_ARCH_ENABLED=1 - fi - pod install - cd ../.. - fastlane build_perf_test_app_plain - fi - env: - APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} - APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} - APP_STORE_CONNECT_KEY: ${{ secrets.APP_STORE_CONNECT_KEY }} - FASTLANE_KEYCHAIN_PASSWORD: ${{ secrets.FASTLANE_KEYCHAIN_PASSWORD }} - MATCH_GIT_PRIVATE_KEY: ${{ secrets.MATCH_GIT_PRIVATE_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }} - - - name: Build app with Sentry - working-directory: ./performance-tests/TestAppSentry - run: | - cd ${{ matrix.platform }} - if [[ "${{ matrix.platform }}" == "android" ]]; then - if [[ ${{ matrix.rn-architecture }} == 'new' ]]; then - perl -i -pe's/newArchEnabled=false/newArchEnabled=true/g' gradle.properties - ./gradlew generateCodegenArtifactsFromSchema - fi - ./gradlew assembleRelease - else - export PRODUCTION=1 - if [[ ${{ matrix.rn-architecture }} == 'new' ]]; then - export RCT_NEW_ARCH_ENABLED=1 - fi - pod install - cd ../.. - fastlane build_perf_test_app_sentry - cd TestAppSentry - fi - env: - APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} - APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} - APP_STORE_CONNECT_KEY: ${{ secrets.APP_STORE_CONNECT_KEY }} - FASTLANE_KEYCHAIN_PASSWORD: ${{ secrets.FASTLANE_KEYCHAIN_PASSWORD }} - MATCH_GIT_PRIVATE_KEY: ${{ secrets.MATCH_GIT_PRIVATE_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }} - - name: Collect apps metrics - uses: getsentry/action-app-sdk-overhead-metrics@5f2d99b8e5a7b833386524924d24320501099a44 - with: - name: ${{ matrix.name }} (${{ matrix.rn-architecture }}) - config: ./performance-tests/metrics-${{ matrix.platform }}.yml - sauce-user: ${{ secrets.SAUCE_USERNAME }} - sauce-key: ${{ secrets.SAUCE_ACCESS_KEY }} - + # TEMPORARILY DISABLED metrics job for replay debugging - skipping to save CI time + react-native-build: name: Build RN ${{ matrix.rn-version }} ${{ matrix.rn-architecture }} ${{ matrix.engine }} ${{ matrix.platform }} ${{ matrix.build-type }} ${{ matrix.ios-use-frameworks }} runs-on: ${{ matrix.runs-on }} @@ -179,52 +50,19 @@ jobs: strategy: fail-fast: false # keeps matrix running if one fails matrix: - rn-version: ['0.71.19', '0.83.0'] + # TEMPORARILY TESTING ONLY iOS Hermes 0.83.0 for replay debugging + rn-version: ['0.83.0'] rn-architecture: ['legacy', 'new'] - platform: ['android', 'ios'] + platform: ['ios'] build-type: ['production'] - ios-use-frameworks: ['no', 'static', 'dynamic'] - engine: ['hermes', 'jsc'] + ios-use-frameworks: ['no'] + engine: ['hermes'] include: - # Use Xcode 16 for older RN versions - - platform: ios - rn-version: '0.71.19' - xcode-version: '16.4' - runs-on: macos-15 # Use Xcode 26 for newer RN versions (0.83.0) - platform: ios rn-version: '0.83.0' xcode-version: '26.1.1' runs-on: macos-26 - - platform: android - runs-on: ubuntu-latest - exclude: - # exclude JSC for new RN versions (keeping the matrix manageable) - - rn-version: '0.83.0' - engine: 'jsc' - # exclude all rn versions lower than 0.80.0 for new architecture - - rn-version: '0.71.19' - rn-architecture: 'new' - # exlude old rn version for use frameworks builds (to minimalize the matrix) - - rn-version: '0.71.19' - platform: 'ios' - ios-use-frameworks: 'static' - - rn-version: '0.71.19' - platform: 'ios' - ios-use-frameworks: 'dynamic' - # use frameworks is ios only feature - - platform: 'android' - ios-use-frameworks: 'static' - - platform: 'android' - ios-use-frameworks: 'dynamic' - # exclude new rn architecture and dynamic frameworks - - rn-architecture: 'new' - ios-use-frameworks: 'dynamic' - # exclude RN 0.82.1 with dynamic frameworks due to React Native circular dependency bug - # https://github.com/facebook/react-native/issues/54267 - - rn-version: '0.82.1' - platform: 'ios' - ios-use-frameworks: 'dynamic' steps: - uses: actions/checkout@v6 @@ -314,9 +152,10 @@ jobs: strategy: fail-fast: false # keeps matrix running if one fails matrix: + # TEMPORARILY TESTING ONLY iOS Hermes 0.83.0 for replay debugging rn-version: ['0.83.0'] rn-architecture: ['legacy', 'new'] - platform: ['android', 'ios'] + platform: ['ios'] build-type: ['production'] ios-use-frameworks: ['no'] # test only no frameworks engine: ['hermes'] @@ -324,8 +163,6 @@ jobs: - platform: ios rn-version: '0.83.0' runs-on: macos-26 - - platform: android - runs-on: ubuntu-latest steps: - uses: actions/checkout@v6