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/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..b77b0174b8 --- /dev/null +++ b/packages/core/test/scripts/sentry-cli-resolution.test.ts @@ -0,0 +1,35 @@ +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', () => { + 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); + }); + + 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 index 98e1057cfb..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,7 +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'); +const sentryCliBin = resolveSentryCliBin(); if (!sentryOrg || !sentryProject || !sentryUrl) { console.log('🐕 Fetching from expo config...');