[ci] Run analysis with older versions of Flutter (#5000)
diff --git a/.cirrus.yml b/.cirrus.yml
index f9454cb..8f6a6f7 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -28,10 +28,10 @@
- git fetch origin
# Switch to the requested channel.
- git checkout $TARGET_TREEISH
- # When using a branch rather than a hash, reset to the upstream branch
- # rather than using pull, since the base image can sometimes be in a state
- # where it has diverged from upstream (!).
- - if [[ "$TARGET_TREEISH" == "$CHANNEL" ]]; then
+ # When using a branch rather than a hash or version tag, reset to the
+ # upstream branch rather than using pull, since the base image can sometimes
+ # be in a state where it has diverged from upstream (!).
+ - if [[ "$TARGET_TREEISH" == "$CHANNEL" ]] && [[ "$CHANNEL" != *"."* ]]; then
- git reset --hard @{u}
- fi
# Run doctor to allow auditing of what version of Flutter the run is using.
@@ -138,6 +138,22 @@
# Restore the tree to a clean state, to avoid accidental issues if
# other script steps are added to this task.
- git checkout .
+ # Does a sanity check that plugins at least pass analysis on the N-1 and N-2
+ # versions of Flutter stable if the plugin claims to support that version.
+ # This is to minimize accidentally making changes that break old versions
+ # (which we don't commit to supporting, but don't want to actively break)
+ # without updating the constraints.
+ # Note: The versions below should be manually updated after a new stable
+ # version comes out.
+ - name: legacy-version-analyze
+ depends_on: analyze
+ env:
+ matrix:
+ CHANNEL: "2.5.3"
+ CHANNEL: "2.8.1"
+ analyze_script:
+ - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml
+ - echo "If this test fails, the minumum Flutter version should be updated"
### Web tasks ###
- name: web-build_all_plugins
env:
diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md
index c72e31f..9e48666 100644
--- a/packages/camera/camera_web/CHANGELOG.md
+++ b/packages/camera/camera_web/CHANGELOG.md
@@ -1,3 +1,7 @@
+## NEXT
+
+* Updates minimum Flutter version for changes in 0.2.1+3.
+
## 0.2.1+3
* Internal code cleanup for stricter analysis options.
diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml
index 6d6f110..cb6aa19 100644
--- a/packages/camera/camera_web/pubspec.yaml
+++ b/packages/camera/camera_web/pubspec.yaml
@@ -6,7 +6,7 @@
environment:
sdk: ">=2.12.0 <3.0.0"
- flutter: ">=2.0.0"
+ flutter: ">=2.8.0"
flutter:
plugin:
diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md
index c1a1606..e35caf7 100644
--- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md
+++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md
@@ -1,3 +1,7 @@
+## NEXT
+
+* Updates minimum Flutter version to 2.8.
+
## 0.10.0+5
* Internal code cleanup for stricter analysis options.
diff --git a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml
index 5a51cd5..1bdb2f0 100644
--- a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml
@@ -3,7 +3,7 @@
environment:
sdk: ">=2.12.0 <3.0.0"
- flutter: ">=2.2.0"
+ flutter: ">=2.8.0"
dependencies:
flutter:
diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml
index 3bc05d1..d97a7c4 100644
--- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml
@@ -7,7 +7,7 @@
environment:
sdk: ">=2.12.0 <3.0.0"
- flutter: ">=2.0.0"
+ flutter: ">=2.8.0"
flutter:
plugin:
diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md
index fe59608..e95d56c 100644
--- a/packages/quick_actions/quick_actions/CHANGELOG.md
+++ b/packages/quick_actions/quick_actions/CHANGELOG.md
@@ -1,3 +1,7 @@
+## NEXT
+
+* Updates minimum Flutter version to 2.8.
+
## 0.6.0+10
* Moves Android and iOS implementations to federated packages.
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index a44f0e3..5d56b15 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,7 @@
+## NEXT
+
+* Updates minimum Flutter version to 2.10.
+
## 2.3.0
* Adds `allowBackgroundPlayback` to `VideoPlayerOptions`.
diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml
index 6032f3c..e6b51f9 100644
--- a/packages/video_player/video_player/example/pubspec.yaml
+++ b/packages/video_player/video_player/example/pubspec.yaml
@@ -4,7 +4,7 @@
environment:
sdk: ">=2.12.0 <3.0.0"
- flutter: ">=1.12.13+hotfix.5"
+ flutter: ">=2.10.0"
dependencies:
flutter:
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index d58de12..88e45c5 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -7,7 +7,7 @@
environment:
sdk: ">=2.14.0 <3.0.0"
- flutter: ">=2.8.0"
+ flutter: ">=2.10.0"
flutter:
plugin:
diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md
index 8f2807f..8e5ae21 100644
--- a/script/tool/CHANGELOG.md
+++ b/script/tool/CHANGELOG.md
@@ -19,6 +19,9 @@
`flutter` behavior.
- Validates `default_package` entries in plugins.
- Removes `allow-warnings` from the `podspecs` command.
+- Adds `skip-if-not-supporting-flutter-version` to allow running tests using a
+ version of Flutter that not all packages support. (E.g., to allow for running
+ some tests against old versions of Flutter to help avoid accidental breakage.)
## 0.7.3
diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart
index faad7f4..d8b17bf 100644
--- a/script/tool/lib/src/analyze_command.dart
+++ b/script/tool/lib/src/analyze_command.dart
@@ -10,12 +10,9 @@
import 'common/core.dart';
import 'common/package_looping_command.dart';
-import 'common/plugin_command.dart';
import 'common/process_runner.dart';
import 'common/repository_package.dart';
-const int _exitPackagesGetFailed = 3;
-
/// A command to run Dart analysis on packages.
class AnalyzeCommand extends PackageLoopingCommand {
/// Creates a analysis command instance.
@@ -84,41 +81,8 @@
return false;
}
- /// Ensures that the dependent packages have been fetched for all packages
- /// (including their sub-packages) that will be analyzed.
- Future<bool> _runPackagesGetOnTargetPackages() async {
- final List<Directory> packageDirectories =
- await getTargetPackagesAndSubpackages()
- .map((PackageEnumerationEntry entry) => entry.package.directory)
- .toList();
- final Set<String> packagePaths =
- packageDirectories.map((Directory dir) => dir.path).toSet();
- packageDirectories.removeWhere((Directory directory) {
- // Remove the 'example' subdirectories; 'flutter packages get'
- // automatically runs 'pub get' there as part of handling the parent
- // directory.
- return directory.basename == 'example' &&
- packagePaths.contains(directory.parent.path);
- });
- for (final Directory package in packageDirectories) {
- final int exitCode = await processRunner.runAndStream(
- flutterCommand, <String>['packages', 'get'],
- workingDir: package);
- if (exitCode != 0) {
- return false;
- }
- }
- return true;
- }
-
@override
Future<void> initializeRun() async {
- print('Fetching dependencies...');
- if (!await _runPackagesGetOnTargetPackages()) {
- printError('Unable to get dependencies.');
- throw ToolExit(_exitPackagesGetFailed);
- }
-
_allowedCustomAnalysisDirectories =
getStringListArg(_customAnalysisFlag).expand<String>((String item) {
if (item.endsWith('.yaml')) {
@@ -138,6 +102,19 @@
@override
Future<PackageResult> runForPackage(RepositoryPackage package) async {
+ // For non-example packages, fetch dependencies. 'flutter packages get'
+ // automatically runs 'pub get' in examples as part of handling the parent
+ // directory, which is guaranteed to come first in the package enumeration.
+ if (package.directory.basename != 'example' ||
+ !RepositoryPackage(package.directory.parent).pubspecFile.existsSync()) {
+ final int exitCode = await processRunner.runAndStream(
+ flutterCommand, <String>['packages', 'get'],
+ workingDir: package.directory);
+ if (exitCode != 0) {
+ return PackageResult.fail(<String>['Unable to get dependencies']);
+ }
+ }
+
if (_hasUnexpecetdAnalysisOptions(package)) {
return PackageResult.fail(<String>['Unexpected local analysis options']);
}
diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart
index bfee71a..b75aaa4 100644
--- a/script/tool/lib/src/common/package_looping_command.dart
+++ b/script/tool/lib/src/common/package_looping_command.dart
@@ -9,6 +9,8 @@
import 'package:git/git.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
+import 'package:pub_semver/pub_semver.dart';
+import 'package:pubspec_parse/pubspec_parse.dart';
import 'core.dart';
import 'plugin_command.dart';
@@ -75,7 +77,16 @@
Platform platform = const LocalPlatform(),
GitDir? gitDir,
}) : super(packagesDir,
- processRunner: processRunner, platform: platform, gitDir: gitDir);
+ processRunner: processRunner, platform: platform, gitDir: gitDir) {
+ argParser.addOption(
+ _skipByFlutterVersionArg,
+ help: 'Skip any packages that require a Flutter version newer than '
+ 'the provided version.',
+ );
+ }
+
+ static const String _skipByFlutterVersionArg =
+ 'skip-if-not-supporting-flutter-version';
/// Packages that had at least one [logWarning] call.
final Set<PackageEnumerationEntry> _packagesWithWarnings =
@@ -219,6 +230,11 @@
_otherWarningCount = 0;
_currentPackageEntry = null;
+ final String minFlutterVersionArg = getStringArg(_skipByFlutterVersionArg);
+ final Version? minFlutterVersion = minFlutterVersionArg.isEmpty
+ ? null
+ : Version.parse(minFlutterVersionArg);
+
final DateTime runStart = DateTime.now();
await initializeRun();
@@ -242,7 +258,8 @@
PackageResult result;
try {
- result = await runForPackage(entry.package);
+ result = await _runForPackageIfSupported(entry.package,
+ minFlutterVersion: minFlutterVersion);
} catch (e, stack) {
printError(e.toString());
printError(stack.toString());
@@ -285,6 +302,26 @@
return true;
}
+ /// Returns the result of running [runForPackage] if the package is supported
+ /// by any run constraints, or a skip result if it is not.
+ Future<PackageResult> _runForPackageIfSupported(
+ RepositoryPackage package, {
+ Version? minFlutterVersion,
+ }) async {
+ if (minFlutterVersion != null) {
+ final Pubspec pubspec = package.parsePubspec();
+ final VersionConstraint? flutterConstraint =
+ pubspec.environment?['flutter'];
+ if (flutterConstraint != null &&
+ !flutterConstraint.allows(minFlutterVersion)) {
+ return PackageResult.skip(
+ 'Does not support Flutter ${minFlutterVersion.toString()}');
+ }
+ }
+
+ return await runForPackage(package);
+ }
+
void _printSuccess(String message) {
captureOutput ? print(message) : printSuccess(message);
}
diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart
index 1846635..fcc87c9 100644
--- a/script/tool/lib/src/common/plugin_command.dart
+++ b/script/tool/lib/src/common/plugin_command.dart
@@ -409,6 +409,9 @@
///
/// By default, packages excluded via --exclude will not be in the stream, but
/// they can be included by passing false for [filterExcluded].
+ ///
+ /// Subpackages are guaranteed to be after the containing package in the
+ /// stream.
Stream<PackageEnumerationEntry> getTargetPackagesAndSubpackages(
{bool filterExcluded = true}) async* {
await for (final PackageEnumerationEntry plugin
diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart
index 878facd..087e5ad 100644
--- a/script/tool/test/analyze_command_test.dart
+++ b/script/tool/test/analyze_command_test.dart
@@ -47,10 +47,10 @@
orderedEquals(<ProcessCall>[
ProcessCall(
'flutter', const <String>['packages', 'get'], plugin1Dir.path),
- ProcessCall(
- 'flutter', const <String>['packages', 'get'], plugin2Dir.path),
ProcessCall('dart', const <String>['analyze', '--fatal-infos'],
plugin1Dir.path),
+ ProcessCall(
+ 'flutter', const <String>['packages', 'get'], plugin2Dir.path),
ProcessCall('dart', const <String>['analyze', '--fatal-infos'],
plugin2Dir.path),
]));
@@ -82,10 +82,10 @@
orderedEquals(<ProcessCall>[
ProcessCall(
'flutter', const <String>['packages', 'get'], plugin1Dir.path),
- ProcessCall(
- 'flutter', const <String>['packages', 'get'], plugin2Dir.path),
ProcessCall('dart', const <String>['analyze', '--fatal-infos'],
plugin1Dir.path),
+ ProcessCall(
+ 'flutter', const <String>['packages', 'get'], plugin2Dir.path),
ProcessCall('dart', const <String>['analyze', '--fatal-infos'],
plugin2Dir.path),
]));
diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart
index 6e46a33..ea02bd4 100644
--- a/script/tool/test/common/package_looping_command_test.dart
+++ b/script/tool/test/common/package_looping_command_test.dart
@@ -236,6 +236,34 @@
expect(command.checkedPackages,
isNot(contains(excluded.childDirectory('example2').path)));
});
+
+ test('skips unsupported versions when requested', () async {
+ final Directory excluded = createFakePlugin('a_plugin', packagesDir,
+ flutterConstraint: '>=2.10.0');
+ final Directory included = createFakePackage('a_package', packagesDir);
+
+ final TestPackageLoopingCommand command =
+ createTestCommand(includeSubpackages: true, hasLongOutput: false);
+ final List<String> output = await runCommand(command, arguments: <String>[
+ '--skip-if-not-supporting-flutter-version=2.5.0'
+ ]);
+
+ expect(
+ command.checkedPackages,
+ unorderedEquals(<String>[
+ included.path,
+ included.childDirectory('example').path,
+ ]));
+ expect(command.checkedPackages, isNot(contains(excluded.path)));
+
+ expect(
+ output,
+ containsAllInOrder(<String>[
+ '${_startHeadingColor}Running for a_package...$_endColor',
+ '${_startHeadingColor}Running for a_plugin...$_endColor',
+ '$_startSkipColor SKIPPING: Does not support Flutter 2.5.0$_endColor',
+ ]));
+ });
});
group('output', () {
diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart
index 28a03c6..782ea72 100644
--- a/script/tool/test/common/plugin_command_test.dart
+++ b/script/tool/test/common/plugin_command_test.dart
@@ -253,6 +253,29 @@
unorderedEquals(<String>[platformInterfacePackage.path]));
});
+ test('returns subpackages after the enclosing package', () async {
+ final SamplePluginCommand localCommand = SamplePluginCommand(
+ packagesDir,
+ processRunner: processRunner,
+ platform: mockPlatform,
+ gitDir: MockGitDir(),
+ includeSubpackages: true,
+ );
+ final CommandRunner<void> localRunner =
+ CommandRunner<void>('common_command', 'subpackage testing');
+ localRunner.addCommand(localCommand);
+
+ final Directory package = createFakePackage('apackage', packagesDir);
+
+ await runCapturingPrint(localRunner, <String>['sample']);
+ expect(
+ localCommand.plugins,
+ containsAllInOrder(<String>[
+ package.path,
+ package.childDirectory('example').path,
+ ]));
+ });
+
group('conflicting package selection', () {
test('does not allow --packages with --run-on-changed-packages',
() async {
@@ -893,11 +916,14 @@
ProcessRunner processRunner = const ProcessRunner(),
Platform platform = const LocalPlatform(),
GitDir? gitDir,
+ this.includeSubpackages = false,
}) : super(packagesDir,
processRunner: processRunner, platform: platform, gitDir: gitDir);
final List<String> plugins = <String>[];
+ final bool includeSubpackages;
+
@override
final String name = 'sample';
@@ -906,7 +932,10 @@
@override
Future<void> run() async {
- await for (final PackageEnumerationEntry entry in getTargetPackages()) {
+ final Stream<PackageEnumerationEntry> packages = includeSubpackages
+ ? getTargetPackagesAndSubpackages()
+ : getTargetPackages();
+ await for (final PackageEnumerationEntry entry in packages) {
plugins.add(entry.package.path);
}
}
diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart
index 91d21c1..6f7d86e 100644
--- a/script/tool/test/util.dart
+++ b/script/tool/test/util.dart
@@ -85,12 +85,14 @@
Map<String, PlatformDetails> platformSupport =
const <String, PlatformDetails>{},
String? version = '0.0.1',
+ String flutterConstraint = '>=2.5.0',
}) {
final Directory pluginDirectory = createFakePackage(name, parentDirectory,
isFlutter: true,
examples: examples,
extraFiles: extraFiles,
- version: version);
+ version: version,
+ flutterConstraint: flutterConstraint);
createFakePubspec(
pluginDirectory,
@@ -99,6 +101,7 @@
isPlugin: true,
platformSupport: platformSupport,
version: version,
+ flutterConstraint: flutterConstraint,
);
return pluginDirectory;
@@ -116,12 +119,16 @@
List<String> extraFiles = const <String>[],
bool isFlutter = false,
String? version = '0.0.1',
+ String flutterConstraint = '>=2.5.0',
}) {
final Directory packageDirectory = parentDirectory.childDirectory(name);
packageDirectory.createSync(recursive: true);
createFakePubspec(packageDirectory,
- name: name, isFlutter: isFlutter, version: version);
+ name: name,
+ isFlutter: isFlutter,
+ version: version,
+ flutterConstraint: flutterConstraint);
createFakeCHANGELOG(packageDirectory, '''
## $version
* Some changes.
@@ -132,7 +139,10 @@
final Directory exampleDir = packageDirectory.childDirectory(examples.first)
..createSync();
createFakePubspec(exampleDir,
- name: '${name}_example', isFlutter: isFlutter, publishTo: 'none');
+ name: '${name}_example',
+ isFlutter: isFlutter,
+ publishTo: 'none',
+ flutterConstraint: flutterConstraint);
} else if (examples.isNotEmpty) {
final Directory exampleDir = packageDirectory.childDirectory('example')
..createSync();
@@ -140,7 +150,10 @@
final Directory currentExample = exampleDir.childDirectory(example)
..createSync();
createFakePubspec(currentExample,
- name: example, isFlutter: isFlutter, publishTo: 'none');
+ name: example,
+ isFlutter: isFlutter,
+ publishTo: 'none',
+ flutterConstraint: flutterConstraint);
}
}
@@ -172,40 +185,61 @@
const <String, PlatformDetails>{},
String publishTo = 'http://no_pub_server.com',
String? version,
+ String dartConstraint = '>=2.0.0 <3.0.0',
+ String flutterConstraint = '>=2.5.0',
}) {
isPlugin |= platformSupport.isNotEmpty;
- parent.childFile('pubspec.yaml').createSync();
- String yaml = '''
-name: $name
+
+ String environmentSection = '''
+environment:
+ sdk: "$dartConstraint"
''';
+ String dependenciesSection = '''
+dependencies:
+''';
+ String pluginSection = '';
+
+ // Add Flutter-specific entries if requested.
if (isFlutter) {
+ environmentSection += '''
+ flutter: "$flutterConstraint"
+''';
+ dependenciesSection += '''
+ flutter:
+ sdk: flutter
+''';
+
if (isPlugin) {
- yaml += '''
+ pluginSection += '''
flutter:
plugin:
platforms:
''';
for (final MapEntry<String, PlatformDetails> platform
in platformSupport.entries) {
- yaml += _pluginPlatformSection(platform.key, platform.value, name);
+ pluginSection +=
+ _pluginPlatformSection(platform.key, platform.value, name);
}
}
- yaml += '''
-dependencies:
- flutter:
- sdk: flutter
-''';
}
- if (version != null) {
- yaml += '''
-version: $version
+
+ String yaml = '''
+name: $name
+${(version != null) ? 'version: $version' : ''}
+
+$environmentSection
+
+$dependenciesSection
+
+$pluginSection
''';
- }
+
if (publishTo.isNotEmpty) {
yaml += '''
publish_to: $publishTo # Hardcoded safeguard to prevent this from somehow being published by a broken test.
''';
}
+ parent.childFile('pubspec.yaml').createSync();
parent.childFile('pubspec.yaml').writeAsStringSync(yaml);
}