Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[flutter_plugin_tools] Migrate analyze to new base command #4084

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 41 additions & 33 deletions script/tool/lib/src/analyze_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import 'package:file/file.dart';
import 'package:path/path.dart' as p;

import 'common/core.dart';
import 'common/plugin_command.dart';
import 'common/package_looping_command.dart';
import 'common/process_runner.dart';

const int _exitBadCustomAnalysisFile = 2;

/// A command to run Dart analysis on packages.
class AnalyzeCommand extends PluginCommand {
class AnalyzeCommand extends PackageLoopingCommand {
/// Creates a analysis command instance.
AnalyzeCommand(
Directory packagesDir, {
Expand All @@ -32,6 +34,8 @@ class AnalyzeCommand extends PluginCommand {

static const String _analysisSdk = 'analysis-sdk';

late String _dartBinaryPath;

@override
final String name = 'analyze';

Expand All @@ -40,9 +44,10 @@ class AnalyzeCommand extends PluginCommand {
'This command requires "dart" and "flutter" to be in your path.';

@override
Future<void> run() async {
print('Verifying analysis settings...');
final bool hasLongOutput = false;

/// Checks that there are no unexpected analysis_options.yaml files.
void _validateAnalysisOptions() {
final List<FileSystemEntity> files = packagesDir.listSync(recursive: true);
for (final FileSystemEntity file in files) {
if (file.basename != 'analysis_options.yaml' &&
Expand All @@ -59,52 +64,55 @@ class AnalyzeCommand extends PluginCommand {
continue;
}

print('Found an extra analysis_options.yaml in ${file.absolute.path}.');
print(
'If this was deliberate, pass the package to the analyze command with the --$_customAnalysisFlag flag and try again.');
throw ToolExit(1);
printError(
'Found an extra analysis_options.yaml in ${file.absolute.path}.');
printError(
'If this was deliberate, pass the package to the analyze command '
'with the --$_customAnalysisFlag flag and try again.');
throw ToolExit(_exitBadCustomAnalysisFile);
}
}

/// Ensures that the dependent packages have been fetched for all packages
/// (including their sub-packages) that will be analyzed.
Future<void> _runPackagesGetOnTargetPackages() async {
final List<Directory> packageDirectories = await getPackages().toList();
final Set<String> packagePaths =
packageDirectories.map((Directory dir) => dir.path).toSet();
packageDirectories.removeWhere((Directory directory) {
// We remove the 'example' subdirectories - 'flutter pub get' automatically
// runs 'pub get' there as part of handling the parent 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) {
await processRunner.runAndStream('flutter', <String>['packages', 'get'],
workingDir: package, exitOnError: true);
}
}

@override
Future<void> initializeRun() async {
print('Verifying analysis settings...');
_validateAnalysisOptions();

print('Fetching dependencies...');
await _runPackagesGetOnTargetPackages();

// Use the Dart SDK override if one was passed in.
final String? dartSdk = argResults![_analysisSdk] as String?;
final String dartBinary =
dartSdk == null ? 'dart' : p.join(dartSdk, 'bin', 'dart');

final List<String> failingPackages = <String>[];
final List<Directory> pluginDirectories = await getPlugins().toList();
for (final Directory package in pluginDirectories) {
final int exitCode = await processRunner.runAndStream(
dartBinary, <String>['analyze', '--fatal-infos'],
workingDir: package);
if (exitCode != 0) {
failingPackages.add(p.basename(package.path));
}
}

print('\n\n');
_dartBinaryPath = dartSdk == null ? 'dart' : p.join(dartSdk, 'bin', 'dart');
}

if (failingPackages.isNotEmpty) {
print('The following packages have analyzer errors (see above):');
for (final String package in failingPackages) {
print(' * $package');
}
throw ToolExit(1);
@override
Future<List<String>> runForPackage(Directory package) async {
final int exitCode = await processRunner.runAndStream(
_dartBinaryPath, <String>['analyze', '--fatal-infos'],
workingDir: package);
if (exitCode != 0) {
return PackageLoopingCommand.failure;
}

print('No analyzer errors found!');
return PackageLoopingCommand.success;
}
}
19 changes: 11 additions & 8 deletions script/tool/test/analyze_command_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void main() {
final MockProcess mockProcess = MockProcess();
mockProcess.exitCodeCompleter.complete(0);
processRunner.processToReturn = mockProcess;
await runner.run(<String>['analyze']);
await runCapturingPrint(runner, <String>['analyze']);

expect(
processRunner.recordedCalls,
Expand All @@ -58,7 +58,7 @@ void main() {
final MockProcess mockProcess = MockProcess();
mockProcess.exitCodeCompleter.complete(0);
processRunner.processToReturn = mockProcess;
await runner.run(<String>['analyze']);
await runCapturingPrint(runner, <String>['analyze']);

expect(
processRunner.recordedCalls,
Expand All @@ -77,7 +77,7 @@ void main() {
final MockProcess mockProcess = MockProcess();
mockProcess.exitCodeCompleter.complete(0);
processRunner.processToReturn = mockProcess;
await runner.run(<String>['analyze']);
await runCapturingPrint(runner, <String>['analyze']);

expect(
processRunner.recordedCalls,
Expand All @@ -99,7 +99,8 @@ void main() {
final MockProcess mockProcess = MockProcess();
mockProcess.exitCodeCompleter.complete(0);
processRunner.processToReturn = mockProcess;
await runner.run(<String>['analyze', '--analysis-sdk', 'foo/bar/baz']);
await runCapturingPrint(
runner, <String>['analyze', '--analysis-sdk', 'foo/bar/baz']);

expect(
processRunner.recordedCalls,
Expand All @@ -123,15 +124,15 @@ void main() {
createFakePlugin('foo', packagesDir,
extraFiles: <String>['analysis_options.yaml']);

await expectLater(() => runner.run(<String>['analyze']),
await expectLater(() => runCapturingPrint(runner, <String>['analyze']),
throwsA(const TypeMatcher<ToolExit>()));
});

test('fails .analysis_options', () async {
createFakePlugin('foo', packagesDir,
extraFiles: <String>['.analysis_options']);

await expectLater(() => runner.run(<String>['analyze']),
await expectLater(() => runCapturingPrint(runner, <String>['analyze']),
throwsA(const TypeMatcher<ToolExit>()));
});

Expand All @@ -142,7 +143,8 @@ void main() {
final MockProcess mockProcess = MockProcess();
mockProcess.exitCodeCompleter.complete(0);
processRunner.processToReturn = mockProcess;
await runner.run(<String>['analyze', '--custom-analysis', 'foo']);
await runCapturingPrint(
runner, <String>['analyze', '--custom-analysis', 'foo']);

expect(
processRunner.recordedCalls,
Expand All @@ -164,7 +166,8 @@ void main() {
processRunner.processToReturn = mockProcess;

await expectLater(
() => runner.run(<String>['analyze', '--custom-analysis', '']),
() => runCapturingPrint(
runner, <String>['analyze', '--custom-analysis', '']),
throwsA(const TypeMatcher<ToolExit>()));
});
});
Expand Down