mirror of
https://github.com/flutter/packages.git
synced 2025-06-02 08:56:45 +08:00
[ci] Enforce a minimum Kotlin version in examples (#3979)
https://github.com/flutter/packages/pull/3973 caused an out-of-band failure after publishing, because an example that uses `url_launcher` had a too-old Kotlin version set. This is not something we consider client-breaking because `flutter` prodives a very clear error message with a straightforward and actionable fix step (update the app's Kotlin version), so the fix for the breakage is just to update our own examples. Since increasingly we're likely to hit problems where modern version of dependencies don't work with old version of Kotlin, this adds repo-wide CI enforcement that examples are set to a minimum version (matching the current `flutter/flutter` template; we can increase this over time as we feel it's useful to do so).
This commit is contained in:
@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<String> output =
|
||||
await runCapturingPrint(runner, <String>['gradle-check']);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
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<String> output =
|
||||
await runCapturingPrint(runner, <String>['gradle-check']);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
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<String> output =
|
||||
await runCapturingPrint(runner, <String>['gradle-check']);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
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<String> output = await runCapturingPrint(
|
||||
runner, <String>['gradle-check'], errorHandler: (Error e) {
|
||||
commandError = e;
|
||||
});
|
||||
|
||||
expect(commandError, isA<ToolExit>());
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
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.'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user