Skip to content
Open
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Features

- Add `disableAutoUpload` option to Expo plugin to disable source map and debug symbol uploads ([#6195](https://github.com/getsentry/sentry-react-native/pull/6195))
- Expose `pauseAppHangTracking` and `resumeAppHangTracking` APIs on iOS ([#6192](https://github.com/getsentry/sentry-react-native/pull/6192))

## 8.12.0
Expand Down
13 changes: 11 additions & 2 deletions packages/core/plugin/src/withSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface PluginProps {
authToken?: string;
url?: string;
useNativeInit?: boolean;
disableAutoUpload?: boolean;
Comment thread
lucas-zimerman marked this conversation as resolved.
options?: Record<string, unknown>;
experimental_android?: SentryAndroidGradlePluginOptions;
}
Expand Down Expand Up @@ -67,7 +68,11 @@ const withSentryPlugin: ConfigPlugin<PluginProps | void> = (config, props) => {
}
if (sentryProperties !== null) {
try {
cfg = withSentryAndroid(cfg, { sentryProperties, useNativeInit: props?.useNativeInit });
cfg = withSentryAndroid(cfg, {
sentryProperties,
useNativeInit: props?.useNativeInit,
disableAutoUpload: props?.disableAutoUpload,
});
} catch (e) {
warnOnce(`There was a problem with configuring your native Android project: ${e}`);
}
Expand All @@ -80,7 +85,11 @@ const withSentryPlugin: ConfigPlugin<PluginProps | void> = (config, props) => {
}
}
try {
cfg = withSentryIOS(cfg, { sentryProperties, useNativeInit: props?.useNativeInit });
cfg = withSentryIOS(cfg, {
sentryProperties,
useNativeInit: props?.useNativeInit,
disableAutoUpload: props?.disableAutoUpload,
});
} catch (e) {
warnOnce(`There was a problem with configuring your native iOS project: ${e}`);
}
Expand Down
27 changes: 20 additions & 7 deletions packages/core/plugin/src/withSentryAndroid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import * as path from 'path';
import { warnOnce } from './logger';
import { writeSentryPropertiesTo } from './utils';

export const withSentryAndroid: ConfigPlugin<{ sentryProperties: string; useNativeInit: boolean | undefined }> = (
config,
{ sentryProperties, useNativeInit = false },
) => {
export const withSentryAndroid: ConfigPlugin<{
sentryProperties: string;
useNativeInit: boolean | undefined;
disableAutoUpload: boolean | undefined;
}> = (config, { sentryProperties, useNativeInit = false, disableAutoUpload = false }) => {
const appBuildGradleCfg = withAppBuildGradle(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents, disableAutoUpload);
} else {
throw new Error('Cannot configure Sentry in the app gradle because the build.gradle is not groovy');
}
Expand All @@ -39,8 +40,17 @@ const resolveSentryReactNativePackageJsonPath =
* Writes to projectDirectory/android/app/build.gradle,
* adding the relevant @sentry/react-native script.
*/
export function modifyAppBuildGradle(buildGradle: string): string {
export function modifyAppBuildGradle(buildGradle: string, disableAutoUpload: boolean = false): string {
if (buildGradle.includes('sentry.gradle')) {
if (disableAutoUpload && !buildGradle.includes('shouldSentryAutoUploadGeneral')) {
Comment thread
sentry-warden[bot] marked this conversation as resolved.
return buildGradle.replace(
/^(apply from:.*sentry\.gradle.*)$/m,
`$1\nproject.ext.shouldSentryAutoUploadGeneral = { -> return false }`,
);
}
if (!disableAutoUpload && buildGradle.includes('shouldSentryAutoUploadGeneral')) {
return buildGradle.replace(/\nproject\.ext\.shouldSentryAutoUploadGeneral = \{ -> return false \}\n?/, '\n');
}
return buildGradle;
Comment thread
antonis marked this conversation as resolved.
}

Expand All @@ -56,8 +66,11 @@ export function modifyAppBuildGradle(buildGradle: string): string {
}

const applyFrom = `apply from: new File(${resolveSentryReactNativePackageJsonPath}, "sentry.gradle")`;
const disableUploadOverride = disableAutoUpload
? `\nproject.ext.shouldSentryAutoUploadGeneral = { -> return false }`
: '';

return buildGradle.replace(pattern, match => `${applyFrom}\n\n${match}`);
return buildGradle.replace(pattern, match => `${applyFrom}${disableUploadOverride}\n\n${match}`);
}

export function modifyMainApplication(config: ExpoConfig): ExpoConfig {
Expand Down
71 changes: 60 additions & 11 deletions packages/core/plugin/src/withSentryIOS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ 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'\"`";
const SENTRY_DISABLE_AUTO_UPLOAD_EXPORT = 'export SENTRY_DISABLE_AUTO_UPLOAD=true';

export const withSentryIOS: ConfigPlugin<{ sentryProperties: string; useNativeInit: boolean | undefined }> = (
config,
{ sentryProperties, useNativeInit = false },
) => {
export const withSentryIOS: ConfigPlugin<{
sentryProperties: string;
useNativeInit: boolean | undefined;
disableAutoUpload: boolean | undefined;
}> = (config, { sentryProperties, useNativeInit = false, disableAutoUpload = false }) => {
const xcodeProjectCfg = withXcodeProject(config, config => {
const xcodeProject: XcodeProject = config.modResults;

Expand All @@ -27,17 +29,24 @@ export const withSentryIOS: ConfigPlugin<{ sentryProperties: string; useNativeIn
'PBXShellScriptBuildPhase',
);
if (!sentryBuildPhase) {
const debugFilesScript = disableAutoUpload
? `${SENTRY_DISABLE_AUTO_UPLOAD_EXPORT}\n/bin/sh ${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}`
: `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}`;
xcodeProject.addBuildPhase([], 'PBXShellScriptBuildPhase', 'Upload Debug Symbols to Sentry', null, {
shellPath: '/bin/sh',
shellScript: `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}`,
shellScript: debugFilesScript,
});
} else if (disableAutoUpload) {
addDisableAutoUploadToExistingScript(sentryBuildPhase);
} else {
removeDisableAutoUploadFromExistingScript(sentryBuildPhase);
}

const bundleReactNativePhase = xcodeProject.pbxItemByComment(
'Bundle React Native code and images',
'PBXShellScriptBuildPhase',
);
modifyExistingXcodeBuildScript(bundleReactNativePhase);
modifyExistingXcodeBuildScript(bundleReactNativePhase, disableAutoUpload);

return config;
});
Expand All @@ -53,7 +62,7 @@ export const withSentryIOS: ConfigPlugin<{ sentryProperties: string; useNativeIn
]);
};

export function modifyExistingXcodeBuildScript(script: BuildPhase): void {
export function modifyExistingXcodeBuildScript(script: BuildPhase, disableAutoUpload: boolean = false): void {
if (!script.shellScript.match(/(packager|scripts)\/react-native-xcode\.sh\b/)) {
warnOnce(
`'react-native-xcode.sh' not found in 'Bundle React Native code and images'.
Expand All @@ -63,7 +72,11 @@ Please open a bug report at https://github.com/getsentry/sentry-react-native`,
}

if (script.shellScript.includes('sentry-xcode.sh')) {
warnOnce("The latest 'sentry-xcode.sh' script already exists in 'Bundle React Native code and images'.");
if (disableAutoUpload) {
addDisableAutoUploadToExistingScript(script);
} else {
removeDisableAutoUploadFromExistingScript(script);
}
return;
}

Expand All @@ -77,16 +90,52 @@ Run npx expo prebuild --clean`,
}

const code = JSON.parse(script.shellScript);
script.shellScript = JSON.stringify(addSentryWithBundledScriptsToBundleShellScript(code));
script.shellScript = JSON.stringify(addSentryWithBundledScriptsToBundleShellScript(code, disableAutoUpload));
}

export function addSentryWithBundledScriptsToBundleShellScript(script: string): string {
export function addSentryWithBundledScriptsToBundleShellScript(
script: string,
disableAutoUpload: boolean = false,
): string {
const disableAutoUploadExport = disableAutoUpload ? `${SENTRY_DISABLE_AUTO_UPLOAD_EXPORT}\n` : '';
return script.replace(
/^.*?(packager|scripts)\/react-native-xcode\.sh\s*(\\'\\\\")?/m,
(match: string) => `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_PATH} ${match}`,
(match: string) => `${disableAutoUploadExport}/bin/sh ${SENTRY_REACT_NATIVE_XCODE_PATH} ${match}`,
);
}

export function addDisableAutoUploadToExistingScript(script: BuildPhase): void {
if (script.shellScript.includes('SENTRY_DISABLE_AUTO_UPLOAD')) {
return;
}
try {
const code = JSON.parse(script.shellScript);
script.shellScript = JSON.stringify(insertExportAfterDelimiter(code));
} catch {
script.shellScript = `${SENTRY_DISABLE_AUTO_UPLOAD_EXPORT}\n${script.shellScript}`;
}
}
Comment thread
cursor[bot] marked this conversation as resolved.

function insertExportAfterDelimiter(script: string): string {
if (script.startsWith('"')) {
const rest = script.slice(1).replace(/^\n/, '');
return `"\n${SENTRY_DISABLE_AUTO_UPLOAD_EXPORT}\n${rest}`;
}
return `${SENTRY_DISABLE_AUTO_UPLOAD_EXPORT}\n${script}`;
}

export function removeDisableAutoUploadFromExistingScript(script: BuildPhase): void {
if (!script.shellScript.includes('SENTRY_DISABLE_AUTO_UPLOAD')) {
return;
}
try {
const code = JSON.parse(script.shellScript);
script.shellScript = JSON.stringify(code.replace(/^export SENTRY_DISABLE_AUTO_UPLOAD=true\n?/m, ''));
} catch {
script.shellScript = script.shellScript.replace(/^export SENTRY_DISABLE_AUTO_UPLOAD=true\n?/m, '');
}
}

export function modifyAppDelegate(config: ExpoConfig): ExpoConfig {
return withAppDelegate(config, async config => {
if (!config.modResults?.path) {
Expand Down
47 changes: 47 additions & 0 deletions packages/core/test/expo-plugin/modifyAppBuildGradle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,51 @@ describe('Configures Android native project correctly', () => {
modifyAppBuildGradle(buildGradleWithOutReactGradleScript);
expect(warnOnce).toHaveBeenCalled();
});

it('Adds shouldSentryAutoUploadGeneral override when disableAutoUpload is true', () => {
const result = modifyAppBuildGradle(buildGradleWithOutSentry, true);
expect(result).toContain('project.ext.shouldSentryAutoUploadGeneral = { -> return false }');
expect(result).toContain('sentry.gradle');
});

it('Does not add shouldSentryAutoUploadGeneral override when disableAutoUpload is false', () => {
const result = modifyAppBuildGradle(buildGradleWithOutSentry, false);
expect(result).not.toContain('shouldSentryAutoUploadGeneral');
});

it('Adds override to already-configured build.gradle on re-prebuild', () => {
const result = modifyAppBuildGradle(buildGradleWithSentry, true);
expect(result).toContain('project.ext.shouldSentryAutoUploadGeneral = { -> return false }');
});

it('Does not duplicate override if already present', () => {
const gradleWithOverride = `
apply from: new File(["node", "--print", "require('path').dirname(require.resolve('@sentry/react-native/package.json'))"].execute().text.trim(), "sentry.gradle")
project.ext.shouldSentryAutoUploadGeneral = { -> return false }

android {
}
`;
const result = modifyAppBuildGradle(gradleWithOverride, true);
expect(result).toBe(gradleWithOverride);
});

it('Removes override when toggling disableAutoUpload back to false', () => {
const gradleWithOverride = `
apply from: new File(["node", "--print", "require('path').dirname(require.resolve('@sentry/react-native/package.json'))"].execute().text.trim(), "sentry.gradle")
project.ext.shouldSentryAutoUploadGeneral = { -> return false }

android {
}
`;
const result = modifyAppBuildGradle(gradleWithOverride, false);
expect(result).not.toContain('shouldSentryAutoUploadGeneral');
expect(result).toContain('sentry.gradle');
expect(result).toContain('android {');
});

it('No-ops when toggling to false and override is not present', () => {
const result = modifyAppBuildGradle(buildGradleWithSentry, false);
expect(result).toBe(buildGradleWithSentry);
});
});
Loading
Loading