From 86e6594641ae835e309d6c09b24a2b39559b0cf3 Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Fri, 12 Jan 2024 16:02:08 +0100 Subject: [PATCH 1/6] add!: proper sub commands for bump and log commands --- lib/src/cli/command/bump_command.dart | 65 ++++++++++++++--------- lib/src/cli/command/bump_sub_command.dart | 41 ++++++++++++++ lib/src/cli/command/log_command.dart | 35 ++++++++++-- lib/src/cli/command/log_sub_command.dart | 28 ++++++++++ test/functional_test.dart | 5 +- 5 files changed, 144 insertions(+), 30 deletions(-) create mode 100644 lib/src/cli/command/bump_sub_command.dart create mode 100644 lib/src/cli/command/log_sub_command.dart diff --git a/lib/src/cli/command/bump_command.dart b/lib/src/cli/command/bump_command.dart index a6e06b6..5784d4d 100644 --- a/lib/src/cli/command/bump_command.dart +++ b/lib/src/cli/command/bump_command.dart @@ -1,27 +1,49 @@ +import 'package:args/command_runner.dart'; import 'package:cider/src/project.dart'; +import 'package:cider/src/cli/command/bump_sub_command.dart'; import 'package:cider/src/cli/command/cider_command.dart'; import 'package:version_manipulation/mutations.dart'; +enum BumpType { + breaking, + major, + minor, + patch, + build, + pre, + release, +} + class BumpCommand extends CiderCommand { BumpCommand(super.printer) { - mutations.keys.forEach(argParser.addCommand); - argParser - ..addFlag('keep-build', help: 'Keep the existing build') - ..addFlag('bump-build', help: 'Also bump the build') - ..addOption('build', - help: 'Sets the build to the given value', defaultsTo: '') - ..addOption('pre', - help: 'Sets the pre-release to the given value', defaultsTo: ''); + for (final type in BumpType.values) { + addSubcommand(BumpSubCommand( + type.name, + description: _subcommandDescriptions[type]!, + mutation: _mutations[type]!, + printer: printer, + )); + } } - static const mutations = { - 'breaking': BumpBreaking(), - 'build': BumpBuild(), - 'major': BumpMajor(), - 'minor': BumpMinor(), - 'patch': BumpPatch(), - 'pre': BumpPreRelease(), - 'release': Release(), + static const _mutations = { + BumpType.breaking: BumpBreaking(), + BumpType.major: BumpMajor(), + BumpType.minor: BumpMinor(), + BumpType.patch: BumpPatch(), + BumpType.build: BumpBuild(), + BumpType.pre: BumpPreRelease(), + BumpType.release: Release(), + }; + + static const _subcommandDescriptions = { + BumpType.breaking: 'Bump the breaking version', + BumpType.major: 'Bump the major version', + BumpType.minor: 'Bump the minor version', + BumpType.patch: 'Bump the patch version', + BumpType.build: 'Bump the build version', + BumpType.pre: 'Bump the pre-release version', + BumpType.release: 'Bump the release version', }; @override @@ -31,14 +53,7 @@ class BumpCommand extends CiderCommand { @override Future exec(Project project) async { - final part = argResults!.command?.name ?? - (throw ArgumentError('Version part must be specified')); - final result = await project.bumpVersion(mutations[part]!, - keepBuild: argResults!['keep-build'], - bumpBuild: argResults!['bump-build'], - build: argResults!['build'], - pre: argResults!['pre']); - printer.out.writeln(result); - return 0; + throw UsageException( + 'Bump command can only be used with subcommands', usage); } } diff --git a/lib/src/cli/command/bump_sub_command.dart b/lib/src/cli/command/bump_sub_command.dart new file mode 100644 index 0000000..939cf71 --- /dev/null +++ b/lib/src/cli/command/bump_sub_command.dart @@ -0,0 +1,41 @@ +import 'package:cider/src/cli/console.dart'; +import 'package:cider/src/project.dart'; +import 'package:cider/src/cli/command/cider_command.dart'; +import 'package:version_manipulation/mutations.dart'; + +class BumpSubCommand extends CiderCommand { + BumpSubCommand( + this.name, { + required this.description, + required this.mutation, + required Console printer, + }) : super(printer) { + argParser + ..addFlag('keep-build', help: 'Keep the existing build') + ..addFlag('bump-build', help: 'Also bump the build') + ..addOption('build', + help: 'Sets the build to the given value', defaultsTo: '') + ..addOption('pre', + help: 'Sets the pre-release to the given value', defaultsTo: ''); + } + + @override + final String name; + + @override + final String description; + + final VersionMutation mutation; + + @override + Future exec(Project project) async { + final result = await project.bumpVersion(mutation, + keepBuild: argResults!['keep-build'], + bumpBuild: argResults!['bump-build'], + build: argResults!['build'], + pre: argResults!['pre']); + printer.out.writeln(result); + + return 0; + } +} diff --git a/lib/src/cli/command/log_command.dart b/lib/src/cli/command/log_command.dart index 13ed894..06bc899 100644 --- a/lib/src/cli/command/log_command.dart +++ b/lib/src/cli/command/log_command.dart @@ -1,8 +1,37 @@ +import 'package:args/command_runner.dart'; import 'package:cider/src/cli/command/cider_command.dart'; +import 'package:cider/src/cli/command/log_sub_command.dart'; import 'package:cider/src/project.dart'; +enum LogType { + add, + fix, + change, + deprecate, + remove, + security, +} + class LogCommand extends CiderCommand { - LogCommand(super.printer); + LogCommand(super.printer) { + for (final type in LogType.values) { + addSubcommand(LogSubCommand( + type.name, + type: type, + description: _subcommandDescriptions[type]!, + printer: printer, + )); + } + } + + static const _subcommandDescriptions = { + LogType.add: 'Add a new feature to the changelog', + LogType.fix: 'Add a new bug fix to the changelog', + LogType.change: 'Add a new change to the changelog', + LogType.deprecate: 'Add a new deprecation to the changelog', + LogType.remove: 'Add a new removal to the changelog', + LogType.security: 'Add a new security fix to the changelog', + }; @override final name = 'log'; @@ -12,7 +41,7 @@ class LogCommand extends CiderCommand { @override Future exec(Project project) async { - await project.addUnreleased(argResults!.rest.first, argResults!.rest[1]); - return 0; + throw UsageException( + 'Log command can only be used with subcommands', usage); } } diff --git a/lib/src/cli/command/log_sub_command.dart b/lib/src/cli/command/log_sub_command.dart new file mode 100644 index 0000000..ab53175 --- /dev/null +++ b/lib/src/cli/command/log_sub_command.dart @@ -0,0 +1,28 @@ +import 'package:cider/src/cli/command/log_command.dart'; +import 'package:cider/src/cli/console.dart'; +import 'package:cider/src/project.dart'; +import 'package:cider/src/cli/command/cider_command.dart'; + +class LogSubCommand extends CiderCommand { + LogSubCommand( + this.name, { + required this.description, + required this.type, + required Console printer, + }) : super(printer); + + @override + final String name; + + @override + final String description; + + final LogType type; + + @override + Future exec(Project project) async { + await project.addUnreleased(type.name, argResults!.rest[0]); + + return 0; + } +} diff --git a/test/functional_test.dart b/test/functional_test.dart index 9a374e9..268accc 100644 --- a/test/functional_test.dart +++ b/test/functional_test.dart @@ -234,8 +234,9 @@ I love my dog. }); test('version part must be specified', () async { final code = await run(['bump']); - expect(code, 65); - expect(err.buffer.toString().trim(), 'Version part must be specified'); + expect(code, 64); + expect(err.buffer.toString().trim().split('\n')[0], + 'Usage: cider bump [arguments]'); }); }); }); From 379fb6cd15048adb3a218a982ee0054eb0c8db20 Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Tue, 30 Jan 2024 08:32:55 +0100 Subject: [PATCH 2/6] refactor: move sub-command props into enum --- lib/src/cli/command/bump_command.dart | 46 ++++++++++----------------- lib/src/cli/command/log_command.dart | 29 ++++++++--------- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/lib/src/cli/command/bump_command.dart b/lib/src/cli/command/bump_command.dart index 5784d4d..ed4a913 100644 --- a/lib/src/cli/command/bump_command.dart +++ b/lib/src/cli/command/bump_command.dart @@ -5,13 +5,21 @@ import 'package:cider/src/cli/command/cider_command.dart'; import 'package:version_manipulation/mutations.dart'; enum BumpType { - breaking, - major, - minor, - patch, - build, - pre, - release, + breaking(mutation: BumpBreaking(), description: 'Bump the breaking version'), + major(mutation: BumpMajor(), description: 'Bump the major version'), + minor(mutation: BumpMinor(), description: 'Bump the minor version'), + patch(mutation: BumpPatch(), description: 'Bump the patch version'), + build(mutation: BumpBuild(), description: 'Bump the build version'), + pre(mutation: BumpPreRelease(), description: 'Bump the pre-release version'), + release(mutation: Release(), description: 'Bump the release version'); + + const BumpType({ + required this.mutation, + required this.description, + }); + + final VersionMutation mutation; + final String description; } class BumpCommand extends CiderCommand { @@ -19,33 +27,13 @@ class BumpCommand extends CiderCommand { for (final type in BumpType.values) { addSubcommand(BumpSubCommand( type.name, - description: _subcommandDescriptions[type]!, - mutation: _mutations[type]!, + description: type.description, + mutation: type.mutation, printer: printer, )); } } - static const _mutations = { - BumpType.breaking: BumpBreaking(), - BumpType.major: BumpMajor(), - BumpType.minor: BumpMinor(), - BumpType.patch: BumpPatch(), - BumpType.build: BumpBuild(), - BumpType.pre: BumpPreRelease(), - BumpType.release: Release(), - }; - - static const _subcommandDescriptions = { - BumpType.breaking: 'Bump the breaking version', - BumpType.major: 'Bump the major version', - BumpType.minor: 'Bump the minor version', - BumpType.patch: 'Bump the patch version', - BumpType.build: 'Bump the build version', - BumpType.pre: 'Bump the pre-release version', - BumpType.release: 'Bump the release version', - }; - @override final name = 'bump'; @override diff --git a/lib/src/cli/command/log_command.dart b/lib/src/cli/command/log_command.dart index 06bc899..f1d0212 100644 --- a/lib/src/cli/command/log_command.dart +++ b/lib/src/cli/command/log_command.dart @@ -4,12 +4,18 @@ import 'package:cider/src/cli/command/log_sub_command.dart'; import 'package:cider/src/project.dart'; enum LogType { - add, - fix, - change, - deprecate, - remove, - security, + add(description: 'Add a new feature to the changelog'), + fix(description: 'Add a new bug fix to the changelog'), + change(description: 'Add a new change to the changelog'), + deprecate(description: 'Add a new deprecation to the changelog'), + remove(description: 'Add a new removal to the changelog'), + security(description: 'Add a new security fix to the changelog'); + + const LogType({ + required this.description, + }); + + final String description; } class LogCommand extends CiderCommand { @@ -18,21 +24,12 @@ class LogCommand extends CiderCommand { addSubcommand(LogSubCommand( type.name, type: type, - description: _subcommandDescriptions[type]!, + description: type.description, printer: printer, )); } } - static const _subcommandDescriptions = { - LogType.add: 'Add a new feature to the changelog', - LogType.fix: 'Add a new bug fix to the changelog', - LogType.change: 'Add a new change to the changelog', - LogType.deprecate: 'Add a new deprecation to the changelog', - LogType.remove: 'Add a new removal to the changelog', - LogType.security: 'Add a new security fix to the changelog', - }; - @override final name = 'log'; @override From d1eb32cdd0e1dddc6dd503548bd1f5ff756a5a75 Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Tue, 30 Jan 2024 09:17:05 +0100 Subject: [PATCH 3/6] fix: use `Console` out channel to print usage instead of `print` --- lib/src/cli/cider_cli.dart | 3 +++ lib/src/cli/command/cider_command.dart | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/src/cli/cider_cli.dart b/lib/src/cli/cider_cli.dart index 6186936..6dd81e7 100644 --- a/lib/src/cli/cider_cli.dart +++ b/lib/src/cli/cider_cli.dart @@ -33,4 +33,7 @@ class CiderCli extends CommandRunner { @override Future run(Iterable args) => ErrorInterceptor(console).run(() => super.run(args)); + + @override + void printUsage() => console.out.writeln(usage); } diff --git a/lib/src/cli/command/cider_command.dart b/lib/src/cli/command/cider_command.dart index 158ed37..475a856 100644 --- a/lib/src/cli/command/cider_command.dart +++ b/lib/src/cli/command/cider_command.dart @@ -39,6 +39,9 @@ abstract class CiderCommand extends Command { tagTemplate: tagTemplate, keepEmptyUnreleased: keepEmptyUnreleased); } + + @override + printUsage() => printer.out.writeln(usage); } extension _Map on Map { From 94fd222e3375c2ea61f2d81bca08836f11a5cdf4 Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Tue, 30 Jan 2024 09:23:29 +0100 Subject: [PATCH 4/6] test: `bump` and `log` usage output test --- test/functional_test.dart | 25 ++++++++++++++++++++++++- test/subcommands_usage.expect.dart | 10 ++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/subcommands_usage.expect.dart diff --git a/test/functional_test.dart b/test/functional_test.dart index 268accc..e35fc82 100644 --- a/test/functional_test.dart +++ b/test/functional_test.dart @@ -7,6 +7,8 @@ import 'package:path/path.dart' as path; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; +import 'subcommands_usage.expect.dart'; + void main() { late Directory temp; final out = BufferChannel(); @@ -232,12 +234,33 @@ I love my dog. expect(err.buffer.toString().trim(), 'The next version must be higher than the current one.'); }); - test('version part must be specified', () async { + test('incorrect usage', () async { final code = await run(['bump']); expect(code, 64); expect(err.buffer.toString().trim().split('\n')[0], 'Usage: cider bump [arguments]'); }); + test('help usage', () async { + final code = await run(['bump', '--help']); + expect(code, 0); + expect(err.buffer.toString(), isEmpty); + expectSubcommandsUsage(out.buffer.toString(), command: 'bump'); + }); + }); + }); + + group('Log', () { + test('incorrect usage', () async { + final code = await run(['log']); + expect(code, 64); + expect(err.buffer.toString().trim().split('\n')[0], + 'Usage: cider log [arguments]'); + }); + test('help usage', () async { + final code = await run(['log', '--help']); + expect(code, 0); + expect(err.buffer.toString(), isEmpty); + expectSubcommandsUsage(out.buffer.toString(), command: 'log'); }); }); diff --git a/test/subcommands_usage.expect.dart b/test/subcommands_usage.expect.dart new file mode 100644 index 0000000..3e0cdd1 --- /dev/null +++ b/test/subcommands_usage.expect.dart @@ -0,0 +1,10 @@ +import 'package:test/test.dart'; + +void expectSubcommandsUsage(String output, {String? command}) { + if (command != null) { + expect(output, contains('Usage: cider $command [arguments]')); + } + expect(output, contains('-h, --help Print this usage information.')); + expect(output, contains('Available subcommands:')); + expect(output, contains('Run "cider help" to see global options.')); +} From 47dc736cce70be8f8e7ee7175f3bbafb8efa5ec7 Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Wed, 31 Jan 2024 02:36:37 +0100 Subject: [PATCH 5/6] chore: use `first` instead of `[0]` --- lib/src/cli/command/log_sub_command.dart | 2 +- test/functional_test.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/cli/command/log_sub_command.dart b/lib/src/cli/command/log_sub_command.dart index ab53175..6645bf6 100644 --- a/lib/src/cli/command/log_sub_command.dart +++ b/lib/src/cli/command/log_sub_command.dart @@ -21,7 +21,7 @@ class LogSubCommand extends CiderCommand { @override Future exec(Project project) async { - await project.addUnreleased(type.name, argResults!.rest[0]); + await project.addUnreleased(type.name, argResults!.rest.first); return 0; } diff --git a/test/functional_test.dart b/test/functional_test.dart index e35fc82..7174222 100644 --- a/test/functional_test.dart +++ b/test/functional_test.dart @@ -237,7 +237,7 @@ I love my dog. test('incorrect usage', () async { final code = await run(['bump']); expect(code, 64); - expect(err.buffer.toString().trim().split('\n')[0], + expect(err.buffer.toString().trim().split('\n').first, 'Usage: cider bump [arguments]'); }); test('help usage', () async { @@ -253,7 +253,7 @@ I love my dog. test('incorrect usage', () async { final code = await run(['log']); expect(code, 64); - expect(err.buffer.toString().trim().split('\n')[0], + expect(err.buffer.toString().trim().split('\n').first, 'Usage: cider log [arguments]'); }); test('help usage', () async { From c78bc1878254b046d80ff3c8ee043aeafdb775c3 Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Wed, 31 Jan 2024 02:44:41 +0100 Subject: [PATCH 6/6] chore: use positional args instead of named ones --- lib/src/cli/command/bump_command.dart | 28 +++++++++-------------- lib/src/cli/command/bump_sub_command.dart | 8 ++----- lib/src/cli/command/log_command.dart | 25 ++++++++------------ lib/src/cli/command/log_sub_command.dart | 8 ++----- 4 files changed, 25 insertions(+), 44 deletions(-) diff --git a/lib/src/cli/command/bump_command.dart b/lib/src/cli/command/bump_command.dart index ed4a913..fc9e196 100644 --- a/lib/src/cli/command/bump_command.dart +++ b/lib/src/cli/command/bump_command.dart @@ -5,18 +5,15 @@ import 'package:cider/src/cli/command/cider_command.dart'; import 'package:version_manipulation/mutations.dart'; enum BumpType { - breaking(mutation: BumpBreaking(), description: 'Bump the breaking version'), - major(mutation: BumpMajor(), description: 'Bump the major version'), - minor(mutation: BumpMinor(), description: 'Bump the minor version'), - patch(mutation: BumpPatch(), description: 'Bump the patch version'), - build(mutation: BumpBuild(), description: 'Bump the build version'), - pre(mutation: BumpPreRelease(), description: 'Bump the pre-release version'), - release(mutation: Release(), description: 'Bump the release version'); + breaking(BumpBreaking(), 'Bump the breaking version'), + major(BumpMajor(), 'Bump the major version'), + minor(BumpMinor(), 'Bump the minor version'), + patch(BumpPatch(), 'Bump the patch version'), + build(BumpBuild(), 'Bump the build version'), + pre(BumpPreRelease(), 'Bump the pre-release version'), + release(Release(), 'Bump the release version'); - const BumpType({ - required this.mutation, - required this.description, - }); + const BumpType(this.mutation, this.description); final VersionMutation mutation; final String description; @@ -25,12 +22,9 @@ enum BumpType { class BumpCommand extends CiderCommand { BumpCommand(super.printer) { for (final type in BumpType.values) { - addSubcommand(BumpSubCommand( - type.name, - description: type.description, - mutation: type.mutation, - printer: printer, - )); + addSubcommand( + BumpSubCommand(type.name, type.description, type.mutation, printer), + ); } } diff --git a/lib/src/cli/command/bump_sub_command.dart b/lib/src/cli/command/bump_sub_command.dart index 939cf71..6058ef3 100644 --- a/lib/src/cli/command/bump_sub_command.dart +++ b/lib/src/cli/command/bump_sub_command.dart @@ -4,12 +4,8 @@ import 'package:cider/src/cli/command/cider_command.dart'; import 'package:version_manipulation/mutations.dart'; class BumpSubCommand extends CiderCommand { - BumpSubCommand( - this.name, { - required this.description, - required this.mutation, - required Console printer, - }) : super(printer) { + BumpSubCommand(this.name, this.description, this.mutation, Console printer) + : super(printer) { argParser ..addFlag('keep-build', help: 'Keep the existing build') ..addFlag('bump-build', help: 'Also bump the build') diff --git a/lib/src/cli/command/log_command.dart b/lib/src/cli/command/log_command.dart index f1d0212..f0ace2d 100644 --- a/lib/src/cli/command/log_command.dart +++ b/lib/src/cli/command/log_command.dart @@ -4,16 +4,14 @@ import 'package:cider/src/cli/command/log_sub_command.dart'; import 'package:cider/src/project.dart'; enum LogType { - add(description: 'Add a new feature to the changelog'), - fix(description: 'Add a new bug fix to the changelog'), - change(description: 'Add a new change to the changelog'), - deprecate(description: 'Add a new deprecation to the changelog'), - remove(description: 'Add a new removal to the changelog'), - security(description: 'Add a new security fix to the changelog'); + fix('Add a new bug fix to the changelog'), + add('Add a new feature to the changelog'), + change('Add a new change to the changelog'), + deprecate('Add a new deprecation to the changelog'), + remove('Add a new removal to the changelog'), + security('Add a new security fix to the changelog'); - const LogType({ - required this.description, - }); + const LogType(this.description); final String description; } @@ -21,12 +19,9 @@ enum LogType { class LogCommand extends CiderCommand { LogCommand(super.printer) { for (final type in LogType.values) { - addSubcommand(LogSubCommand( - type.name, - type: type, - description: type.description, - printer: printer, - )); + addSubcommand( + LogSubCommand(type.name, type.description, type, printer), + ); } } diff --git a/lib/src/cli/command/log_sub_command.dart b/lib/src/cli/command/log_sub_command.dart index 6645bf6..a83a7e9 100644 --- a/lib/src/cli/command/log_sub_command.dart +++ b/lib/src/cli/command/log_sub_command.dart @@ -4,12 +4,8 @@ import 'package:cider/src/project.dart'; import 'package:cider/src/cli/command/cider_command.dart'; class LogSubCommand extends CiderCommand { - LogSubCommand( - this.name, { - required this.description, - required this.type, - required Console printer, - }) : super(printer); + LogSubCommand(this.name, this.description, this.type, Console printer) + : super(printer); @override final String name;