Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Do not mark `sendDefaultPii` as deprecated in the React Native options ([#6864](https://github.com/getsentry/sentry-react-native/pull/6864))
- Fix duplicate HTTP breadcrumbs for requests with a query string or differently formatted URLs ([#6881](https://github.com/getsentry/sentry-react-native/pull/6881))
- Include CPU and memory measurements in Android profiles ([#6891](https://github.com/getsentry/sentry-react-native/pull/6891))
- Load `ios/.xcode.env` and `ios/.xcode.env.local` in the Expo plugin "Upload Debug Symbols to Sentry" phase before it runs Node, so the phase finds Node when Xcode starts from the Dock ([#6861](https://github.com/getsentry/sentry-react-native/pull/6861))

### Dependencies

Expand Down
9 changes: 7 additions & 2 deletions packages/core/plugin/src/withSentryIOS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ type BuildPhase = { shellScript: string };
const SENTRY_REACT_NATIVE_XCODE_PATH =
"`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode.sh'\"`";
const SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH =
"`${NODE_BINARY:-node} --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode-debug-files.sh'\"`";
"`\"${NODE_BINARY:-node}\" --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode-debug-files.sh'\"`";
const SENTRY_DISABLE_AUTO_UPLOAD_EXPORT = 'export SENTRY_DISABLE_AUTO_UPLOAD=true';
// Xcode started from the Dock has no node on PATH, so load NODE_BINARY the same way as with-environment.sh.
const LOAD_XCODE_ENV = `XCODE_ENV_PATH="\${PODFILE_DIR:-$PODS_ROOT/..}/.xcode.env"
[ -f "$XCODE_ENV_PATH" ] && . "$XCODE_ENV_PATH"
[ -f "$XCODE_ENV_PATH.local" ] && . "$XCODE_ENV_PATH.local"
`;
Comment thread
cursor[bot] marked this conversation as resolved.

export const withSentryIOS: ConfigPlugin<{
sentryProperties: string;
Expand Down Expand Up @@ -114,7 +119,7 @@ export function getDebugFilesUploadScript(disableAutoUpload: boolean = false): s
// The resolved script path is wrapped in double quotes so a project path containing spaces is
// passed to `/bin/sh` as a single argument instead of being word-split. See issue #6583.
const disableAutoUploadExport = disableAutoUpload ? `${SENTRY_DISABLE_AUTO_UPLOAD_EXPORT}\n` : '';
return `${disableAutoUploadExport}/bin/sh "${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}"`;
return `${disableAutoUploadExport}${LOAD_XCODE_ENV}/bin/sh "${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}"`;
}

export function requoteExistingSentryBundleScript(script: BuildPhase): void {
Expand Down
76 changes: 74 additions & 2 deletions packages/core/test/expo-plugin/modifyXcodeProject.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { execFileSync } from 'child_process';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';

import { warnOnce } from '../../plugin/src/logger';
import {
addDisableAutoUploadToExistingScript,
Expand Down Expand Up @@ -470,7 +475,8 @@ describe('overwriteDebugFilesUploadScript: existing phase re-quoted on upgrade (
overwriteDebugFilesUploadScript(script, true);
const parsed = JSON.parse(script.shellScript);
expect(parsed).toBe(getDebugFilesUploadScript(true));
expect(parsed).toMatch(/^export SENTRY_DISABLE_AUTO_UPLOAD=true\n\/bin\/sh "`/);
expect(parsed).toMatch(/^export SENTRY_DISABLE_AUTO_UPLOAD=true\n/);
expect(parsed).toContain('/bin/sh "`');
});

it('drops a previously injected export when disableAutoUpload is toggled back to false', () => {
Expand Down Expand Up @@ -564,8 +570,74 @@ export NODE_BINARY=node

it('quotes the debug files upload script path with disableAutoUpload', () => {
const result = getDebugFilesUploadScript(true);
expect(result).toMatch(/^export SENTRY_DISABLE_AUTO_UPLOAD=true\n\/bin\/sh "`/);
expect(result).toMatch(/^export SENTRY_DISABLE_AUTO_UPLOAD=true\n/);
expect(result).toContain('/bin/sh "`');
expect(result).toContain('sentry-xcode-debug-files.sh');
expect(result).not.toMatch(/\/bin\/sh `/);
});
});

describe('Upload Debug Symbols phase loads .xcode.env before it resolves the script (issue #6859)', () => {
let projectDir: string;
let iosDir: string;

beforeEach(() => {
projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sentry-expo-phase-'));
iosDir = path.join(projectDir, 'ios');
const sentryDir = path.join(projectDir, 'node_modules', '@sentry', 'react-native');
fs.mkdirSync(path.join(sentryDir, 'scripts'), { recursive: true });
fs.mkdirSync(path.join(iosDir, 'Pods'), { recursive: true });
fs.writeFileSync(path.join(sentryDir, 'package.json'), '{"name":"@sentry/react-native"}');
fs.writeFileSync(
path.join(sentryDir, 'scripts', 'sentry-xcode-debug-files.sh'),
'echo "debug files script ran with SENTRY_DISABLE_AUTO_UPLOAD=$SENTRY_DISABLE_AUTO_UPLOAD"\n',
);
fs.writeFileSync(path.join(iosDir, '.xcode.env'), 'export NODE_BINARY=$(command -v node)\n');
});

afterEach(() => {
fs.rmSync(projectDir, { recursive: true, force: true });
});

const runPhase = (script: string, env: Record<string, string>): string =>
execFileSync('/bin/sh', ['-c', script], {
cwd: iosDir,
env: { HOME: os.homedir(), PATH: '/usr/bin:/bin:/usr/sbin:/sbin', ...env },
encoding: 'utf8',
stdio: 'pipe',
});

it('includes the .xcode.env loading before the script path is resolved', () => {
const result = getDebugFilesUploadScript();
expect(result.indexOf('.xcode.env')).toBeLessThan(result.indexOf('--print'));
expect(result).toContain('. "$XCODE_ENV_PATH.local"');
});

it('uses NODE_BINARY from .xcode.env.local when node is not on PATH', () => {
fs.writeFileSync(path.join(iosDir, '.xcode.env.local'), `export NODE_BINARY="${process.execPath}"\n`);

const stdout = runPhase(getDebugFilesUploadScript(true), { PODS_ROOT: path.join(iosDir, 'Pods') });

expect(stdout).toContain('debug files script ran with SENTRY_DISABLE_AUTO_UPLOAD=true');
});

it('uses PODFILE_DIR before PODS_ROOT to find .xcode.env', () => {
fs.writeFileSync(path.join(iosDir, '.xcode.env.local'), `export NODE_BINARY="${process.execPath}"\n`);

const stdout = runPhase(getDebugFilesUploadScript(), { PODFILE_DIR: iosDir, PODS_ROOT: '/nonexistent' });

expect(stdout).toContain('debug files script ran');
});

it('uses a NODE_BINARY path that contains spaces', () => {
const nodeDir = path.join(projectDir, 'dir with spaces');
fs.mkdirSync(nodeDir);
const nodePath = path.join(nodeDir, 'node');
fs.symlinkSync(process.execPath, nodePath);
fs.writeFileSync(path.join(iosDir, '.xcode.env.local'), `export NODE_BINARY="${nodePath}"\n`);

const stdout = runPhase(getDebugFilesUploadScript(), { PODFILE_DIR: iosDir });

expect(stdout).toContain('debug files script ran');
});
});
Loading