Skip to content

Commit fbb4b3a

Browse files
[flutter_plugin_tools] Improve license-check output (flutter#4154)
Currently each type of check handles its output in isolation, which creates confusing output when the last check succeeds but an earlier check fails, since the end of the output will just be a success message. This makes the output follow the same basic approach as the package looper commands, where all failures are collected, and then a final summary is presented at the end, so the last message will always reflect the important details. It also adopts the colorized output now used by most other commands.
1 parent ce371a6 commit fbb4b3a

File tree

3 files changed

+175
-98
lines changed

3 files changed

+175
-98
lines changed

script/tool/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## NEXT
2+
3+
- Improved `license-check` output.
4+
15
## 0.4.0
26

37
- Modified the output format of many commands

script/tool/lib/src/license_check_command.dart

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -107,21 +107,65 @@ class LicenseCheckCommand extends PluginCommand {
107107

108108
@override
109109
Future<void> run() async {
110-
final Iterable<File> codeFiles = (await _getAllFiles()).where((File file) =>
110+
final Iterable<File> allFiles = await _getAllFiles();
111+
112+
final Iterable<File> codeFiles = allFiles.where((File file) =>
111113
_codeFileExtensions.contains(p.extension(file.path)) &&
112114
!_shouldIgnoreFile(file));
113-
final Iterable<File> firstPartyLicenseFiles = (await _getAllFiles()).where(
114-
(File file) =>
115-
path.basename(file.basename) == 'LICENSE' && !_isThirdParty(file));
115+
final Iterable<File> firstPartyLicenseFiles = allFiles.where((File file) =>
116+
path.basename(file.basename) == 'LICENSE' && !_isThirdParty(file));
116117

117-
final bool copyrightCheckSucceeded = await _checkCodeLicenses(codeFiles);
118-
print('\n=======================================\n');
119-
final bool licenseCheckSucceeded =
118+
final List<File> licenseFileFailures =
120119
await _checkLicenseFiles(firstPartyLicenseFiles);
120+
final Map<_LicenseFailureType, List<File>> codeFileFailures =
121+
await _checkCodeLicenses(codeFiles);
122+
123+
bool passed = true;
124+
125+
print('\n=======================================\n');
126+
127+
if (licenseFileFailures.isNotEmpty) {
128+
passed = false;
129+
printError(
130+
'The following LICENSE files do not follow the expected format:');
131+
for (final File file in licenseFileFailures) {
132+
printError(' ${file.path}');
133+
}
134+
printError('Please ensure that they use the exact format used in this '
135+
'repository".\n');
136+
}
137+
138+
if (codeFileFailures[_LicenseFailureType.incorrectFirstParty]!.isNotEmpty) {
139+
passed = false;
140+
printError('The license block for these files is missing or incorrect:');
141+
for (final File file
142+
in codeFileFailures[_LicenseFailureType.incorrectFirstParty]!) {
143+
printError(' ${file.path}');
144+
}
145+
printError(
146+
'If this third-party code, move it to a "third_party/" directory, '
147+
'otherwise ensure that you are using the exact copyright and license '
148+
'text used by all first-party files in this repository.\n');
149+
}
150+
151+
if (codeFileFailures[_LicenseFailureType.unknownThirdParty]!.isNotEmpty) {
152+
passed = false;
153+
printError(
154+
'No recognized license was found for the following third-party files:');
155+
for (final File file
156+
in codeFileFailures[_LicenseFailureType.unknownThirdParty]!) {
157+
printError(' ${file.path}');
158+
}
159+
print('Please check that they have a license at the top of the file. '
160+
'If they do, the license check needs to be updated to recognize '
161+
'the new third-party license block.\n');
162+
}
121163

122-
if (!copyrightCheckSucceeded || !licenseCheckSucceeded) {
164+
if (!passed) {
123165
throw ToolExit(1);
124166
}
167+
168+
printSuccess('All files passed validation!');
125169
}
126170

127171
// Creates the expected copyright+license block for first-party code.
@@ -135,9 +179,10 @@ class LicenseCheckCommand extends PluginCommand {
135179
'${comment}found in the LICENSE file.$suffix\n';
136180
}
137181

138-
// Checks all license blocks for [codeFiles], returning false if any of them
139-
// fail validation.
140-
Future<bool> _checkCodeLicenses(Iterable<File> codeFiles) async {
182+
/// Checks all license blocks for [codeFiles], returning any that fail
183+
/// validation.
184+
Future<Map<_LicenseFailureType, List<File>>> _checkCodeLicenses(
185+
Iterable<File> codeFiles) async {
141186
final List<File> incorrectFirstPartyFiles = <File>[];
142187
final List<File> unrecognizedThirdPartyFiles = <File>[];
143188

@@ -171,46 +216,21 @@ class LicenseCheckCommand extends PluginCommand {
171216
}
172217
}
173218
}
174-
print('\n');
175219

176220
// Sort by path for more usable output.
177221
final int Function(File, File) pathCompare =
178222
(File a, File b) => a.path.compareTo(b.path);
179223
incorrectFirstPartyFiles.sort(pathCompare);
180224
unrecognizedThirdPartyFiles.sort(pathCompare);
181225

182-
if (incorrectFirstPartyFiles.isNotEmpty) {
183-
print('The license block for these files is missing or incorrect:');
184-
for (final File file in incorrectFirstPartyFiles) {
185-
print(' ${file.path}');
186-
}
187-
print('If this third-party code, move it to a "third_party/" directory, '
188-
'otherwise ensure that you are using the exact copyright and license '
189-
'text used by all first-party files in this repository.\n');
190-
}
191-
192-
if (unrecognizedThirdPartyFiles.isNotEmpty) {
193-
print(
194-
'No recognized license was found for the following third-party files:');
195-
for (final File file in unrecognizedThirdPartyFiles) {
196-
print(' ${file.path}');
197-
}
198-
print('Please check that they have a license at the top of the file. '
199-
'If they do, the license check needs to be updated to recognize '
200-
'the new third-party license block.\n');
201-
}
202-
203-
final bool succeeded =
204-
incorrectFirstPartyFiles.isEmpty && unrecognizedThirdPartyFiles.isEmpty;
205-
if (succeeded) {
206-
print('All source files passed validation!');
207-
}
208-
return succeeded;
226+
return <_LicenseFailureType, List<File>>{
227+
_LicenseFailureType.incorrectFirstParty: incorrectFirstPartyFiles,
228+
_LicenseFailureType.unknownThirdParty: unrecognizedThirdPartyFiles,
229+
};
209230
}
210231

211-
// Checks all provide LICENSE files, returning false if any of them
212-
// fail validation.
213-
Future<bool> _checkLicenseFiles(Iterable<File> files) async {
232+
/// Checks all provided LICENSE [files], returning any that fail validation.
233+
Future<List<File>> _checkLicenseFiles(Iterable<File> files) async {
214234
final List<File> incorrectLicenseFiles = <File>[];
215235

216236
for (final File file in files) {
@@ -219,22 +239,8 @@ class LicenseCheckCommand extends PluginCommand {
219239
incorrectLicenseFiles.add(file);
220240
}
221241
}
222-
print('\n');
223242

224-
if (incorrectLicenseFiles.isNotEmpty) {
225-
print('The following LICENSE files do not follow the expected format:');
226-
for (final File file in incorrectLicenseFiles) {
227-
print(' ${file.path}');
228-
}
229-
print(
230-
'Please ensure that they use the exact format used in this repository".\n');
231-
}
232-
233-
final bool succeeded = incorrectLicenseFiles.isEmpty;
234-
if (succeeded) {
235-
print('All LICENSE files passed validation!');
236-
}
237-
return succeeded;
243+
return incorrectLicenseFiles;
238244
}
239245

240246
bool _shouldIgnoreFile(File file) {
@@ -255,3 +261,5 @@ class LicenseCheckCommand extends PluginCommand {
255261
.map((FileSystemEntity file) => file as File)
256262
.toList();
257263
}
264+
265+
enum _LicenseFailureType { incorrectFirstParty, unknownThirdParty }

0 commit comments

Comments
 (0)