diff --git a/dfat/bin/src/commands/aggregate.dart b/dfat/bin/src/commands/aggregate.dart index c96a3cf..b7aae8f 100644 --- a/dfat/bin/src/commands/aggregate.dart +++ b/dfat/bin/src/commands/aggregate.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'dart:convert'; -import 'package:tint/tint.dart'; import 'package:path/path.dart' as path; import 'package:collection/collection.dart'; @@ -18,6 +17,9 @@ class AggregateCommand extends DfatCommand { @override String get category => 'Granular'; + @override + List revealTasks() => []; + final _lambdaZipMatcher = RegExp(r"^.*/\.dist/.*\.zip$", caseSensitive: false, dotAll: true); diff --git a/dfat/bin/src/commands/base.dart b/dfat/bin/src/commands/base.dart index 42fb5c3..1069918 100644 --- a/dfat/bin/src/commands/base.dart +++ b/dfat/bin/src/commands/base.dart @@ -1,5 +1,4 @@ import 'package:args/command_runner.dart'; -import 'package:meta/meta.dart'; import 'tasks/base.dart'; export '../enums.dart'; @@ -9,16 +8,19 @@ typedef ArgsProvider = Map? Function(String taskName); abstract class DfatCommand extends Command { DfatCommand({required this.tools, required this.logger}); + /// Private storage for the sequence + List _sequence = []; + /// A logger instance final Logger logger; /// The external tools (commands and exes) required by the command final List tools; - /// The sequence of tasks run by this command - @nonVirtual - List get sequence => _sequence; - List _sequence = []; + /// Reveals all tasks potentially used by the command. + List revealTasks(); + + /// Sets the sequence of tasks run by this command void useSequence(List sequence) { _sequence = sequence; } @@ -28,12 +30,14 @@ abstract class DfatCommand extends Command { (runner?.commands.values ?? []).whereType().toList(); /// All [tools] from this commands [sequence] - List get allTools => - [...tools, ...sequence.map((e) => e.requirements.tools).expand((e) => e)]; + List get allTools => [ + ...tools, + ...revealTasks().map((e) => e.requirements.tools).expand((e) => e) + ]; /// All [TaskRequirements.files] from this commands [sequence] List get allFsPaths => - [...sequence.map((e) => e.requirements.files).expand((e) => e)]; + [...revealTasks().map((e) => e.requirements.files).expand((e) => e)]; /// A sorted, distinct list of [allTools] from [allCommands] List get globalTools => @@ -50,7 +54,7 @@ abstract class DfatCommand extends Command { [Map> args = const {}]) async { bool result = true; final _def = {}; - for (var task in sequence) { + for (var task in _sequence) { if (!result) { break; } else { @@ -65,7 +69,7 @@ abstract class DfatCommand extends Command { Future runSequenceSame(ArgsProvider provider) async { bool result = true; final _def = {}; - for (var task in sequence) { + for (var task in _sequence) { if (!result) { break; } else { diff --git a/dfat/bin/src/commands/build.dart b/dfat/bin/src/commands/build.dart index f3c122c..655949c 100644 --- a/dfat/bin/src/commands/build.dart +++ b/dfat/bin/src/commands/build.dart @@ -15,15 +15,18 @@ class BuildCommand extends DfatCommand { String get category => 'General'; @override - ArgParser get argParser => ArgParser(allowTrailingOptions: true); + ArgParser get argParser => ArgParser.allowAnything(); // Pass-thru to sequence + + @override + List revealTasks() => []; BuildCommand(Logger logger) : super(logger: logger, tools: []); @override Future run() async { final runner = super.runner!; - final dockerSeq = ['docker']; - final buildSeq = ['check', 'validate', 'shared', 'lambda', 'aggregate']; + final dockerSeq = ['check', 'validate', 'shared', 'docker', 'aggregate']; + final buildSeq = ['check', 'lambda']; final execSeq = (Utils.isInDocker ? buildSeq : dockerSeq); final baseArgs = (argResults?.arguments ?? []).where((a) => a != name); diff --git a/dfat/bin/src/commands/check.dart b/dfat/bin/src/commands/check.dart index 72403e8..6c597e9 100644 --- a/dfat/bin/src/commands/check.dart +++ b/dfat/bin/src/commands/check.dart @@ -34,6 +34,12 @@ class CheckCommand extends DfatCommand { final String inBl = ' '; + @override + List revealTasks() => [ + CheckToolsTask(this, logger), + CheckFSTask(this, logger), + ]; + @override Future run() async { final blockCloser = logger.header('Checks'); diff --git a/dfat/bin/src/commands/docker.dart b/dfat/bin/src/commands/docker.dart index 1c23709..0baf675 100644 --- a/dfat/bin/src/commands/docker.dart +++ b/dfat/bin/src/commands/docker.dart @@ -15,11 +15,6 @@ class DockerCommand extends DfatCommand { @override String get category => 'Granular'; - @override - List get sequence => - _sequence ?? [DockerBuildTask(this, logger), DockerRunTask(this, logger)]; - List? _sequence; - DockerCommand(Logger logger) : super(logger: logger, tools: Utils.isInDocker ? [] : ['docker']) { var workDir = Directory.current.path; @@ -52,6 +47,12 @@ class DockerCommand extends DfatCommand { ); } + @override + List revealTasks() => [ + DockerBuildTask(this, logger), + DockerRunTask(this, logger), + ]; + @override Future run() async { final args = argResults!; @@ -61,14 +62,15 @@ class DockerCommand extends DfatCommand { final String imageNameFallback = "${path.basename(rootDir)}-builder"; final String imageName = args['name'] ?? imageNameFallback; - _sequence = []; + var _sequence = []; bool hasImage = Utils.dockerImageExists(imageName); if (!hasImage || buildOnly || useForce) { - _sequence!.add(DockerBuildTask(this, logger)); + _sequence.add(DockerBuildTask(this, logger)); } if (!buildOnly) { - _sequence!.add(DockerRunTask(this, logger)); + _sequence.add(DockerRunTask(this, logger)); } + useSequence(_sequence); bool result = await runSequence({ DockerRunTask.taskName: {'name': imageName}, DockerBuildTask.taskName: {'name': imageName}, diff --git a/dfat/bin/src/commands/install.dart b/dfat/bin/src/commands/install.dart index 576b376..3376690 100644 --- a/dfat/bin/src/commands/install.dart +++ b/dfat/bin/src/commands/install.dart @@ -26,6 +26,12 @@ class InstallCommand extends DfatCommand { ); } + @override + List revealTasks() => [ + InstallDfatFilesTask(this, logger), + UpdateSchemasTask(this, logger), + ]; + @override Future run() async { logger.header("Install"); diff --git a/dfat/bin/src/commands/lambda.dart b/dfat/bin/src/commands/lambda.dart index 88fc9b6..9426c41 100644 --- a/dfat/bin/src/commands/lambda.dart +++ b/dfat/bin/src/commands/lambda.dart @@ -25,6 +25,15 @@ class LambdaCommand extends DfatCommand { ); } + @override + List revealTasks() => [ + CleanDirTask(this, logger), + PubGetTask(this, logger), + DartTestTask(this, logger), + DartCompileTask(this, logger), + ZipArchiveTask(this, logger) + ]; + @override Future run() async { final footer = logger.header("Lambdas"); diff --git a/dfat/bin/src/commands/shared.dart b/dfat/bin/src/commands/shared.dart index 59e4038..5716b5f 100644 --- a/dfat/bin/src/commands/shared.dart +++ b/dfat/bin/src/commands/shared.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:path/path.dart' as path; -import 'all.dart'; import 'tasks/all.dart'; class SharedCommand extends DfatCommand { @@ -28,6 +27,15 @@ class SharedCommand extends DfatCommand { ); } + @override + List revealTasks() => [ + CleanDirTask(this, logger), + PubGetTask(this, logger), + BuildRunnerCleanTask(this, logger), + BuildRunnerBuildTask(this, logger), + DartTestTask(this, logger) + ]; + @override Future run() async { logger.header("Shared"); @@ -39,13 +47,13 @@ class SharedCommand extends DfatCommand { useSequence([ CleanDirTask(this, logger), PubGetTask(this, logger), - DartTestTask(this, logger), ...(isNoCacheSet ? [ BuildRunnerCleanTask(this, logger), BuildRunnerBuildTask(this, logger) ] - : [BuildRunnerBuildTask(this, logger)]) + : [BuildRunnerBuildTask(this, logger)]), + DartTestTask(this, logger), ]); final result = await runSequence({ diff --git a/dfat/bin/src/commands/tasks/dart_compile.dart b/dfat/bin/src/commands/tasks/dart_compile.dart index d4a172d..a84a7bd 100644 --- a/dfat/bin/src/commands/tasks/dart_compile.dart +++ b/dfat/bin/src/commands/tasks/dart_compile.dart @@ -30,7 +30,7 @@ class DartCompileTask extends TaskCommand { '๐Ÿ’ช Compiling ${baseName.green()} โ†’ ${outputName?.green()}', ind); if (!Directory(distPath).existsSync()) Directory(distPath).createSync(); - final dartArgs = ['compile', 'exe', 'main.dart', '-o', relOutPath]; + final dartArgs = ['compile', 'exe', 'lib/main.dart', '-o', relOutPath]; final pRes = Process.runSync('dart', dartArgs, workingDirectory: dirPath); return Utils.handleProcessResult(pRes, logger, ' ', (code) { diff --git a/dfat/bin/src/commands/tasks/dart_test.dart b/dfat/bin/src/commands/tasks/dart_test.dart index ee0b313..40ca027 100644 --- a/dfat/bin/src/commands/tasks/dart_test.dart +++ b/dfat/bin/src/commands/tasks/dart_test.dart @@ -6,7 +6,18 @@ import 'base.dart'; class DartTestTask extends TaskCommand { DartTestTask(DfatCommand parent, Logger logger) - : super(parent, logger, TaskRequirements()); + : super( + parent, + logger, + TaskRequirements( + tools: [ + 'dart', + 'collect_coverage', + 'format_coverage', + 'cobertura', + 'genhtml' + ], + )); static String taskName = 'dart-test'; @@ -18,6 +29,95 @@ class DartTestTask extends TaskCommand { final inRs = ' '; + bool _runTests(bool useCoverage, String dirPath, String relOutPath) { + final usesCover = useCoverage && Utils.isCommand('collect_coverage'); + + final List dartArgs = [ + 'run', + 'test', + '--chain-stack-traces', + ...(usesCover ? ['--coverage=coverage'] : []), + ]; + final tRes = Process.runSync('dart', dartArgs, workingDirectory: dirPath); + + return Utils.handleProcessResult(tRes, logger, inRs + inRs); + } + + bool _formatLcov( + bool useCoverage, + String baseName, + String ind, + String dirPath, + ) { + final usesCover = useCoverage && Utils.isCommand('format_coverage'); + + // Early bail-out if there's nothing we can do here + if (!usesCover) return true; + + logger.printFixed('๐Ÿงช Formatting ${baseName.green()} coverage', ind); + final List formatArgs = [ + '--packages=.packages', + '--base-directory=${path.normalize(dirPath)}', + '--report-on=lib', + '--lcov', + '-o', + 'coverage/lcov.info', + '-i', + 'coverage', + ]; + final fRes = Process.runSync( + 'format_coverage', + formatArgs, + workingDirectory: dirPath, + ); + return Utils.handleProcessResult( + fRes, + logger, + inRs + inRs, + null, + _makeBadge(dirPath).trim(), + ); + } + + bool _formatHtml( + bool useCoverage, String baseName, String ind, String dirPath) { + // Early bail-out if there's nothing we can do here + final hasGenHtml = Utils.isCommand('genhtml'); + if (hasGenHtml) { + logger.printFixed('๐Ÿงช Marking up ${baseName.green()} coverage', ind); + final List markupArgs = [ + '-o', + './coverage/report', + './coverage/lcov.info', + ]; + final gRes = + Process.runSync('genhtml', markupArgs, workingDirectory: dirPath); + return Utils.handleProcessResult( + gRes, + logger, + inRs + inRs, + null, + './$baseName/coverage/report/index.html', + ); + } + return true; + } + + String _makeBadge(String dirPath) { + final List badgeArgs = [ + 'pub', + 'global', + 'run', + 'cobertura', + 'show', + '-b', + '-i', + './coverage/lcov.info', + ]; + final bRes = Process.runSync('dart', badgeArgs, workingDirectory: dirPath); + return bRes.stdout; + } + @override Future run() async { final dirPath = targetDir; @@ -44,23 +144,10 @@ class DartTestTask extends TaskCommand { final useCoverage = args['coverage'] ?? true; final relOutPath = '.dist/$outputName'; - final usesCover = useCoverage && - (Utils.getPubSpecValue(targetDir, 'dev_dependencies.coverage') ?? - Utils.getPubSpecValue(targetDir, 'dependencies.coverage')) != - null; - - final List dartArgs = [ - 'run', - 'test', - '--chain-stack-traces', - ...(usesCover ? ['--coverage="coverage"'] : []), - ]; - final pRes = Process.runSync('dart', dartArgs, workingDirectory: dirPath); + bool result = _runTests(useCoverage, dirPath, relOutPath); + if (result) result = _formatLcov(useCoverage, baseName, ind, dirPath); + if (result) result = _formatHtml(useCoverage, baseName, ind, dirPath); - return Utils.handleProcessResult(pRes, logger, ' ', (code) { - final outputFile = File(relOutPath); - final success = code == 0 && outputFile.existsSync(); - if (success) Utils.chmod('+x', relOutPath); - }); + return result; } } diff --git a/dfat/bin/src/commands/test.dart b/dfat/bin/src/commands/test.dart index 46771f0..e692014 100644 --- a/dfat/bin/src/commands/test.dart +++ b/dfat/bin/src/commands/test.dart @@ -1,7 +1,6 @@ import 'dart:collection'; import 'dart:io'; -import 'package:args/args.dart'; import 'package:path/path.dart' as path; import 'tasks/all.dart'; @@ -29,7 +28,7 @@ class TestCommand extends DfatCommand { ) ..addOption('style', abbr: 's', - allowed: ['gcov', 'lcov', 'cobertura'], + allowed: ['gcov', 'lcov'], defaultsTo: 'gcov', help: "If coverage is used, determines the output type.") ..addFlag( @@ -40,6 +39,9 @@ class TestCommand extends DfatCommand { ); } + @override + List revealTasks() => [DartTestTask(this, logger)]; + @override Future run() async { final closer = logger.header("Test"); diff --git a/dfat/bin/src/commands/validate.dart b/dfat/bin/src/commands/validate.dart index b09f3bf..03b3920 100644 --- a/dfat/bin/src/commands/validate.dart +++ b/dfat/bin/src/commands/validate.dart @@ -26,6 +26,9 @@ class ValidateCommand extends DfatCommand { ); } + @override + List revealTasks() => [ValidateJsonTask(this, logger)]; + @override Future run() async { final footer = logger.header("Validate"); diff --git a/dfat/bin/src/logger.dart b/dfat/bin/src/logger.dart index 6c49119..fbdab42 100644 --- a/dfat/bin/src/logger.dart +++ b/dfat/bin/src/logger.dart @@ -14,8 +14,8 @@ class Logger { return length > _padding.length ? '' : _padding.substring(length); } - void printDone() { - printRaw("โœ…\n"); + void printDone([String message = '']) { + printRaw("โœ…${message.isEmpty ? '' : ' $message'}\n"); } void printFailed([String? reason = '', String indent = '']) { @@ -77,8 +77,9 @@ class Logger { void printPassThru(String message, [String indent = '']) { if (message.isEmpty) return; + final lfp = RegExp(r'\n|\r'); print(indent + - message.trim().split('\n').map((s) => s.trim()).join('\n$indent')); + message.trim().split(lfp).map((s) => s.trim()).join('\n$indent')); } SuccessClosure printBlock(String message, [String indent = '']) { diff --git a/dfat/bin/src/utils.dart b/dfat/bin/src/utils.dart index f803894..1a96816 100644 --- a/dfat/bin/src/utils.dart +++ b/dfat/bin/src/utils.dart @@ -159,9 +159,10 @@ class Utils { Logger logger, [ String indent = '', ProcessFinalizer? finalizer, + String message = '', ]) { if (result.exitCode == 0) { - logger.printDone(); + logger.printDone(message); } else { logger.printFailed('code ${result.exitCode}');