diff --git a/packages/react-native-cli/src/lib/Gradle.ts b/packages/react-native-cli/src/lib/Gradle.ts index ea68a170ac..daddbe8b16 100644 --- a/packages/react-native-cli/src/lib/Gradle.ts +++ b/packages/react-native-cli/src/lib/Gradle.ts @@ -1,6 +1,8 @@ import { Logger } from '../Logger' import { promises as fs } from 'fs' import path from 'path' +import { detectInstalledVersion } from './Npm' +import semver from 'semver' const GRADLE_PLUGIN_IMPORT = (version: string) => `classpath("com.bugsnag:bugsnag-android-gradle-plugin:${version}")` const GRADLE_PLUGIN_IMPORT_REGEX = /classpath\(["']com\.bugsnag:bugsnag-android-gradle-plugin:.*["']\)/ @@ -12,6 +14,9 @@ const BUGSNAG_CONFIGURATION_BLOCK = 'bugsnag {\n}\n' const BUGSNAG_CONFIGURATION_BLOCK_REGEX = /^\s*bugsnag {[^}]*?}/m const UPLOAD_ENDPOINT_REGEX = /^\s*bugsnag {[^}]*endpoint[^}]*?}/m const BUILD_ENDPOINT_REGEX = /^\s*bugsnag {[^}]*releasesEndpoint[^}]*?}/m +const GRADLE_VERSION_FAIL_MSG = `Cannot determine an appropriate version of the Bugsnag Android Gradle plugin for use in this project. + +Please see ${DOCS_LINK} for information on Gradle and the Android Gradle Plugin (AGP) compatibility` export async function getSuggestedBugsnagGradleVersion (projectRoot: string, logger: Logger): Promise { let fileContents: string @@ -30,14 +35,24 @@ export async function getSuggestedBugsnagGradleVersion (projectRoot: string, log } else if (major === 7) { return '7.+' } else { - const versionMatchResult = fileContents.match(/classpath\(["']com.android.tools.build:gradle["']\)/) - if (versionMatchResult) { + // if the AGP version isn't set explicitly in the build.gradle file, + // try to suggest a version based on the detected react-native version + const noVersionMatchResult = fileContents.match(/classpath\(["']com.android.tools.build:gradle["']\)/) + let reactNativeVersion + try { + reactNativeVersion = await detectInstalledVersion('react-native', projectRoot) + } catch (e) {} + + if (!noVersionMatchResult || !reactNativeVersion) { + logger.warn(GRADLE_VERSION_FAIL_MSG) + return '' + } + + // RN 0.73+ requires AGP 8.+ + if (semver.lt(reactNativeVersion, '0.73.0')) { return '7.+' } else { - logger.warn(`Cannot determine an appropriate version of the Bugsnag Android Gradle plugin for use in this project. - -Please see ${DOCS_LINK} for information on Gradle and the Android Gradle Plugin (AGP) compatibility`) - return '' + return '8.+' } } } diff --git a/packages/react-native-cli/src/lib/__test__/Gradle.test.ts b/packages/react-native-cli/src/lib/__test__/Gradle.test.ts index e6092f17b7..b024323340 100644 --- a/packages/react-native-cli/src/lib/__test__/Gradle.test.ts +++ b/packages/react-native-cli/src/lib/__test__/Gradle.test.ts @@ -512,3 +512,31 @@ test('getSuggestedBugsnagGradleVersion(): success with bracketed AGP version', a const version = await getSuggestedBugsnagGradleVersion('/random/path', logger) expect(version).toBe('7.+') }) + +test('getSuggestedBugsnagGradleVersion(): success with unspecified AGP version', async () => { + const buildGradle = await loadFixture(path.join(__dirname, 'fixtures', 'root-build-before-without-agp-version.gradle')) + + const readFileMock = fs.readFile as jest.MockedFunction + readFileMock.mockResolvedValueOnce(buildGradle) + readFileMock.mockResolvedValueOnce('{"dependencies": { "react-native": "0.73.0"} }') + + let version = await getSuggestedBugsnagGradleVersion('/random/path', logger) + expect(version).toBe('8.+') + + readFileMock.mockResolvedValueOnce(buildGradle) + readFileMock.mockResolvedValueOnce('{"dependencies": { "react-native": "0.72.11"} }') + + version = await getSuggestedBugsnagGradleVersion('/random/path', logger) + expect(version).toBe('7.+') +}) + +test('getSuggestedBugsnagGradleVersion(): null with unspecified AGP and react native versions', async () => { + const buildGradle = await loadFixture(path.join(__dirname, 'fixtures', 'root-build-before-without-agp-version.gradle')) + + const readFileMock = fs.readFile as jest.MockedFunction + readFileMock.mockResolvedValueOnce(buildGradle) + readFileMock.mockResolvedValueOnce('') + + const version = await getSuggestedBugsnagGradleVersion('/random/path', logger) + expect(version).toBe('') +}) diff --git a/packages/react-native-cli/src/lib/__test__/fixtures/root-build-before-without-agp-version.gradle b/packages/react-native-cli/src/lib/__test__/fixtures/root-build-before-without-agp-version.gradle new file mode 100644 index 0000000000..842db470dc --- /dev/null +++ b/packages/react-native-cli/src/lib/__test__/fixtures/root-build-before-without-agp-version.gradle @@ -0,0 +1,37 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext { + buildToolsVersion = "29.0.2" + minSdkVersion = 16 + compileSdkVersion = 29 + targetSdkVersion = 29 + } + repositories { + google() + jcenter() + } + dependencies { + classpath("com.android.tools.build:gradle") + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + mavenLocal() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url("$rootDir/../node_modules/react-native/android") + } + maven { + // Android JSC is installed from npm + url("$rootDir/../node_modules/jsc-android/dist") + } + + google() + jcenter() + maven { url 'https://www.jitpack.io' } + } +}