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;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth checking the warden checks, not sure why it didnt post as a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for catching this @lucas-zimerman. Updated with eaa6ae2

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