Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

## 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))

### Dependencies

- Bump JavaScript SDK from v10.39.0 to v10.40.0 ([#5715](https://github.com/getsentry/sentry-react-native/pull/5715))
Expand Down
7 changes: 7 additions & 0 deletions packages/core/scripts/sentry-xcode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
77 changes: 77 additions & 0 deletions packages/core/test/scripts/sentry-xcode-scripts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
});
});
});
Loading