diff --git a/packages/animations/example/android/build.gradle b/packages/animations/example/android/build.gradle index 4b30292ebe1..ce647a433bd 100644 --- a/packages/animations/example/android/build.gradle +++ b/packages/animations/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/dynamic_layouts/example/android/build.gradle b/packages/dynamic_layouts/example/android/build.gradle index 4b30292ebe1..ce647a433bd 100644 --- a/packages/dynamic_layouts/example/android/build.gradle +++ b/packages/dynamic_layouts/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/flutter_markdown/example/android/build.gradle b/packages/flutter_markdown/example/android/build.gradle index 4b30292ebe1..ce647a433bd 100644 --- a/packages/flutter_markdown/example/android/build.gradle +++ b/packages/flutter_markdown/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/go_router/example/android/build.gradle b/packages/go_router/example/android/build.gradle index 96e940b06ea..586557f0afc 100644 --- a/packages/go_router/example/android/build.gradle +++ b/packages/go_router/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/rfw/example/hello/android/build.gradle b/packages/rfw/example/hello/android/build.gradle index ff27ef813e9..586557f0afc 100644 --- a/packages/rfw/example/hello/android/build.gradle +++ b/packages/rfw/example/hello/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.0' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/rfw/example/local/android/build.gradle b/packages/rfw/example/local/android/build.gradle index ff27ef813e9..586557f0afc 100644 --- a/packages/rfw/example/local/android/build.gradle +++ b/packages/rfw/example/local/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.0' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/rfw/example/remote/android/build.gradle b/packages/rfw/example/remote/android/build.gradle index ff27ef813e9..586557f0afc 100644 --- a/packages/rfw/example/remote/android/build.gradle +++ b/packages/rfw/example/remote/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.0' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/shared_preferences/shared_preferences/example/android/build.gradle b/packages/shared_preferences/shared_preferences/example/android/build.gradle index 4b30292ebe1..ce647a433bd 100644 --- a/packages/shared_preferences/shared_preferences/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle index c11ef06c4e3..f6f6475fb39 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/script/tool/lib/src/gradle_check_command.dart b/script/tool/lib/src/gradle_check_command.dart index 09a9d7a55f2..53da6405beb 100644 --- a/script/tool/lib/src/gradle_check_command.dart +++ b/script/tool/lib/src/gradle_check_command.dart @@ -3,12 +3,18 @@ // found in the LICENSE file. import 'package:file/file.dart'; +import 'package:meta/meta.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'common/core.dart'; import 'common/package_looping_command.dart'; import 'common/plugin_utils.dart'; import 'common/repository_package.dart'; +/// The lowest `ext.kotlin_version` that example apps are allowed to use. +@visibleForTesting +final Version minKotlinVersion = Version(1, 7, 10); + /// A command to enforce gradle file conventions and best practices. class GradleCheckCommand extends PackageLoopingCommand { /// Creates an instance of the gradle check command. @@ -125,6 +131,9 @@ class GradleCheckCommand extends PackageLoopingCommand { if (!_validateJavacLintConfig(package, lines)) { succeeded = false; } + if (!_validateKotlinVersion(package, lines)) { + succeeded = false; + } return succeeded; } @@ -347,4 +356,26 @@ gradle.projectsEvaluated { } return true; } + + /// Validates whether the given [example] has its Kotlin version set to at + /// least a minimum value, if it is set at all. + bool _validateKotlinVersion( + RepositoryPackage example, List gradleLines) { + final RegExp kotlinVersionRegex = + RegExp(r"ext\.kotlin_version\s*=\s*'([\d.]+)'"); + RegExpMatch? match; + if (gradleLines.any((String line) { + match = kotlinVersionRegex.firstMatch(line); + return match != null; + })) { + final Version version = Version.parse(match!.group(1)!); + if (version < minKotlinVersion) { + printError('build.gradle sets "ext.kotlin_version" to "$version". The ' + 'minimum Kotlin version that can be specified is ' + '$minKotlinVersion, for compatibility with modern dependencies.'); + return false; + } + } + return true; + } } diff --git a/script/tool/test/gradle_check_command_test.dart b/script/tool/test/gradle_check_command_test.dart index 68581c7a639..28603d25cd1 100644 --- a/script/tool/test/gradle_check_command_test.dart +++ b/script/tool/test/gradle_check_command_test.dart @@ -123,6 +123,7 @@ dependencies { RepositoryPackage package, { required String pluginName, required bool warningsConfigured, + String? kotlinVersion, }) { final File buildGradle = package .platformDirectory(FlutterPlatform.android) @@ -140,6 +141,7 @@ gradle.projectsEvaluated { '''; buildGradle.writeAsStringSync(''' buildscript { + ${kotlinVersion == null ? '' : "ext.kotlin_version = '$kotlinVersion'"} repositories { google() mavenCentral() @@ -228,9 +230,12 @@ dependencies { bool includeNamespace = true, bool commentNamespace = false, bool warningsConfigured = true, + String? kotlinVersion, }) { writeFakeExampleTopLevelBuildGradle(package, - pluginName: pluginName, warningsConfigured: warningsConfigured); + pluginName: pluginName, + warningsConfigured: warningsConfigured, + kotlinVersion: kotlinVersion); writeFakeExampleAppBuildGradle(package, includeNamespace: includeNamespace, commentNamespace: commentNamespace); } @@ -644,4 +649,99 @@ dependencies { ], )); }); + + group('Kotlin version check', () { + test('passes if not set', () async { + const String packageName = 'a_package'; + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeManifest(package); + final RepositoryPackage example = package.getExamples().first; + writeFakeExampleBuildGradles(example, pluginName: packageName); + writeFakeManifest(example, isApp: true); + + final List output = + await runCapturingPrint(runner, ['gradle-check']); + + expect( + output, + containsAllInOrder([ + contains('Validating android/build.gradle'), + ]), + ); + }); + + test('passes if at the minimum allowed version', () async { + const String packageName = 'a_package'; + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeManifest(package); + final RepositoryPackage example = package.getExamples().first; + writeFakeExampleBuildGradles(example, + pluginName: packageName, kotlinVersion: minKotlinVersion.toString()); + writeFakeManifest(example, isApp: true); + + final List output = + await runCapturingPrint(runner, ['gradle-check']); + + expect( + output, + containsAllInOrder([ + contains('Validating android/build.gradle'), + ]), + ); + }); + + test('passes if above the minimum allowed version', () async { + const String packageName = 'a_package'; + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeManifest(package); + final RepositoryPackage example = package.getExamples().first; + writeFakeExampleBuildGradles(example, + pluginName: packageName, kotlinVersion: '99.99.0'); + writeFakeManifest(example, isApp: true); + + final List output = + await runCapturingPrint(runner, ['gradle-check']); + + expect( + output, + containsAllInOrder([ + contains('Validating android/build.gradle'), + ]), + ); + }); + + test('fails if below the minimum allowed version', () async { + const String packageName = 'a_package'; + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeManifest(package); + final RepositoryPackage example = package.getExamples().first; + writeFakeExampleBuildGradles(example, + pluginName: packageName, kotlinVersion: '1.6.21'); + writeFakeManifest(example, isApp: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['gradle-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('build.gradle sets "ext.kotlin_version" to "1.6.21". The ' + 'minimum Kotlin version that can be specified is ' + '$minKotlinVersion, for compatibility with modern dependencies.'), + ]), + ); + }); + }); }