From 35b2ff4fc6b6b4dad719bd9843b7df5bfe7ee9b3 Mon Sep 17 00:00:00 2001 From: Arif Kurkchi Date: Sat, 23 Aug 2025 12:32:09 +0300 Subject: [PATCH 1/2] fix(core/plugin): Ensure android plugin type safety --- packages/core/plugin/src/withSentryAndroid.ts | 14 +++---- .../src/withSentryAndroidGradlePlugin.ts | 40 +++++++------------ 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/packages/core/plugin/src/withSentryAndroid.ts b/packages/core/plugin/src/withSentryAndroid.ts index 9beaa23883..9743bb1d6e 100644 --- a/packages/core/plugin/src/withSentryAndroid.ts +++ b/packages/core/plugin/src/withSentryAndroid.ts @@ -5,19 +5,19 @@ import * as path from 'path'; import { warnOnce, writeSentryPropertiesTo } from './utils'; export const withSentryAndroid: ConfigPlugin = (config, sentryProperties: string) => { - const cfg = withAppBuildGradle(config, config => { - if (config.modResults.language === 'groovy') { - config.modResults.contents = modifyAppBuildGradle(config.modResults.contents); + const cfg = withAppBuildGradle(config, appBuildGradle => { + if (appBuildGradle.modResults.language === 'groovy') { + appBuildGradle.modResults.contents = modifyAppBuildGradle(appBuildGradle.modResults.contents); } else { throw new Error('Cannot configure Sentry in the app gradle because the build.gradle is not groovy'); } - return config; + return appBuildGradle; }); return withDangerousMod(cfg, [ 'android', - config => { - writeSentryPropertiesTo(path.resolve(config.modRequest.projectRoot, 'android'), sentryProperties); - return config; + dangerousMod => { + writeSentryPropertiesTo(path.resolve(dangerousMod.modRequest.projectRoot, 'android'), sentryProperties); + return dangerousMod; }, ]); }; diff --git a/packages/core/plugin/src/withSentryAndroidGradlePlugin.ts b/packages/core/plugin/src/withSentryAndroidGradlePlugin.ts index ee20842ca6..dd8fa719a3 100644 --- a/packages/core/plugin/src/withSentryAndroidGradlePlugin.ts +++ b/packages/core/plugin/src/withSentryAndroidGradlePlugin.ts @@ -1,4 +1,5 @@ import { withAppBuildGradle, withProjectBuildGradle } from '@expo/config-plugins'; +import type { ExpoConfig } from '@expo/config-types'; import { warnOnce } from './utils'; @@ -20,7 +21,7 @@ export const sentryAndroidGradlePluginVersion = '5.9.0'; * https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/#enable-sentry-agp */ export function withSentryAndroidGradlePlugin( - config: any, + config: ExpoConfig, { includeProguardMapping = true, dexguardEnabled = false, @@ -30,40 +31,34 @@ export function withSentryAndroidGradlePlugin( includeNativeSources = true, includeSourceContext = false, }: SentryAndroidGradlePluginOptions = {}, -): any { +): ExpoConfig { // Modify android/build.gradle - const withSentryProjectBuildGradle = (config: any): any => { - return withProjectBuildGradle(config, (projectBuildGradle: any) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const withSentryProjectBuildGradle = (config: ExpoConfig): ExpoConfig => { + return withProjectBuildGradle(config, projectBuildGradle => { if (!projectBuildGradle.modResults || !projectBuildGradle.modResults.contents) { warnOnce('android/build.gradle content is missing or undefined.'); - return config; + return projectBuildGradle; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (projectBuildGradle.modResults.language !== 'groovy') { warnOnce('Cannot configure Sentry in android/build.gradle because it is not in Groovy.'); - return config; + return projectBuildGradle; } const dependency = `classpath("io.sentry:sentry-android-gradle-plugin:${sentryAndroidGradlePluginVersion}")`; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (projectBuildGradle.modResults.contents.includes(dependency)) { warnOnce('sentry-android-gradle-plugin dependency in already in android/build.gradle.'); - return config; + return projectBuildGradle; } try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const updatedContents = projectBuildGradle.modResults.contents.replace( /dependencies\s*{/, `dependencies {\n ${dependency}`, ); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (updatedContents === projectBuildGradle.modResults.contents) { warnOnce('Failed to inject the dependency. Could not find `dependencies` in build.gradle.'); } else { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access projectBuildGradle.modResults.contents = updatedContents; } } catch (error) { @@ -74,12 +69,11 @@ export function withSentryAndroidGradlePlugin( }; // Modify android/app/build.gradle - const withSentryAppBuildGradle = (config: any): any => { - return withAppBuildGradle(config, (config: any) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (config.modResults.language !== 'groovy') { + const withSentryAppBuildGradle = (config: ExpoConfig): ExpoConfig => { + return withAppBuildGradle(config, appBuildGradle => { + if (appBuildGradle.modResults.language !== 'groovy') { warnOnce('Cannot configure Sentry in android/app/build.gradle because it is not in Groovy.'); - return config; + return appBuildGradle; } const sentryPlugin = `apply plugin: "io.sentry.android.gradle"`; const sentryConfig = ` @@ -99,22 +93,18 @@ export function withSentryAndroidGradlePlugin( } }`; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - let contents = config.modResults.contents; + let contents = appBuildGradle.modResults.contents; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!contents.includes(sentryPlugin)) { contents = `${sentryPlugin}\n${contents}`; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!contents.includes('sentry {')) { contents = `${contents}\n${sentryConfig}`; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - config.modResults.contents = contents; - return config; + appBuildGradle.modResults.contents = contents; + return appBuildGradle; }); }; From 86a89988c9b3e8b79ca1b6e1018d92912ddf75bb Mon Sep 17 00:00:00 2001 From: Arif Kurkchi Date: Sat, 23 Aug 2025 12:57:06 +0300 Subject: [PATCH 2/2] Add test --- .../withSentryAndroidGradlePlugin.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/core/test/expo-plugin/withSentryAndroidGradlePlugin.test.ts b/packages/core/test/expo-plugin/withSentryAndroidGradlePlugin.test.ts index 6b513afd41..28df575c78 100644 --- a/packages/core/test/expo-plugin/withSentryAndroidGradlePlugin.test.ts +++ b/packages/core/test/expo-plugin/withSentryAndroidGradlePlugin.test.ts @@ -75,12 +75,22 @@ describe('withSentryAndroidGradlePlugin', () => { const includedBuildGradle = `dependencies { classpath("io.sentry:sentry-android-gradle-plugin:${sentryAndroidGradlePluginVersion}")}`; const options: SentryAndroidGradlePluginOptions = { enableAndroidGradlePlugin: true }; + const projectBuildGradle = { + modResults: { language: 'groovy', contents: includedBuildGradle }, + }; + (withProjectBuildGradle as jest.Mock).mockImplementation((config, callback) => { - callback({ modResults: { language: 'groovy', contents: includedBuildGradle } }); + callback(projectBuildGradle); }); withSentryAndroidGradlePlugin(mockConfig, options); + const calledCallback = (withProjectBuildGradle as jest.Mock).mock.calls[0][1]; + const modifiedGradle = calledCallback(projectBuildGradle); + + expect(modifiedGradle).toEqual({ + modResults: { language: 'groovy', contents: includedBuildGradle }, + }); expect(warnOnce).toHaveBeenCalledWith( 'sentry-android-gradle-plugin dependency in already in android/build.gradle.', );