From fc943a9a8203e6f115a4025f008c6f4f428532a0 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 3 Jun 2026 13:17:56 -0400 Subject: [PATCH 1/2] [tool] Use `flutter` for pub commands when examples use Flutter https://github.com/flutter/packages/pull/11797 changed the repo tooling to use `dart` instead of `flutter` for more commands when the target package isn't a Flutter package, include `pub downgrade` and `pub get` in `analyze`. However, since `pub get` will resolve any example apps, if any of *them* use Flutter, `dart pub get` will fail unless `dart` is coming from the Flutter SDK. Normally this is fine since that's the expected setup, but for the head-head tests in dart-lang that run the repo analysis using the head version of Dart via the `--analysis-sdk` flag, using that version of Dart for pub commands breaks things. This checks the example apps for Flutter dependencies to ensure that `dart pub` won't be used in cases where that can cause failures to resolve in the examples. This should be safe for core-packages, since core-packages should never use Flutter, even in examples. --- script/tool/lib/src/analyze_command.dart | 3 ++ script/tool/lib/src/common/pub_utils.dart | 27 +++++++++-- script/tool/test/analyze_command_test.dart | 54 +++++++++++++++++++-- script/tool/test/common/pub_utils_test.dart | 19 +++++++- 4 files changed, 94 insertions(+), 9 deletions(-) diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index 4810e3bbfc10..5fe1ee9e0929 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -323,6 +323,9 @@ class AnalyzeCommand extends PackageLoopingCommand { processRunner, platform, dartSdkPathOverride: _dartBinaryPath, + // 'get' and 'downgrade' resolve examples as well, so check + // those as well when deciding between `flutter` and `dart`. + recursiveFlutterCheck: true, ); } diff --git a/script/tool/lib/src/common/pub_utils.dart b/script/tool/lib/src/common/pub_utils.dart index adfeffc3f684..0f8aefb01c98 100644 --- a/script/tool/lib/src/common/pub_utils.dart +++ b/script/tool/lib/src/common/pub_utils.dart @@ -26,6 +26,7 @@ Future runPubGet( processRunner, platform, streamOutput: streamOutput, + recursiveFlutterCheck: true, ); } @@ -41,8 +42,14 @@ Future runPubCommand( Platform platform, { bool streamOutput = true, String? dartSdkPathOverride, + bool recursiveFlutterCheck = false, }) async { - final String command = _pubCommand(package, platform, dartSdkPathOverride: dartSdkPathOverride); + final String command = _pubCommand( + package, + platform, + dartSdkPathOverride: dartSdkPathOverride, + recursiveFlutterCheck: recursiveFlutterCheck, + ); final args = ['pub', ...commandArgs]; final int exitCode; if (streamOutput) { @@ -78,10 +85,24 @@ Future startPubCommand( ], workingDirectory: package.directory); } -String _pubCommand(RepositoryPackage package, Platform platform, {String? dartSdkPathOverride}) { +String _pubCommand( + RepositoryPackage package, + Platform platform, { + String? dartSdkPathOverride, + bool recursiveFlutterCheck = false, +}) { // Running `dart pub get` on a Flutter package can fail if a non-Flutter Dart // is first in the path, so use `flutter pub get` for any Flutter package. - return package.requiresFlutter() + bool useFlutter = package.requiresFlutter(); + if (!useFlutter && recursiveFlutterCheck) { + for (final RepositoryPackage example in package.getExamples()) { + if (example.requiresFlutter()) { + useFlutter = true; + break; + } + } + } + return useFlutter ? (platform.isWindows ? 'flutter.bat' : 'flutter') : (dartSdkPathOverride ?? 'dart'); } diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index fa5d742e90f0..1d3b5cba5176 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -175,6 +175,21 @@ void main() { ); }); + test('runs flutter pub get for non-Flutter packages with Flutter examples', () async { + final RepositoryPackage mainPackage = createFakePackage('a', packagesDir, examples: []); + createFakePackage('example', mainPackage.directory, examples: [], isFlutter: true); + + await runCapturingPrint(runner, ['analyze']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('flutter', const ['pub', 'get'], mainPackage.path), + ProcessCall('dart', const ['analyze', '--fatal-infos'], mainPackage.path), + ]), + ); + }); + test('passes lib/ directory with --lib-only', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); @@ -254,16 +269,47 @@ void main() { }); test('downgrades first when requested', () async { - final RepositoryPackage plugin = createFakePlugin('a', packagesDir); + final RepositoryPackage package = createFakePackage('a', packagesDir); await runCapturingPrint(runner, ['analyze', '--downgrade']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'downgrade'], plugin.path), - ProcessCall('flutter', const ['pub', 'get'], plugin.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin.path), + ProcessCall('dart', const ['pub', 'downgrade'], package.path), + ProcessCall('dart', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['analyze', '--fatal-infos'], package.path), + ]), + ); + }); + + test('downgrades using flutter for Flutter packages', () async { + final RepositoryPackage package = createFakePackage('a', packagesDir, isFlutter: true); + + await runCapturingPrint(runner, ['analyze', '--downgrade']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('flutter', const ['pub', 'downgrade'], package.path), + ProcessCall('flutter', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['analyze', '--fatal-infos'], package.path), + ]), + ); + }); + + test('downgrades using flutter for non-Flutter packages with Flutter examples', () async { + final RepositoryPackage package = createFakePackage('a', packagesDir, examples: []); + createFakePackage('example', package.directory, isFlutter: true); + + await runCapturingPrint(runner, ['analyze', '--downgrade']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('flutter', const ['pub', 'downgrade'], package.path), + ProcessCall('flutter', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['analyze', '--fatal-infos'], package.path), ]), ); }); diff --git a/script/tool/test/common/pub_utils_test.dart b/script/tool/test/common/pub_utils_test.dart index cfcfdf5f8c9d..97925a9572a4 100644 --- a/script/tool/test/common/pub_utils_test.dart +++ b/script/tool/test/common/pub_utils_test.dart @@ -17,7 +17,7 @@ void main() { (:packagesDir, :processRunner, gitProcessRunner: _, gitDir: _) = configureBaseCommandMocks(); }); - test('runs with Dart for a non-Flutter package by default', () async { + test('runs with Dart for a non-Flutter package', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); final platform = MockPlatform(); @@ -31,7 +31,7 @@ void main() { ); }); - test('runs with Flutter for a Flutter package by default', () async { + test('runs with Flutter for a Flutter package', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir, isFlutter: true); final platform = MockPlatform(); @@ -45,6 +45,21 @@ void main() { ); }); + test('runs with Flutter for a non-Flutter package with a Flutter example', () async { + final RepositoryPackage package = createFakePackage('a_package', packagesDir, examples: []); + createFakePackage('example', package.directory, examples: [], isFlutter: true); + final platform = MockPlatform(); + + await runPubGet(package, processRunner, platform); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('flutter', const ['pub', 'get'], package.path), + ]), + ); + }); + test('uses the correct Flutter command on Windows', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir, isFlutter: true); final platform = MockPlatform(isWindows: true); From 0d200c1db824f79114d77cb6373fb24abdd96b77 Mon Sep 17 00:00:00 2001 From: stuartmorgan-g Date: Wed, 3 Jun 2026 13:34:45 -0400 Subject: [PATCH 2/2] Update script/tool/test/analyze_command_test.dart Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- script/tool/test/analyze_command_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 1d3b5cba5176..7a45616a8325 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -300,7 +300,7 @@ void main() { test('downgrades using flutter for non-Flutter packages with Flutter examples', () async { final RepositoryPackage package = createFakePackage('a', packagesDir, examples: []); - createFakePackage('example', package.directory, isFlutter: true); + createFakePackage('example', package.directory, examples: [], isFlutter: true); await runCapturingPrint(runner, ['analyze', '--downgrade']);