From 0cdfb2e65fddae65705203cb0b1f094104874191 Mon Sep 17 00:00:00 2001 From: Shawn Thye Date: Mon, 1 Jun 2026 23:28:57 +0800 Subject: [PATCH 1/4] fix: resolve sentry cli from react native package --- .../core/scripts/sentry-xcode-debug-files.sh | 5 ++-- packages/core/scripts/sentry-xcode.sh | 5 ++-- packages/core/sentry.gradle.kts | 8 +++++- .../scripts/sentry-cli-resolution.test.ts | 25 +++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 packages/core/test/scripts/sentry-cli-resolution.test.ts diff --git a/packages/core/scripts/sentry-xcode-debug-files.sh b/packages/core/scripts/sentry-xcode-debug-files.sh index fe65325aa6..d05f2012c6 100755 --- a/packages/core/scripts/sentry-xcode-debug-files.sh +++ b/packages/core/scripts/sentry-xcode-debug-files.sh @@ -32,9 +32,10 @@ RN_PROJECT_ROOT="${SENTRY_PROJECT_ROOT:-${PROJECT_DIR}/..}" [ -z "$SOURCEMAP_FILE" ] && export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map" if [ -z "$SENTRY_CLI_EXECUTABLE" ]; then - # Try standard resolution safely + # Resolve from @sentry/react-native so package managers with isolated dependencies + # can find the transitive @sentry/cli dependency. RESOLVED_PATH=$( - "$LOCAL_NODE_BINARY" --print "require('path').dirname(require.resolve('@sentry/cli/package.json'))" 2>/dev/null + "$LOCAL_NODE_BINARY" --print "require('path').dirname(require.resolve('@sentry/cli/package.json', { paths: [require.resolve('@sentry/react-native/package.json')] }))" 2>/dev/null ) || true if [ -n "$RESOLVED_PATH" ]; then SENTRY_CLI_PACKAGE_PATH="$RESOLVED_PATH/bin/sentry-cli" diff --git a/packages/core/scripts/sentry-xcode.sh b/packages/core/scripts/sentry-xcode.sh index 229ac90bb2..aa4ead6225 100755 --- a/packages/core/scripts/sentry-xcode.sh +++ b/packages/core/scripts/sentry-xcode.sh @@ -27,9 +27,10 @@ if [[ "$SOURCEMAP_FILE" != /* ]]; then fi if [ -z "$SENTRY_CLI_EXECUTABLE" ]; then - # Try standard resolution safely + # Resolve from @sentry/react-native so package managers with isolated dependencies + # can find the transitive @sentry/cli dependency. RESOLVED_PATH=$( - "$LOCAL_NODE_BINARY" --print "require('path').dirname(require.resolve('@sentry/cli/package.json'))" 2>/dev/null + "$LOCAL_NODE_BINARY" --print "require('path').dirname(require.resolve('@sentry/cli/package.json', { paths: [require.resolve('@sentry/react-native/package.json')] }))" 2>/dev/null ) || true if [ -n "$RESOLVED_PATH" ]; then SENTRY_CLI_PACKAGE_PATH="$RESOLVED_PATH/bin/sentry-cli" diff --git a/packages/core/sentry.gradle.kts b/packages/core/sentry.gradle.kts index 9c813c7cfb..51163f97a5 100644 --- a/packages/core/sentry.gradle.kts +++ b/packages/core/sentry.gradle.kts @@ -167,7 +167,13 @@ fun resolveSentryCliPackagePath(reactRoot: File): String { var resolvedCliPath: File? = null try { val process = - ProcessBuilder(listOf("node", "--print", "require.resolve('@sentry/cli/package.json')")) + ProcessBuilder( + listOf( + "node", + "--print", + "require.resolve('@sentry/cli/package.json', { paths: [require.resolve('@sentry/react-native/package.json')] })", + ), + ) .directory(rootDir) .start() val output = diff --git a/packages/core/test/scripts/sentry-cli-resolution.test.ts b/packages/core/test/scripts/sentry-cli-resolution.test.ts new file mode 100644 index 0000000000..53a3ad57f3 --- /dev/null +++ b/packages/core/test/scripts/sentry-cli-resolution.test.ts @@ -0,0 +1,25 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +const CORE_DIR = path.resolve(__dirname, '../..'); +const RELATIVE_SENTRY_CLI_RESOLVER = + "require.resolve('@sentry/cli/package.json', { paths: [require.resolve('@sentry/react-native/package.json')] })"; + +describe('sentry-cli resolution', () => { + it('resolves @sentry/cli relative to @sentry/react-native on Android', () => { + const gradleScript = fs.readFileSync(path.join(CORE_DIR, 'sentry.gradle.kts'), 'utf8'); + + expect(gradleScript).toContain(RELATIVE_SENTRY_CLI_RESOLVER); + }); + + it('resolves @sentry/cli relative to @sentry/react-native on iOS', () => { + const xcodeScript = fs.readFileSync(path.join(CORE_DIR, 'scripts', 'sentry-xcode.sh'), 'utf8'); + const xcodeDebugFilesScript = fs.readFileSync( + path.join(CORE_DIR, 'scripts', 'sentry-xcode-debug-files.sh'), + 'utf8', + ); + + expect(xcodeScript).toContain(RELATIVE_SENTRY_CLI_RESOLVER); + expect(xcodeDebugFilesScript).toContain(RELATIVE_SENTRY_CLI_RESOLVER); + }); +}); From 92d3ec57ec03d5d12123a3895ff338fb0972172f Mon Sep 17 00:00:00 2001 From: Shawn Thye Date: Tue, 2 Jun 2026 01:59:57 +0800 Subject: [PATCH 2/4] fix: resolve sentry-cli for expo sourcemap uploader --- .../core/test/scripts/sentry-cli-resolution.test.ts | 13 +++++++++++++ packages/expo-upload-sourcemaps/cli.js | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) mode change 100755 => 100644 packages/expo-upload-sourcemaps/cli.js diff --git a/packages/core/test/scripts/sentry-cli-resolution.test.ts b/packages/core/test/scripts/sentry-cli-resolution.test.ts index 53a3ad57f3..00e4ed09c9 100644 --- a/packages/core/test/scripts/sentry-cli-resolution.test.ts +++ b/packages/core/test/scripts/sentry-cli-resolution.test.ts @@ -2,8 +2,11 @@ import * as fs from 'fs'; import * as path from 'path'; const CORE_DIR = path.resolve(__dirname, '../..'); +const EXPO_UPLOAD_SOURCEMAPS_DIR = path.resolve(CORE_DIR, '../expo-upload-sourcemaps'); const RELATIVE_SENTRY_CLI_RESOLVER = "require.resolve('@sentry/cli/package.json', { paths: [require.resolve('@sentry/react-native/package.json')] })"; +const RELATIVE_EXPO_UPLOAD_SOURCEMAPS_CLI_RESOLVER_PATH = + "paths: [require.resolve('@sentry/expo-upload-sourcemaps/package.json')]"; describe('sentry-cli resolution', () => { it('resolves @sentry/cli relative to @sentry/react-native on Android', () => { @@ -22,4 +25,14 @@ describe('sentry-cli resolution', () => { expect(xcodeScript).toContain(RELATIVE_SENTRY_CLI_RESOLVER); expect(xcodeDebugFilesScript).toContain(RELATIVE_SENTRY_CLI_RESOLVER); }); + + it('resolves @sentry/cli relative to @sentry/expo-upload-sourcemaps in the Expo uploader', () => { + const expoUploadSourcemapsScript = fs.readFileSync( + path.join(EXPO_UPLOAD_SOURCEMAPS_DIR, 'cli.js'), + 'utf8', + ); + + expect(expoUploadSourcemapsScript).toContain("require.resolve('@sentry/cli/bin/sentry-cli'"); + expect(expoUploadSourcemapsScript).toContain(RELATIVE_EXPO_UPLOAD_SOURCEMAPS_CLI_RESOLVER_PATH); + }); }); diff --git a/packages/expo-upload-sourcemaps/cli.js b/packages/expo-upload-sourcemaps/cli.js old mode 100755 new mode 100644 index 98e1057cfb..58b9fbe64f --- a/packages/expo-upload-sourcemaps/cli.js +++ b/packages/expo-upload-sourcemaps/cli.js @@ -187,7 +187,11 @@ let sentryOrg = getEnvVar(SENTRY_ORG); let sentryUrl = getEnvVar(SENTRY_URL); let sentryProject = getEnvVar(SENTRY_PROJECT); let authToken = getEnvVar(SENTRY_AUTH_TOKEN); -const sentryCliBin = getEnvVar(SENTRY_CLI_EXECUTABLE) || require.resolve('@sentry/cli/bin/sentry-cli'); +const sentryCliBin = + getEnvVar(SENTRY_CLI_EXECUTABLE) || + require.resolve('@sentry/cli/bin/sentry-cli', { + paths: [require.resolve('@sentry/expo-upload-sourcemaps/package.json')], + }); if (!sentryOrg || !sentryProject || !sentryUrl) { console.log('🐕 Fetching from expo config...'); From dceaac29438d8c7c11fb1ab1a477dd9c4ea04205 Mon Sep 17 00:00:00 2001 From: Shawn Thye Date: Wed, 3 Jun 2026 22:19:54 +0800 Subject: [PATCH 3/4] chore: address sentry cli review comments --- packages/core/test/scripts/sentry-cli-resolution.test.ts | 5 +---- packages/expo-upload-sourcemaps/cli.js | 0 2 files changed, 1 insertion(+), 4 deletions(-) mode change 100644 => 100755 packages/expo-upload-sourcemaps/cli.js diff --git a/packages/core/test/scripts/sentry-cli-resolution.test.ts b/packages/core/test/scripts/sentry-cli-resolution.test.ts index 00e4ed09c9..b77b0174b8 100644 --- a/packages/core/test/scripts/sentry-cli-resolution.test.ts +++ b/packages/core/test/scripts/sentry-cli-resolution.test.ts @@ -27,10 +27,7 @@ describe('sentry-cli resolution', () => { }); it('resolves @sentry/cli relative to @sentry/expo-upload-sourcemaps in the Expo uploader', () => { - const expoUploadSourcemapsScript = fs.readFileSync( - path.join(EXPO_UPLOAD_SOURCEMAPS_DIR, 'cli.js'), - 'utf8', - ); + const expoUploadSourcemapsScript = fs.readFileSync(path.join(EXPO_UPLOAD_SOURCEMAPS_DIR, 'cli.js'), 'utf8'); expect(expoUploadSourcemapsScript).toContain("require.resolve('@sentry/cli/bin/sentry-cli'"); expect(expoUploadSourcemapsScript).toContain(RELATIVE_EXPO_UPLOAD_SOURCEMAPS_CLI_RESOLVER_PATH); diff --git a/packages/expo-upload-sourcemaps/cli.js b/packages/expo-upload-sourcemaps/cli.js old mode 100644 new mode 100755 From 7faa992878b839b0f6cb51ca3322c853f0899b6b Mon Sep 17 00:00:00 2001 From: Shawn Thye Date: Wed, 3 Jun 2026 23:04:12 +0800 Subject: [PATCH 4/4] fix: handle expo uploader cli resolution failures --- CHANGELOG.md | 4 ++++ packages/expo-upload-sourcemaps/cli.js | 31 +++++++++++++++++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f890cc62..91608d7717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ - Add `enableAutoConsoleLogs` option to opt out of automatic `console.*` capture while keeping `enableLogs: true` for manual `Sentry.logger.*` calls ([#6235](https://github.com/getsentry/sentry-react-native/pull/6235)) +### Fixes + +- Resolve `sentry-cli` in isolated dependency layouts ([#6242](https://github.com/getsentry/sentry-react-native/pull/6242)) + ### Internal - Convert `sentry.gradle` to Kotlin DSL (`sentry.gradle.kts`) ([#6119](https://github.com/getsentry/sentry-react-native/pull/6119)) diff --git a/packages/expo-upload-sourcemaps/cli.js b/packages/expo-upload-sourcemaps/cli.js index 58b9fbe64f..2a2e6afbd4 100755 --- a/packages/expo-upload-sourcemaps/cli.js +++ b/packages/expo-upload-sourcemaps/cli.js @@ -14,6 +14,31 @@ function getEnvVar(varname) { return process.env[varname]; } +function resolveSentryCliBin() { + const configuredExecutable = getEnvVar(SENTRY_CLI_EXECUTABLE); + if (configuredExecutable) { + return configuredExecutable; + } + + let dependencyRelativeError; + try { + return require.resolve('@sentry/cli/bin/sentry-cli', { + paths: [require.resolve('@sentry/expo-upload-sourcemaps/package.json')], + }); + } catch (error) { + dependencyRelativeError = error; + } + + try { + return require.resolve('@sentry/cli/bin/sentry-cli'); + } catch (error) { + console.error(`Could not resolve sentry-cli. Set ${SENTRY_CLI_EXECUTABLE} to the sentry-cli executable path.`); + console.error('Dependency-relative resolution failed:', dependencyRelativeError); + console.error('Standard resolution failed:', error); + process.exit(1); + } +} + function getSentryPluginPropertiesFromExpoConfig() { try { let expoPkgJson; @@ -187,11 +212,7 @@ let sentryOrg = getEnvVar(SENTRY_ORG); let sentryUrl = getEnvVar(SENTRY_URL); let sentryProject = getEnvVar(SENTRY_PROJECT); let authToken = getEnvVar(SENTRY_AUTH_TOKEN); -const sentryCliBin = - getEnvVar(SENTRY_CLI_EXECUTABLE) || - require.resolve('@sentry/cli/bin/sentry-cli', { - paths: [require.resolve('@sentry/expo-upload-sourcemaps/package.json')], - }); +const sentryCliBin = resolveSentryCliBin(); if (!sentryOrg || !sentryProject || !sentryUrl) { console.log('🐕 Fetching from expo config...');