From af065a6a1a72bd20c4a00d8ba41fefa868aec265 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 26 Jan 2023 10:28:41 -0800 Subject: [PATCH] [tool/ci] Add minimum supported SDK validation (#7028) Adds options to `pubspec.yaml` to check that the minimum supported SDK range for Flutter/Dart is at least a given version, to add CI enforcement that we're updating all of our support claims when we update our tested versions (rather than it being something we have to remember to do), and enables it in CI. As part of enabling it, fixes some violations: - path_provider_foundation had been temporarily dropped back to 2.10 as part of pushing out a regression fix. - a number of examples were missing Flutter constraints even though they used Flutter. - the non-Flutter `plugin_platform_interface` package hadn't been update since I hadn't thought about Dart-only constraints in the past. --- .cirrus.yml | 5 +- .../file_selector/example/pubspec.yaml | 1 + .../file_selector_ios/example/pubspec.yaml | 3 +- .../file_selector_linux/example/pubspec.yaml | 1 + .../example/pubspec.yaml | 1 + .../path_provider_foundation/CHANGELOG.md | 4 + .../path_provider_foundation/pubspec.yaml | 2 +- .../plugin_platform_interface/CHANGELOG.md | 4 + .../plugin_platform_interface/pubspec.yaml | 2 +- .../example/pubspec.yaml | 1 + .../webview_flutter_web/example/pubspec.yaml | 1 + .../example/pubspec.yaml | 1 + script/tool/CHANGELOG.md | 5 + .../tool/lib/src/pubspec_check_command.dart | 76 +++++++- script/tool/pubspec.yaml | 2 +- .../tool/test/pubspec_check_command_test.dart | 173 +++++++++++++++++- 16 files changed, 270 insertions(+), 12 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 85f8a2a115dd..4a5d604a081f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -101,7 +101,9 @@ task: always: format_script: ./script/tool_runner.sh format --fail-on-change license_script: $PLUGIN_TOOL_COMMAND license-check - pubspec_script: ./script/tool_runner.sh pubspec-check + # The major and minor versions here should match the lowest version + # analyzed in legacy_version_analyze. + pubspec_script: ./script/tool_runner.sh pubspec-check --min-min-flutter-version=3.0.0 --min-min-dart-version=2.17.0 readme_script: - ./script/tool_runner.sh readme-check # Re-run with --require-excerpts, skipping packages that still need @@ -171,6 +173,7 @@ task: - name: legacy_version_analyze depends_on: analyze matrix: + # Change the arguments to pubspec-check when changing these values. env: CHANNEL: "3.0.5" DART_VERSION: "2.17.6" diff --git a/packages/file_selector/file_selector/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml index 011d95874ae4..ff9d6d0d2e17 100644 --- a/packages/file_selector/file_selector/example/pubspec.yaml +++ b/packages/file_selector/file_selector/example/pubspec.yaml @@ -6,6 +6,7 @@ version: 1.0.0+1 environment: sdk: ">=2.12.0 <3.0.0" + flutter: ">=3.0.0" dependencies: file_selector: diff --git a/packages/file_selector/file_selector_ios/example/pubspec.yaml b/packages/file_selector/file_selector_ios/example/pubspec.yaml index 5a2eaa6f7dcd..175ec6c6e7d0 100644 --- a/packages/file_selector/file_selector_ios/example/pubspec.yaml +++ b/packages/file_selector/file_selector_ios/example/pubspec.yaml @@ -5,6 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.14.4 <3.0.0" + flutter: ">=3.0.0" dependencies: # The following adds the Cupertino Icons font to your application. @@ -28,4 +29,4 @@ dev_dependencies: sdk: flutter flutter: - uses-material-design: true \ No newline at end of file + uses-material-design: true diff --git a/packages/file_selector/file_selector_linux/example/pubspec.yaml b/packages/file_selector/file_selector_linux/example/pubspec.yaml index 912a082bd602..f90d1c88ef97 100644 --- a/packages/file_selector/file_selector_linux/example/pubspec.yaml +++ b/packages/file_selector/file_selector_linux/example/pubspec.yaml @@ -5,6 +5,7 @@ version: 1.0.0+1 environment: sdk: ">=2.12.0 <3.0.0" + flutter: ">=3.0.0" dependencies: file_selector_linux: diff --git a/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml index e732497eee95..4c97e6c44cd1 100644 --- a/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml @@ -4,6 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" + flutter: ">=3.0.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_foundation/CHANGELOG.md b/packages/path_provider/path_provider_foundation/CHANGELOG.md index 17e45d37a2ba..7adb04f4c984 100644 --- a/packages/path_provider/path_provider_foundation/CHANGELOG.md +++ b/packages/path_provider/path_provider_foundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum supported Flutter version to 3.0. + ## 2.1.1 * Fixes a regression in the path retured by `getApplicationSupportDirectory` on iOS. diff --git a/packages/path_provider/path_provider_foundation/pubspec.yaml b/packages/path_provider/path_provider_foundation/pubspec.yaml index 9ceb115ccfe1..30dd655acc00 100644 --- a/packages/path_provider/path_provider_foundation/pubspec.yaml +++ b/packages/path_provider/path_provider_foundation/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.10.0" + flutter: ">=3.0.0" flutter: plugin: diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index 0b5a6b63a52f..93e45c814668 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum supported Dart version. + ## 2.1.3 * Minor fixes for new analysis options. diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index 6a4bc488693b..25189d942f84 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -18,7 +18,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ version: 2.1.3 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.17.0 <3.0.0" dependencies: meta: ^1.3.0 diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml index 0daacb07b13f..0fc0daf84118 100644 --- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml @@ -4,6 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" + flutter: ">=3.0.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml index 782817eb7100..4685135acdf1 100644 --- a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml @@ -4,6 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" + flutter: ">=3.0.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 7ccb302a843b..718eb282018b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -4,6 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" + flutter: ">=3.0.0" dependencies: flutter: diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 55b5aeb7222a..34def6ecf676 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.13.4 + +* Adds the ability to validate minimum supported Dart/Flutter versions in + `pubspec-check`. + ## 0.13.3 * Renames `podspecs` to `podspec-check`. The old name will continue to work. diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 5682ba057688..aefa316a41f6 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -5,6 +5,7 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:platform/platform.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'package:yaml/yaml.dart'; import 'common/core.dart'; @@ -29,7 +30,23 @@ class PubspecCheckCommand extends PackageLoopingCommand { processRunner: processRunner, platform: platform, gitDir: gitDir, - ); + ) { + argParser.addOption( + _minMinDartVersionFlag, + help: + 'The minimum Dart version to allow as the minimum SDK constraint.\n\n' + 'This is only enforced for non-Flutter packages; Flutter packages ' + 'use --$_minMinFlutterVersionFlag', + ); + argParser.addOption( + _minMinFlutterVersionFlag, + help: + 'The minimum Flutter version to allow as the minimum SDK constraint.', + ); + } + + static const String _minMinDartVersionFlag = 'min-min-dart-version'; + static const String _minMinFlutterVersionFlag = 'min-min-flutter-version'; // Section order for plugins. Because the 'flutter' section is critical // information for plugins, and usually small, it goes near the top unlike in @@ -100,6 +117,24 @@ class PubspecCheckCommand extends PackageLoopingCommand { printError('$listIndentation${sectionOrder.join('\n$listIndentation')}'); } + final String minMinDartVersionString = getStringArg(_minMinDartVersionFlag); + final String minMinFlutterVersionString = + getStringArg(_minMinFlutterVersionFlag); + final String? minVersionError = _checkForMinimumVersionError( + pubspec, + package, + minMinDartVersion: minMinDartVersionString.isEmpty + ? null + : Version.parse(minMinDartVersionString), + minMinFlutterVersion: minMinFlutterVersionString.isEmpty + ? null + : Version.parse(minMinFlutterVersionString), + ); + if (minVersionError != null) { + printError('$indentation$minVersionError'); + passing = false; + } + if (isPlugin) { final String? implementsError = _checkForImplementsError(pubspec, package: package); @@ -320,4 +355,43 @@ class PubspecCheckCommand extends PackageLoopingCommand { final String suffix = packageName.substring(parentName.length); return !nonImplementationSuffixes.contains(suffix); } + + /// Validates that a Flutter package has a minimum SDK version constraint of + /// at least [minMinFlutterVersion] (if provided), or that a non-Flutter + /// package has a minimum SDK version constraint of [minMinDartVersion] + /// (if provided). + /// + /// Returns an error string if validation fails. + String? _checkForMinimumVersionError( + Pubspec pubspec, + RepositoryPackage package, { + Version? minMinDartVersion, + Version? minMinFlutterVersion, + }) { + final VersionConstraint? dartConstraint = pubspec.environment?['sdk']; + final VersionConstraint? flutterConstraint = + pubspec.environment?['flutter']; + + if (flutterConstraint != null) { + // Validate Flutter packages against the Flutter requirement. + if (minMinFlutterVersion != null) { + final Version? constraintMin = + flutterConstraint is VersionRange ? flutterConstraint.min : null; + if ((constraintMin ?? Version(0, 0, 0)) < minMinFlutterVersion) { + return 'Minimum allowed Flutter version $constraintMin is less than $minMinFlutterVersion'; + } + } + } else { + // Validate non-Flutter packages against the Dart requirement. + if (minMinDartVersion != null) { + final Version? constraintMin = + dartConstraint is VersionRange ? dartConstraint.min : null; + if ((constraintMin ?? Version(0, 0, 0)) < minMinDartVersion) { + return 'Minimum allowed Dart version $constraintMin is less than $minMinDartVersion'; + } + } + } + + return null; + } } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index abf2a61f4cf0..a8df2a9cd23a 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.13.3 +version: 0.13.4 dependencies: args: ^2.1.0 diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 2c254ca94984..7a9c0cec7cbc 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -60,12 +60,16 @@ ${publishable ? '' : "publish_to: 'none'"} '''; } -String _environmentSection() { - return ''' -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" -'''; +String _environmentSection({ + String dartConstraint = '>=2.12.0 <3.0.0', + String? flutterConstraint = '>=2.0.0', +}) { + return [ + 'environment:', + ' sdk: "$dartConstraint"', + if (flutterConstraint != null) ' flutter: "$flutterConstraint"', + '', + ].join('\n'); } String _flutterSection({ @@ -931,6 +935,163 @@ ${_devDependenciesSection()} ]), ); }); + + test('fails when a Flutter package has a too-low minimum Flutter version', + () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + isFlutter: true, examples: []); + + package.pubspecFile.writeAsStringSync(''' +${_headerSection('a_package')} +${_environmentSection(flutterConstraint: '>=2.10.0')} +${_dependenciesSection()} +'''); + + Error? commandError; + final List output = await runCapturingPrint(runner, [ + 'pubspec-check', + '--min-min-flutter-version', + '3.0.0' + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Minimum allowed Flutter version 2.10.0 is less than 3.0.0'), + ]), + ); + }); + + test( + 'passes when a Flutter package requires exactly the minimum Flutter version', + () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + isFlutter: true, examples: []); + + package.pubspecFile.writeAsStringSync(''' +${_headerSection('a_package')} +${_environmentSection(flutterConstraint: '>=3.0.0')} +${_dependenciesSection()} +'''); + + final List output = await runCapturingPrint(runner, + ['pubspec-check', '--min-min-flutter-version', '3.0.0']); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test( + 'passes when a Flutter package requires a higher minimum Flutter version', + () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + isFlutter: true, examples: []); + + package.pubspecFile.writeAsStringSync(''' +${_headerSection('a_package')} +${_environmentSection(flutterConstraint: '>=3.3.0')} +${_dependenciesSection()} +'''); + + final List output = await runCapturingPrint(runner, + ['pubspec-check', '--min-min-flutter-version', '3.0.0']); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test('fails when a non-Flutter package has a too-low minimum Dart version', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, examples: []); + + package.pubspecFile.writeAsStringSync(''' +${_headerSection('a_package')} +${_environmentSection(dartConstraint: '>=2.14.0 <3.0.0', flutterConstraint: null)} +${_dependenciesSection()} +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['pubspec-check', '--min-min-dart-version', '2.17.0'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Minimum allowed Dart version 2.14.0 is less than 2.17.0'), + ]), + ); + }); + + test( + 'passes when a non-Flutter package requires exactly the minimum Dart version', + () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + isFlutter: true, examples: []); + + package.pubspecFile.writeAsStringSync(''' +${_headerSection('a_package')} +${_environmentSection(dartConstraint: '>=2.17.0 <3.0.0', flutterConstraint: null)} +${_dependenciesSection()} +'''); + + final List output = await runCapturingPrint(runner, + ['pubspec-check', '--min-min-dart-version', '2.17.0']); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test( + 'passes when a non-Flutter package requires a higher minimum Dart version', + () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + isFlutter: true, examples: []); + + package.pubspecFile.writeAsStringSync(''' +${_headerSection('a_package')} +${_environmentSection(dartConstraint: '>=2.18.0 <3.0.0', flutterConstraint: null)} +${_dependenciesSection()} +'''); + + final List output = await runCapturingPrint(runner, + ['pubspec-check', '--min-min-dart-version', '2.17.0']); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); }); group('test pubspec_check_command on Windows', () {