| // 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; |
| } |
| } |