From 03daa62021d48e250eb011e3e02307e1637d1a70 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Thu, 26 Feb 2026 16:43:20 +0100 Subject: [PATCH 1/4] fix(ios): resolve relative SOURCEMAP_FILE against project root in Xcode build script When users set SOURCEMAP_FILE to a relative path (e.g. `SOURCEMAP_FILE=maps/main.jsbundle.map`), the value is now resolved against the project root (one level up from `ios/`) rather than against the `ios/` directory where the Xcode build script runs. Without this fix, sentry-cli could not find the source map because the bundle is created relative to the project root. Fixes #3889 Co-Authored-By: Claude Sonnet 4.6 --- packages/core/scripts/sentry-xcode.sh | 7 ++ .../test/scripts/sentry-xcode-scripts.test.ts | 77 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/packages/core/scripts/sentry-xcode.sh b/packages/core/scripts/sentry-xcode.sh index 2ab36fc1b1..973f358b39 100755 --- a/packages/core/scripts/sentry-xcode.sh +++ b/packages/core/scripts/sentry-xcode.sh @@ -15,6 +15,13 @@ RN_PROJECT_ROOT="${PROJECT_DIR}/.." [ -z "$SENTRY_PROPERTIES" ] && export SENTRY_PROPERTIES=sentry.properties [ -z "$SENTRY_DOTENV_PATH" ] && [ -f "$RN_PROJECT_ROOT/.env.sentry-build-plugin" ] && export SENTRY_DOTENV_PATH="$RN_PROJECT_ROOT/.env.sentry-build-plugin" [ -z "$SOURCEMAP_FILE" ] && export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map" +# Resolve relative SOURCEMAP_FILE to absolute. The script runs from `ios/` (Xcode's PWD), +# but users typically specify paths relative to the project root. Without this, sentry-cli +# would resolve relative paths against `ios/` and fail to find the file. +# See: https://github.com/getsentry/sentry-react-native/issues/3889 +if [[ "$SOURCEMAP_FILE" != /* ]]; then + export SOURCEMAP_FILE="$(cd "$RN_PROJECT_ROOT" && pwd)/$SOURCEMAP_FILE" +fi if [ -z "$SENTRY_CLI_EXECUTABLE" ]; then # Try standard resolution safely diff --git a/packages/core/test/scripts/sentry-xcode-scripts.test.ts b/packages/core/test/scripts/sentry-xcode-scripts.test.ts index aac5d69566..7e9252ad05 100644 --- a/packages/core/test/scripts/sentry-xcode-scripts.test.ts +++ b/packages/core/test/scripts/sentry-xcode-scripts.test.ts @@ -454,4 +454,81 @@ describe('sentry-xcode.sh', () => { expect(result.stdout).toContain('SENTRY_DISABLE_AUTO_UPLOAD=true'); expect(result.stdout).toContain('skipping sourcemaps upload'); }); + + describe('SOURCEMAP_FILE path resolution', () => { + // Returns a mock sentry-cli that prints the SOURCEMAP_FILE env var it received. + const makeSourcmapEchoScript = (dir: string): string => { + const scriptPath = path.join(dir, 'mock-sentry-cli-echo-sourcemap.js'); + fs.writeFileSync( + scriptPath, + ` + const sourcemapFile = process.env.SOURCEMAP_FILE || 'not-set'; + console.log('SOURCEMAP_FILE=' + sourcemapFile); + process.exit(0); + `, + ); + return scriptPath; + }; + + it('leaves an absolute SOURCEMAP_FILE unchanged', () => { + const absolutePath = path.join(tempDir, 'absolute', 'main.jsbundle.map'); + const echoScript = makeSourcmapEchoScript(tempDir); + + const result = runScript({ + SENTRY_CLI_EXECUTABLE: echoScript, + SOURCEMAP_FILE: absolutePath, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain(`SOURCEMAP_FILE=${absolutePath}`); + }); + + it('resolves a relative SOURCEMAP_FILE against the project root, not ios/', () => { + // PROJECT_DIR is tempDir (simulates the ios/ folder). + // RN_PROJECT_ROOT = PROJECT_DIR/.. = parent of tempDir. + // A user setting SOURCEMAP_FILE=relative/path.map expects it relative to the project root. + const echoScript = makeSourcmapEchoScript(tempDir); + + const result = runScript({ + SENTRY_CLI_EXECUTABLE: echoScript, + SOURCEMAP_FILE: 'relative/path.map', + }); + + const projectRoot = path.dirname(tempDir); // PROJECT_DIR/.. = RN_PROJECT_ROOT + const expectedPath = path.join(projectRoot, 'relative/path.map'); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain(`SOURCEMAP_FILE=${expectedPath}`); + }); + + it('resolves a ./prefixed SOURCEMAP_FILE against the project root', () => { + const echoScript = makeSourcmapEchoScript(tempDir); + + const result = runScript({ + SENTRY_CLI_EXECUTABLE: echoScript, + SOURCEMAP_FILE: './maps/main.jsbundle.map', + }); + + // The script concatenates: "$(cd RN_PROJECT_ROOT && pwd)/./maps/main.jsbundle.map" + // The ./ is preserved but the path is absolute and valid for sentry-cli. + const projectRoot = path.dirname(tempDir); + const expectedPath = `${projectRoot}/./maps/main.jsbundle.map`; + + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain(`SOURCEMAP_FILE=${expectedPath}`); + }); + + it('uses the absolute default SOURCEMAP_FILE when not set by the user', () => { + const echoScript = makeSourcmapEchoScript(tempDir); + + const result = runScript({ + SENTRY_CLI_EXECUTABLE: echoScript, + // SOURCEMAP_FILE intentionally not set — script should default to $DERIVED_FILE_DIR/main.jsbundle.map + DERIVED_FILE_DIR: tempDir, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain(`SOURCEMAP_FILE=${tempDir}/main.jsbundle.map`); + }); + }); }); From f79190d71df8d1e47f7138f143ea1f4ba2c6daa4 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Thu, 26 Feb 2026 16:55:54 +0100 Subject: [PATCH 2/4] changelog: resolve relative SOURCEMAP_FILE in Xcode build script --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77fd7a2dbf..478b9ed917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ > make sure you follow our [migration guide](https://docs.sentry.io/platforms/react-native/migration/) first. +## Unreleased + +### Fixes + +- Resolve relative `SOURCEMAP_FILE` paths against the project root in the Xcode build script ([#5730](https://github.com/getsentry/sentry-react-native/pull/5730)) + ## 8.2.0 ### Fixes From 6c838132c40a5d052a1763cd11fea9feb17f4bce Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 27 Feb 2026 11:32:40 +0100 Subject: [PATCH 3/4] Fix typo --- .../core/test/scripts/sentry-xcode-scripts.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/test/scripts/sentry-xcode-scripts.test.ts b/packages/core/test/scripts/sentry-xcode-scripts.test.ts index 7e9252ad05..69b160bdb9 100644 --- a/packages/core/test/scripts/sentry-xcode-scripts.test.ts +++ b/packages/core/test/scripts/sentry-xcode-scripts.test.ts @@ -457,7 +457,7 @@ describe('sentry-xcode.sh', () => { describe('SOURCEMAP_FILE path resolution', () => { // Returns a mock sentry-cli that prints the SOURCEMAP_FILE env var it received. - const makeSourcmapEchoScript = (dir: string): string => { + const makeSourcemapEchoScript = (dir: string): string => { const scriptPath = path.join(dir, 'mock-sentry-cli-echo-sourcemap.js'); fs.writeFileSync( scriptPath, @@ -472,7 +472,7 @@ describe('sentry-xcode.sh', () => { it('leaves an absolute SOURCEMAP_FILE unchanged', () => { const absolutePath = path.join(tempDir, 'absolute', 'main.jsbundle.map'); - const echoScript = makeSourcmapEchoScript(tempDir); + const echoScript = makeSourcemapEchoScript(tempDir); const result = runScript({ SENTRY_CLI_EXECUTABLE: echoScript, @@ -487,7 +487,7 @@ describe('sentry-xcode.sh', () => { // PROJECT_DIR is tempDir (simulates the ios/ folder). // RN_PROJECT_ROOT = PROJECT_DIR/.. = parent of tempDir. // A user setting SOURCEMAP_FILE=relative/path.map expects it relative to the project root. - const echoScript = makeSourcmapEchoScript(tempDir); + const echoScript = makeSourcemapEchoScript(tempDir); const result = runScript({ SENTRY_CLI_EXECUTABLE: echoScript, @@ -502,7 +502,7 @@ describe('sentry-xcode.sh', () => { }); it('resolves a ./prefixed SOURCEMAP_FILE against the project root', () => { - const echoScript = makeSourcmapEchoScript(tempDir); + const echoScript = makeSourcemapEchoScript(tempDir); const result = runScript({ SENTRY_CLI_EXECUTABLE: echoScript, @@ -519,7 +519,7 @@ describe('sentry-xcode.sh', () => { }); it('uses the absolute default SOURCEMAP_FILE when not set by the user', () => { - const echoScript = makeSourcmapEchoScript(tempDir); + const echoScript = makeSourcemapEchoScript(tempDir); const result = runScript({ SENTRY_CLI_EXECUTABLE: echoScript, From 187fc5de9e7909eba58fcd4b2d93abc6acbd00ca Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 27 Feb 2026 11:53:18 +0100 Subject: [PATCH 4/4] fix(ios): normalize ./ prefix when resolving relative SOURCEMAP_FILE Strip leading ./ via parameter expansion before joining with the project root, so ./maps/main.jsbundle.map resolves to a clean absolute path instead of /project/root/./maps/main.jsbundle.map. Co-Authored-By: Claude Sonnet 4.6 --- packages/core/scripts/sentry-xcode.sh | 2 +- packages/core/test/scripts/sentry-xcode-scripts.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/scripts/sentry-xcode.sh b/packages/core/scripts/sentry-xcode.sh index 973f358b39..b73ffbca35 100755 --- a/packages/core/scripts/sentry-xcode.sh +++ b/packages/core/scripts/sentry-xcode.sh @@ -20,7 +20,7 @@ RN_PROJECT_ROOT="${PROJECT_DIR}/.." # would resolve relative paths against `ios/` and fail to find the file. # See: https://github.com/getsentry/sentry-react-native/issues/3889 if [[ "$SOURCEMAP_FILE" != /* ]]; then - export SOURCEMAP_FILE="$(cd "$RN_PROJECT_ROOT" && pwd)/$SOURCEMAP_FILE" + export SOURCEMAP_FILE="$(cd "$RN_PROJECT_ROOT" && pwd)/${SOURCEMAP_FILE#./}" fi if [ -z "$SENTRY_CLI_EXECUTABLE" ]; then diff --git a/packages/core/test/scripts/sentry-xcode-scripts.test.ts b/packages/core/test/scripts/sentry-xcode-scripts.test.ts index 69b160bdb9..b9154fd752 100644 --- a/packages/core/test/scripts/sentry-xcode-scripts.test.ts +++ b/packages/core/test/scripts/sentry-xcode-scripts.test.ts @@ -509,10 +509,10 @@ describe('sentry-xcode.sh', () => { SOURCEMAP_FILE: './maps/main.jsbundle.map', }); - // The script concatenates: "$(cd RN_PROJECT_ROOT && pwd)/./maps/main.jsbundle.map" - // The ./ is preserved but the path is absolute and valid for sentry-cli. + // The leading ./ is stripped via ${SOURCEMAP_FILE#./} before concatenation, + // so the result is a clean absolute path without any ./ component. const projectRoot = path.dirname(tempDir); - const expectedPath = `${projectRoot}/./maps/main.jsbundle.map`; + const expectedPath = path.join(projectRoot, 'maps/main.jsbundle.map'); expect(result.exitCode).toBe(0); expect(result.stdout).toContain(`SOURCEMAP_FILE=${expectedPath}`);