blob: 2998ccfa2c90c23e1194f0adeb756b11b3ce3848 [file] [log] [blame]
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io' as io;
import 'package:args/args.dart';
import 'package:cocoon_code_health/src/checks.dart';
import 'package:cocoon_code_health/src/checks/do_not_submit_fixme.dart';
import 'package:cocoon_code_health/src/checks/no_trailing_spaces.dart';
import 'package:cocoon_code_health/src/checks/use_test_logging.dart';
import 'package:cocoon_common/cocoon_common.dart';
import 'package:file/local.dart';
import 'package:path/path.dart' as p;
final _parser = ArgParser(allowTrailingOptions: false)
..addOption(
'repository-root',
help: 'The root of the flutter/cocoon repository.',
defaultsTo: _findRepositoryRoot(),
)
..addFlag('quiet', abbr: 'q', help: 'Quiet output')
..addFlag('verbose', abbr: 'v', help: 'Show additional output');
const _anyPackageChecks = [DoNotSubmitFixme(), NoTrailingWhitespace()];
const _serverPackageChecks = [..._anyPackageChecks, UseTestLogging()];
final _checks = {
p.join('analyze'): _anyPackageChecks,
p.join('app_dart'): _serverPackageChecks,
p.join('cipd_packages', 'codesign'): _anyPackageChecks,
p.join('cipd_packages', 'device_doctor'): _anyPackageChecks,
p.join('cipd_packages', 'doxygen'): _anyPackageChecks,
p.join('cipd_packages', 'ktlint'): _anyPackageChecks,
p.join('cipd_packages', 'ruby'): _anyPackageChecks,
p.join('cloud_build'): _anyPackageChecks,
p.join('dashboard'): _anyPackageChecks,
p.join('dev', 'githubanalysis'): _anyPackageChecks,
p.join('licenses'): _anyPackageChecks,
p.join('licenses'): _anyPackageChecks,
p.join('packages', 'buildbucket-dart'): _anyPackageChecks,
p.join('packages', 'cocoon_common'): _anyPackageChecks,
p.join('packages', 'cocoon_common_test'): _anyPackageChecks,
p.join('packages', 'cocoon_server'): _anyPackageChecks,
p.join('packages', 'cocoon_server_test'): _anyPackageChecks,
p.join('test_utilities'): _anyPackageChecks,
p.join('tooling'): _anyPackageChecks,
};
void main(List<String> args) async {
final parsed = _parser.parse(args);
final cocoon = parsed.option('repository-root');
if (cocoon == null) {
io.stderr.writeln('Could not find the repository root');
io.exitCode = 1;
return;
}
final verbose = parsed.flag('verbose');
final quiet = parsed.flag('quiet') && !verbose;
const fs = LocalFileSystem();
for (final MapEntry(key: package, value: checks) in _checks.entries) {
for (final check in checks) {
if (!quiet) {
io.stderr.writeln('Running ${check.runtimeType} on $package...');
}
// Look at tracked files using git ls-files.
final gitLsFiles = io.Process.runSync('git', ['-C', package, 'ls-files']);
if (gitLsFiles.exitCode != 0) {
io.stderr.writeln(gitLsFiles.stderr);
io.exitCode = 1;
return;
}
final trackedFiles = (gitLsFiles.stdout as String)
.split('\n')
.map((p) => fs.file(fs.path.join(package, p)));
for (final file in trackedFiles) {
if (!check.include.any((g) => g.matches(file.path))) {
continue;
}
// Exclude the directory (package) itself.
if (file.path == package) {
continue;
}
final relative = p.relative(file.path, from: cocoon);
if (check.exclude.any((g) => g.matches(relative))) {
if (verbose) {
io.stderr.writeln('${check.runtimeType} $relative: Skip');
}
continue;
}
final logger = BufferedLogger();
final result = await check.check(logger, file);
if (result == CheckResult.failed) {
io.exitCode = 1;
for (final message in logger.messages) {
io.stderr.writeln(
'${message.severity.name}: ${check.runtimeType}\n'
'$relative\n'
'${message.message}\n',
);
}
} else if (verbose) {
io.stderr.writeln('${check.runtimeType} $relative: Pass');
}
}
}
}
io.stderr.writeln('Done!');
}
String? _findRepositoryRoot() {
var current = io.Directory.current;
while (true) {
final pubspec = io.File(p.join(current.path, 'pubspec.yaml'));
if (pubspec.existsSync()) {
final contents = pubspec.readAsStringSync();
if (contents.contains('name: _cocoon_workspace')) {
return current.path;
}
}
if (p.equals(current.path, current.parent.path)) {
return null;
}
current = current.parent;
}
}