Skip to content

Commit 26102c9

Browse files
authored
Condense and summarize doctor output without --verbose (flutter#14173)
* Flatten change out from lots of merges and temporary hacks for flutter#7224 * Expand fakes to cover cases where doctor passes * -v != --verbose * Cosmetic touchups * delint * Clean up summary line, add a switch for review comments * Review comments * nit * review comment * review comments
1 parent 308899f commit 26102c9

File tree

7 files changed

+266
-26
lines changed

7 files changed

+266
-26
lines changed

ISSUE_TEMPLATE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ Run `flutter analyze` and attach any output of that command also.
1313

1414
## Flutter Doctor
1515

16-
Paste the output of running `flutter doctor` here.
16+
Paste the output of running `flutter doctor -v` here.
1717

1818
> For more information about diagnosing and reporting Flutter bugs, please see [https://flutter.io/bug-reports/](https://flutter.io/bug-reports/).

packages/flutter_tools/lib/executable.dart

+8-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@ import 'src/runner/flutter_command.dart';
3737
/// This function is intended to be used from the `flutter` command line tool.
3838
Future<Null> main(List<String> args) async {
3939
final bool verbose = args.contains('-v') || args.contains('--verbose');
40+
41+
final bool doctor = (args.isNotEmpty && args.first == 'doctor') ||
42+
(args.length == 2 && verbose && args.last == 'doctor');
4043
final bool help = args.contains('-h') || args.contains('--help') ||
4144
(args.isNotEmpty && args.first == 'help') || (args.length == 1 && verbose);
45+
final bool muteCommandLogging = (help || doctor);
4246
final bool verboseHelp = help && verbose;
4347

4448
await runner.run(args, <FlutterCommand>[
@@ -51,7 +55,7 @@ Future<Null> main(List<String> args) async {
5155
new CreateCommand(),
5256
new DaemonCommand(hidden: !verboseHelp),
5357
new DevicesCommand(),
54-
new DoctorCommand(),
58+
new DoctorCommand(verbose: verbose),
5559
new DriveCommand(),
5660
new FormatCommand(),
5761
new FuchsiaReloadCommand(),
@@ -67,5 +71,7 @@ Future<Null> main(List<String> args) async {
6771
new TraceCommand(),
6872
new UpdatePackagesCommand(hidden: !verboseHelp),
6973
new UpgradeCommand(),
70-
], verbose: verbose, verboseHelp: verboseHelp);
74+
], verbose: verbose,
75+
muteCommandLogging: muteCommandLogging,
76+
verboseHelp: verboseHelp);
7177
}

packages/flutter_tools/lib/runner.dart

+4-2
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ import 'src/version.dart';
3636
Future<int> run(
3737
List<String> args,
3838
List<FlutterCommand> commands, {
39+
bool muteCommandLogging: false,
3940
bool verbose: false,
4041
bool verboseHelp: false,
4142
bool reportCrashes,
4243
String flutterVersion,
4344
}) async {
4445
reportCrashes ??= !isRunningOnBot;
4546

46-
if (verboseHelp) {
47-
// Remove the verbose option; for help, users don't need to see verbose logs.
47+
if (muteCommandLogging) {
48+
// Remove the verbose option; for help and doctor, users don't need to see
49+
// verbose logs.
4850
args = new List<String>.from(args);
4951
args.removeWhere((String option) => option == '-v' || option == '--verbose');
5052
}

packages/flutter_tools/lib/src/commands/doctor.dart

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ import '../doctor.dart';
88
import '../runner/flutter_command.dart';
99

1010
class DoctorCommand extends FlutterCommand {
11-
DoctorCommand() {
11+
DoctorCommand({this.verbose: false}) {
1212
argParser.addFlag('android-licenses',
1313
defaultsTo: false,
1414
negatable: false,
1515
help: 'Run the Android SDK manager tool to accept the SDK\'s licenses.',
1616
);
1717
}
1818

19+
final bool verbose;
20+
1921
@override
2022
final String name = 'doctor';
2123

@@ -24,7 +26,7 @@ class DoctorCommand extends FlutterCommand {
2426

2527
@override
2628
Future<FlutterCommandResult> runCommand() async {
27-
final bool success = await doctor.diagnose(androidLicenses: argResults['android-licenses']);
29+
final bool success = await doctor.diagnose(androidLicenses: argResults['android-licenses'], verbose: verbose);
2830
return new FlutterCommandResult(success ? ExitStatus.success : ExitStatus.warning);
2931
}
3032
}

packages/flutter_tools/lib/src/doctor.dart

+47-17
Original file line numberDiff line numberDiff line change
@@ -95,34 +95,55 @@ class Doctor {
9595
return buffer.toString();
9696
}
9797

98-
/// Print verbose information about the state of installed tooling.
99-
Future<bool> diagnose({ bool androidLicenses: false }) async {
98+
/// Print information about the state of installed tooling.
99+
Future<bool> diagnose({ bool androidLicenses: false, bool verbose: true }) async {
100100
if (androidLicenses)
101101
return AndroidWorkflow.runLicenseManager();
102102

103+
if (!verbose) {
104+
printStatus('Doctor summary (to see all details, run flutter doctor -v):');
105+
}
103106
bool doctorResult = true;
107+
int issues = 0;
104108

105109
for (DoctorValidator validator in validators) {
106110
final ValidationResult result = await validator.validate();
107111

108-
if (result.type == ValidationType.missing)
112+
if (result.type == ValidationType.missing) {
109113
doctorResult = false;
114+
}
115+
if (result.type != ValidationType.installed) {
116+
issues += 1;
117+
}
110118

111119
if (result.statusInfo != null)
112120
printStatus('${result.leadingBox} ${validator.title} (${result.statusInfo})');
113121
else
114122
printStatus('${result.leadingBox} ${validator.title}');
115123

116124
for (ValidationMessage message in result.messages) {
117-
final String text = message.message.replaceAll('\n', '\n ');
118-
if (message.isError) {
119-
printStatus(' ✗ $text', emphasis: true);
120-
} else {
121-
printStatus(' • $text');
125+
if (message.isError || message.isHint || verbose == true) {
126+
final String text = message.message.replaceAll('\n', '\n ');
127+
if (message.isError) {
128+
printStatus(' ✗ $text', emphasis: true);
129+
} else if (message.isHint) {
130+
printStatus(' ! $text');
131+
} else {
132+
printStatus(' • $text');
133+
}
122134
}
123135
}
136+
if (verbose)
137+
printStatus('');
138+
}
124139

140+
// Make sure there's always one line before the summary even when not verbose.
141+
if (!verbose)
125142
printStatus('');
143+
if (issues > 0) {
144+
printStatus('! Doctor found issues in $issues categor${issues > 1 ? "ies" : "y"}.');
145+
} else {
146+
printStatus('• No issues found!');
126147
}
127148

128149
return doctorResult;
@@ -159,7 +180,10 @@ abstract class DoctorValidator {
159180
Future<ValidationResult> validate();
160181
}
161182

183+
162184
class ValidationResult {
185+
/// [ValidationResult.type] should only equal [ValidationResult.installed]
186+
/// if no [messages] are hints or errors.
163187
ValidationResult(this.type, this.messages, { this.statusInfo });
164188

165189
final ValidationType type;
@@ -168,20 +192,26 @@ class ValidationResult {
168192
final List<ValidationMessage> messages;
169193

170194
String get leadingBox {
171-
if (type == ValidationType.missing)
172-
return '[✗]';
173-
else if (type == ValidationType.installed)
174-
return '[✓]';
175-
else
176-
return '[-]';
195+
assert(type != null);
196+
switch (type) {
197+
case ValidationType.missing:
198+
return '[✗]';
199+
case ValidationType.installed:
200+
return '[✓]';
201+
case ValidationType.partial:
202+
return '[!]';
203+
}
204+
return null;
177205
}
178206
}
179207

180208
class ValidationMessage {
181-
ValidationMessage(this.message) : isError = false;
182-
ValidationMessage.error(this.message) : isError = true;
209+
ValidationMessage(this.message) : isError = false, isHint = false;
210+
ValidationMessage.error(this.message) : isError = true, isHint = false;
211+
ValidationMessage.hint(this.message) : isError = false, isHint = true;
183212

184213
final bool isError;
214+
final bool isHint;
185215
final String message;
186216

187217
@override
@@ -512,7 +542,7 @@ class DeviceValidator extends DoctorValidator {
512542
if (diagnostics.isNotEmpty) {
513543
messages = diagnostics.map((String message) => new ValidationMessage(message)).toList();
514544
} else {
515-
messages = <ValidationMessage>[new ValidationMessage('None')];
545+
messages = <ValidationMessage>[new ValidationMessage.hint('No devices available')];
516546
}
517547
} else {
518548
messages = await Device.descriptions(devices)

packages/flutter_tools/test/analytics_test.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ void main() {
9696

9797
testUsingContext('flutter commands send timing events', () async {
9898
mockTimes = <int>[1000, 2000];
99-
when(mockDoctor.diagnose(androidLicenses: false)).thenReturn(true);
99+
when(mockDoctor.diagnose(androidLicenses: false, verbose: false)).thenReturn(true);
100100
final DoctorCommand command = new DoctorCommand();
101101
final CommandRunner<Null> runner = createTestCommandRunner(command);
102102
await runner.run(<String>['doctor']);
@@ -115,7 +115,7 @@ void main() {
115115

116116
testUsingContext('doctor fail sends warning', () async {
117117
mockTimes = <int>[1000, 2000];
118-
when(mockDoctor.diagnose(androidLicenses: false)).thenReturn(false);
118+
when(mockDoctor.diagnose(androidLicenses: false, verbose: false)).thenReturn(false);
119119
final DoctorCommand command = new DoctorCommand();
120120
final CommandRunner<Null> runner = createTestCommandRunner(command);
121121
await runner.run(<String>['doctor']);

0 commit comments

Comments
 (0)