[flutter_plugin_tools] Fix subpackage analysis (#5027)
diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md
index 8e5ae21..35786f4 100644
--- a/script/tool/CHANGELOG.md
+++ b/script/tool/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.8.1
+
+- Fixes an `analyze` regression in 0.8.0 with packages that have non-`example`
+ sub-packages.
+
## 0.8.0
- Ensures that `firebase-test-lab` runs include an `integration_test` runner.
diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart
index d8b17bf..824d766 100644
--- a/script/tool/lib/src/analyze_command.dart
+++ b/script/tool/lib/src/analyze_command.dart
@@ -102,16 +102,25 @@
@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']);
+ // Analysis runs over the package and all subpackages, so all of them need
+ // `flutter packages get` run before analyzing. `example` packages can be
+ // skipped since 'flutter packages get' automatically runs `pub get` in
+ // examples as part of handling the parent directory.
+ final List<RepositoryPackage> packagesToGet = <RepositoryPackage>[
+ package,
+ ...await getSubpackages(package).toList(),
+ ];
+ for (final RepositoryPackage packageToGet in packagesToGet) {
+ if (packageToGet.directory.basename != 'example' ||
+ !RepositoryPackage(packageToGet.directory.parent)
+ .pubspecFile
+ .existsSync()) {
+ final int exitCode = await processRunner.runAndStream(
+ flutterCommand, <String>['packages', 'get'],
+ workingDir: packageToGet.directory);
+ if (exitCode != 0) {
+ return PackageResult.fail(<String>['Unable to get dependencies']);
+ }
}
}
diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart
index fcc87c9..d210028 100644
--- a/script/tool/lib/src/common/plugin_command.dart
+++ b/script/tool/lib/src/common/plugin_command.dart
@@ -417,16 +417,22 @@
await for (final PackageEnumerationEntry plugin
in getTargetPackages(filterExcluded: filterExcluded)) {
yield plugin;
- yield* plugin.package.directory
- .list(recursive: true, followLinks: false)
- .where(_isDartPackage)
- .map((FileSystemEntity directory) => PackageEnumerationEntry(
- // _isDartPackage guarantees that this cast is valid.
- RepositoryPackage(directory as Directory),
- excluded: plugin.excluded));
+ yield* getSubpackages(plugin.package).map((RepositoryPackage package) =>
+ PackageEnumerationEntry(package, excluded: plugin.excluded));
}
}
+ /// Returns all Dart package folders (e.g., examples) under the given package.
+ Stream<RepositoryPackage> getSubpackages(RepositoryPackage package,
+ {bool filterExcluded = true}) async* {
+ yield* package.directory
+ .list(recursive: true, followLinks: false)
+ .where(_isDartPackage)
+ .map((FileSystemEntity directory) =>
+ // _isDartPackage guarantees that this cast is valid.
+ RepositoryPackage(directory as Directory));
+ }
+
/// Returns the files contained, recursively, within the packages
/// involved in this command execution.
Stream<File> getFiles() {
diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart
index 5ee4284..c090603 100644
--- a/script/tool/lib/src/make_deps_path_based_command.dart
+++ b/script/tool/lib/src/make_deps_path_based_command.dart
@@ -207,6 +207,10 @@
allComponents.contains('example')) {
continue;
}
+ if (!allComponents.contains(packagesDir.basename)) {
+ print(' Skipping $changedPath; not in packages directory.');
+ continue;
+ }
final RepositoryPackage package =
RepositoryPackage(packagesDir.fileSystem.file(changedPath).parent);
// Ignored deleted packages, as they won't be published.
diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml
index 9ca5e2b..93a1a87 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.8.0
+version: 0.8.1
dependencies:
args: ^2.1.0
diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart
index 087e5ad..56f4ab9 100644
--- a/script/tool/test/analyze_command_test.dart
+++ b/script/tool/test/analyze_command_test.dart
@@ -71,6 +71,31 @@
]));
});
+ test('runs flutter pub get for non-example subpackages', () async {
+ final Directory mainPackageDir = createFakePackage('a', packagesDir);
+ final Directory otherPackages =
+ mainPackageDir.childDirectory('other_packages');
+ final Directory subpackage1 =
+ createFakePackage('subpackage1', otherPackages);
+ final Directory subpackage2 =
+ createFakePackage('subpackage2', otherPackages);
+
+ await runCapturingPrint(runner, <String>['analyze']);
+
+ expect(
+ processRunner.recordedCalls,
+ orderedEquals(<ProcessCall>[
+ ProcessCall('flutter', const <String>['packages', 'get'],
+ mainPackageDir.path),
+ ProcessCall(
+ 'flutter', const <String>['packages', 'get'], subpackage1.path),
+ ProcessCall(
+ 'flutter', const <String>['packages', 'get'], subpackage2.path),
+ ProcessCall('dart', const <String>['analyze', '--fatal-infos'],
+ mainPackageDir.path),
+ ]));
+ });
+
test('don\'t elide a non-contained example package', () async {
final Directory plugin1Dir = createFakePlugin('a', packagesDir);
final Directory plugin2Dir = createFakePlugin('example', packagesDir);
diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart
index bdd2139..2021f24 100644
--- a/script/tool/test/make_deps_path_based_command_test.dart
+++ b/script/tool/test/make_deps_path_based_command_test.dart
@@ -323,5 +323,41 @@
]),
);
});
+
+ test('skips anything outside of the packages directory', () async {
+ final Directory toolDir = packagesDir.parent.childDirectory('tool');
+ const String newVersion = '1.1.0';
+ final Directory package = createFakePackage(
+ 'flutter_plugin_tools', toolDir,
+ version: newVersion);
+
+ // Simulate a minor version change so it would be a target.
+ final File pubspecFile = RepositoryPackage(package).pubspecFile;
+ final String changedFileOutput = <File>[
+ pubspecFile,
+ ].map((File file) => file.path).join('\n');
+ processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
+ MockProcess(stdout: changedFileOutput),
+ ];
+ final String gitPubspecContents =
+ pubspecFile.readAsStringSync().replaceAll(newVersion, '1.0.0');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: gitPubspecContents),
+ ];
+
+ final List<String> output = await runCapturingPrint(runner, <String>[
+ 'make-deps-path-based',
+ '--target-dependencies-with-non-breaking-updates'
+ ]);
+
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains(
+ 'Skipping /tool/flutter_plugin_tools/pubspec.yaml; not in packages directory.'),
+ contains('No target dependencies'),
+ ]),
+ );
+ });
});
}