diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f6a0a8cbdfa..770deeba642 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -19,9 +19,9 @@ https://github.com/dotnet/runtime 37c0371ab3f7651d64a6dfe4c2e8677206fa34ee - + https://github.com/dotnet/command-line-api - 42c58533cdf478b15120542be76f7cbaacaab024 + 02fe27cd6a9b001c8feb7938e6ef4b3799745759 diff --git a/eng/Versions.props b/eng/Versions.props index ad9f30b5d04..6b133287029 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -16,7 +16,7 @@ - 2.0.0-beta4.23165.3 + 2.0.0-beta4.23307.1 8.0.0-preview.6.23309.7 8.0.0-preview.6.23309.7 diff --git a/test/Microsoft.TemplateEngine.Authoring.CLI.UnitTests/ValidateCommandTests.cs b/test/Microsoft.TemplateEngine.Authoring.CLI.UnitTests/ValidateCommandTests.cs index 7d17777be84..fbb400db5dc 100644 --- a/test/Microsoft.TemplateEngine.Authoring.CLI.UnitTests/ValidateCommandTests.cs +++ b/test/Microsoft.TemplateEngine.Authoring.CLI.UnitTests/ValidateCommandTests.cs @@ -12,7 +12,7 @@ public class ValidateCommandTests : TestBase [Fact] public async Task ValidateCommand_BasicTest_InvalidTemplate() { - RootCommand root = new() + CliRootCommand root = new() { new ValidateCommand() }; @@ -26,7 +26,7 @@ public async Task ValidateCommand_BasicTest_InvalidTemplate() [Fact] public async Task ValidateCommand_BasicTest_ValidTemplate() { - RootCommand root = new() + CliRootCommand root = new() { new ValidateCommand() }; diff --git a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/ExecutableCommand.cs b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/ExecutableCommand.cs index 20ca5f6851c..37ea2658d33 100644 --- a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/ExecutableCommand.cs +++ b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/ExecutableCommand.cs @@ -2,36 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; -using System.CommandLine.Invocation; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; namespace Microsoft.TemplateEngine.Authoring.CLI.Commands { /// - /// Represents a together with its handler. + /// Represents a together with its action. /// - internal abstract class ExecutableCommand : Command, ICommandHandler where TModel : class + internal abstract class ExecutableCommand : CliCommand where TModel : class { internal ExecutableCommand(string name, string? description = null) : base(name, description) { - Handler = this; + Action = new CommandAction(this); } - /// - public async Task InvokeAsync(InvocationContext context, CancellationToken cancellationToken = default) - { - using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddSimpleConsole(c => c.ColorBehavior = LoggerColorBehavior.Disabled)); - TModel arguments = ParseContext(context.ParseResult); - - //exceptions are handled by parser itself - return await ExecuteAsync(arguments, loggerFactory, cancellationToken).ConfigureAwait(false); - } - - /// - public int Invoke(InvocationContext context) => InvokeAsync(context).GetAwaiter().GetResult(); - /// /// Parses the context from . /// @@ -42,5 +28,22 @@ public async Task InvokeAsync(InvocationContext context, CancellationToken /// protected abstract Task ExecuteAsync(TModel args, ILoggerFactory loggerFactory, CancellationToken cancellationToken); + private sealed class CommandAction : CliAction + { + private readonly ExecutableCommand _command; + + public CommandAction(ExecutableCommand command) => _command = command; + + public override int Invoke(ParseResult parseResult) => InvokeAsync(parseResult).GetAwaiter().GetResult(); + + public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken = default) + { + using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddSimpleConsole(c => c.ColorBehavior = LoggerColorBehavior.Disabled)); + TModel arguments = _command.ParseContext(parseResult); + + //exceptions are handled by parser itself + return await _command.ExecuteAsync(arguments, loggerFactory, cancellationToken).ConfigureAwait(false); + } + } } } diff --git a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/Verify/VerifyCommand.cs b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/Verify/VerifyCommand.cs index 69cbe372e80..4a402e4d4ee 100644 --- a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/Verify/VerifyCommand.cs +++ b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/Verify/VerifyCommand.cs @@ -12,72 +12,72 @@ internal class VerifyCommand : ExecutableCommand { private const string CommandName = "verify"; - private readonly Argument _templateNameArgument = new("template-short-name") + private readonly CliArgument _templateNameArgument = new("template-short-name") { Description = LocalizableStrings.command_verify_help_templateName_description, // 0 for case where only path is specified Arity = new ArgumentArity(1, 1) }; - private readonly Option _remainingArguments = new Option("template-args", new[] { "--template-args" }) + private readonly CliOption _remainingArguments = new("--template-args") { Description = "Template specific arguments - all joined into single enquoted string. Any needed quotations of actual arguments has to be escaped.", Arity = new ArgumentArity(0, 1) }; - private readonly Option _templatePathOption = new("template-path", new[] { "-p", "--template-path" }) + private readonly CliOption _templatePathOption = new("--template-path", "-p") { Description = LocalizableStrings.command_verify_help_templatePath_description, }; - private readonly Option _templateOutputPathOption = new("output", new[] { "-o", "--output" }) + private readonly CliOption _templateOutputPathOption = new("--output", "-o") { Description = LocalizableStrings.command_verify_help_outputPath_description, }; - private readonly Option _snapshotsDirectoryOption = new("snapshots-directory", new[] { "-d", "--snapshots-directory" }) + private readonly CliOption _snapshotsDirectoryOption = new("--snapshots-directory", "-d") { Description = LocalizableStrings.command_verify_help_snapshotsDirPath_description, }; - private readonly Option _scenarioNameOption = new("scenario-name", new[] { "--scenario-name" }) + private readonly CliOption _scenarioNameOption = new("--scenario-name") { Description = LocalizableStrings.command_verify_help_scenarioName_description, }; - private readonly Option _disableDiffToolOption = new("disable-diff-tool", new[] { "--disable-diff-tool" }) + private readonly CliOption _disableDiffToolOption = new("--disable-diff-tool") { Description = LocalizableStrings.command_verify_help_disableDiffTool_description, }; - private readonly Option _disableDefaultExcludePatternsOption = new("disable-default-exclude-patterns", new[] { "--disable-default-exclude-patterns" }) + private readonly CliOption _disableDefaultExcludePatternsOption = new("--disable-default-exclude-patterns") { Description = LocalizableStrings.command_verify_help_disableDefaultExcludes_description, }; - private readonly Option> _excludePatternOption = new("exclude-pattern", new[] { "--exclude-pattern" }) + private readonly CliOption> _excludePatternOption = new("--exclude-pattern") { Description = LocalizableStrings.command_verify_help_customExcludes_description, Arity = new ArgumentArity(0, 999) }; - private readonly Option> _includePatternOption = new("include-pattern", new[] { "--include-pattern" }) + private readonly CliOption> _includePatternOption = new("--include-pattern") { Description = LocalizableStrings.command_verify_help_customIncludes_description, Arity = new ArgumentArity(0, 999) }; - private readonly Option _verifyCommandOutputOption = new("verify-std", new[] { "--verify-std" }) + private readonly CliOption _verifyCommandOutputOption = new("--verify-std") { Description = LocalizableStrings.command_verify_help_verifyOutputs_description, }; - private readonly Option _isCommandExpectedToFailOption = new("fail-expected", new[] { "--fail-expected" }) + private readonly CliOption _isCommandExpectedToFailOption = new("--fail-expected") { Description = LocalizableStrings.command_verify_help_expectFailure_description, }; - private readonly Option> _uniqueForOption = new("unique-for", new[] { "--unique-for" }) + private readonly CliOption> _uniqueForOption = new("--unique-for") { Description = LocalizableStrings.command_verify_help_uniqueFor_description, Arity = new ArgumentArity(0, 999), @@ -171,9 +171,9 @@ await engine.Execute( } /// - /// Case insensitive version for . + /// Case insensitive version for . /// - private static void FromAmongCaseInsensitive(Option> option, string[]? allowedValues = null, string? allowedHiddenValue = null) + private static void FromAmongCaseInsensitive(CliOption> option, string[]? allowedValues = null, string? allowedHiddenValue = null) { allowedValues ??= Array.Empty(); option.Validators.Add(optionResult => ValidateAllowedValues(optionResult, allowedValues, allowedHiddenValue)); diff --git a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/LocalizeCommand.cs b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/LocalizeCommand.cs index 40607e925d1..5ba6abbc512 100644 --- a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/LocalizeCommand.cs +++ b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/LocalizeCommand.cs @@ -5,12 +5,12 @@ namespace Microsoft.TemplateEngine.Authoring.CLI.Commands { - internal class LocalizeCommand : Command + internal class LocalizeCommand : CliCommand { internal LocalizeCommand() : base("localize") { - this.Subcommands.Add(new ExportCommand()); + Subcommands.Add(new ExportCommand()); } } } diff --git a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/export/ExportCommand.cs b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/export/ExportCommand.cs index 0b0d6ae062d..ec7c95b54e3 100644 --- a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/export/ExportCommand.cs +++ b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/localize/export/ExportCommand.cs @@ -11,25 +11,25 @@ internal sealed class ExportCommand : ExecutableCommand { private const string CommandName = "export"; - private readonly Argument> _templatePathArgument = new("template-path") + private readonly CliArgument> _templatePathArgument = new("template-path") { Arity = ArgumentArity.OneOrMore, Description = LocalizableStrings.command_export_help_templatePath_description, }; - private readonly Option> _languageOption = new("language", new[] { "--language", "-l" }) + private readonly CliOption> _languageOption = new("--language", "-l") { Description = LocalizableStrings.command_export_help_language_description, Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = true, }; - private readonly Option _recursiveOption = new("recursive", new[] { "--recursive", "-r" }) + private readonly CliOption _recursiveOption = new("recursive", new[] { "--recursive", "-r" }) { Description = LocalizableStrings.command_export_help_recursive_description, }; - private readonly Option _dryRunOption = new("dry-run", new[] { "--dry-run", "-d" }) + private readonly CliOption _dryRunOption = new("--dry-run", "-d") { Description = LocalizableStrings.command_export_help_dryrun_description, }; diff --git a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/validate/ValidateCommand.cs b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/validate/ValidateCommand.cs index 0fdc8abef0f..1d4d9a61e62 100644 --- a/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/validate/ValidateCommand.cs +++ b/tools/Microsoft.TemplateEngine.Authoring.CLI/Commands/validate/ValidateCommand.cs @@ -14,7 +14,7 @@ internal class ValidateCommand : ExecutableCommand { private const string CommandName = "validate"; - private readonly Argument _templateLocationArg = new("template-location") + private readonly CliArgument _templateLocationArg = new("template-location") { Description = LocalizableStrings.command_validate_help_description, Arity = new ArgumentArity(1, 1) diff --git a/tools/Microsoft.TemplateEngine.Authoring.CLI/Program.cs b/tools/Microsoft.TemplateEngine.Authoring.CLI/Program.cs index 92fcd7508de..2d9ca2b5a91 100644 --- a/tools/Microsoft.TemplateEngine.Authoring.CLI/Program.cs +++ b/tools/Microsoft.TemplateEngine.Authoring.CLI/Program.cs @@ -11,7 +11,7 @@ internal sealed class Program { internal static Task Main(string[] args) { - RootCommand rootCommand = new("dotnet-template-authoring"); + CliRootCommand rootCommand = new("dotnet-template-authoring"); rootCommand.Subcommands.Add(new LocalizeCommand()); rootCommand.Subcommands.Add(new VerifyCommand()); rootCommand.Subcommands.Add(new ValidateCommand()); @@ -19,12 +19,12 @@ internal static Task Main(string[] args) return GetCommandLineConfiguration(rootCommand).InvokeAsync(args); } - internal static CommandLineConfiguration GetCommandLineConfiguration(Command command) + internal static CliConfiguration GetCommandLineConfiguration(CliCommand command) { - CommandLineBuilder builder = new CommandLineBuilder(command) - .UseDefaults() - .EnablePosixBundling(false); - return builder.Build(); + return new CliConfiguration(command) + { + EnablePosixBundling = false + }; } } } diff --git a/tools/Microsoft.TemplateSearch.TemplateDiscovery/Program.cs b/tools/Microsoft.TemplateSearch.TemplateDiscovery/Program.cs index a18447029ec..a0dfb840a1b 100644 --- a/tools/Microsoft.TemplateSearch.TemplateDiscovery/Program.cs +++ b/tools/Microsoft.TemplateSearch.TemplateDiscovery/Program.cs @@ -9,8 +9,8 @@ internal class Program { private static async Task Main(string[] args) { - Command rootCommand = new TemplateDiscoveryCommand(); - return await rootCommand.InvokeAsync(args).ConfigureAwait(false); + CliCommand rootCommand = new TemplateDiscoveryCommand(); + return await rootCommand.Parse(args).InvokeAsync().ConfigureAwait(false); } } } diff --git a/tools/Microsoft.TemplateSearch.TemplateDiscovery/TemplateDiscoveryCommand.cs b/tools/Microsoft.TemplateSearch.TemplateDiscovery/TemplateDiscoveryCommand.cs index 975ebe3233c..44ad8810480 100644 --- a/tools/Microsoft.TemplateSearch.TemplateDiscovery/TemplateDiscoveryCommand.cs +++ b/tools/Microsoft.TemplateSearch.TemplateDiscovery/TemplateDiscoveryCommand.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; -using System.CommandLine.Invocation; using Microsoft.TemplateSearch.TemplateDiscovery.NuGet; using Microsoft.TemplateSearch.TemplateDiscovery.PackChecking; using Microsoft.TemplateSearch.TemplateDiscovery.Results; @@ -10,79 +9,79 @@ namespace Microsoft.TemplateSearch.TemplateDiscovery { - internal class TemplateDiscoveryCommand : Command + internal class TemplateDiscoveryCommand : CliCommand { private const int DefaultPageSize = 100; - private readonly Option _basePathOption = new Option("basePath", new[] { "--basePath" }) + private readonly CliOption _basePathOption = new("--basePath") { Arity = ArgumentArity.ExactlyOne, Description = "The root dir for output for this run.", - IsRequired = true + Required = true }; - private readonly Option _allowPreviewPacksOption = new Option("allowPreviewPacks", new[] { "--allowPreviewPacks" }) + private readonly CliOption _allowPreviewPacksOption = new("--allowPreviewPacks") { Description = "Include preview packs in the results (by default, preview packs are ignored and the latest stable pack is used.", }; - private readonly Option _pageSizeOption = new Option("pageSize", new[] { "--pageSize" }) + private readonly CliOption _pageSizeOption = new("--pageSize") { Description = "(debugging) The chunk size for interactions with the source.", DefaultValueFactory = (r) => DefaultPageSize, }; - private readonly Option _onePageOption = new Option("onePage", new[] { "--onePage" }) + private readonly CliOption _onePageOption = new("--onePage") { Description = "(debugging) Only process one page of template packs.", }; - private readonly Option _savePacksOption = new Option("savePacks", new[] { "--savePacks" }) + private readonly CliOption _savePacksOption = new("--savePacks") { Description = "Don't delete downloaded candidate packs (by default, they're deleted at the end of a run).", }; - private readonly Option _noTemplateJsonFilterOption = new Option("noTemplateJsonFilter", new[] { "--noTemplateJsonFilter" }) + private readonly CliOption _noTemplateJsonFilterOption = new("--noTemplateJsonFilter") { Description = "Don't prefilter packs that don't contain any template.json files (this filter is applied by default).", }; - private readonly Option _verboseOption = new Option("verbose", new[] { "-v", "--verbose" }) + private readonly CliOption _verboseOption = new("--verbose", "-v") { Description = "Verbose output for template processing.", }; - private readonly Option _testOption = new Option("test", new[] { "-t", "--test" }) + private readonly CliOption _testOption = new("--test", "-t") { Description = "Run tests on generated metadata files.", }; - private readonly Option _queriesOption = new Option("queries", new[] { "--queries" }) + private readonly CliOption _queriesOption = new("--queries") { Arity = ArgumentArity.OneOrMore, Description = $"The list of providers to run. Supported providers: {string.Join(",", Enum.GetValues())}.", AllowMultipleArgumentsPerToken = true, }; - private readonly Option _packagesPathOption = new Option("packagesPath", new[] { "--packagesPath" }) + private readonly CliOption _packagesPathOption = new CliOption("--packagesPath") { - Description = $"Path to pre-downloaded packages. If specified, the packages won't be downloaded from NuGet.org.", + Description = "Path to pre-downloaded packages. If specified, the packages won't be downloaded from NuGet.org." }.AcceptExistingOnly(); - private readonly Option _diffOption = new Option("diff", new[] { "--diff" }) + private readonly CliOption _diffOption = new("--diff") { - Description = $"The list of packages will be compared with previous run, and if package version is not changed, the package won't be rescanned.", + Description = "The list of packages will be compared with previous run, and if package version is not changed, the package won't be rescanned.", DefaultValueFactory = (r) => true, }; - private readonly Option _diffOverrideCacheOption = new Option("diff-override-cache", new[] { "--diff-override-cache" }) + private readonly CliOption _diffOverrideCacheOption = new CliOption("--diff-override-cache") { - Description = $"Location of current search cache (local path only).", + Description = "Location of current search cache (local path only).", }.AcceptExistingOnly(); - private readonly Option _diffOverrideNonPackagesOption = new Option("diff-override-non-packages", new[] { "--diff-override-non-packages" }) + private readonly CliOption _diffOverrideNonPackagesOption = new CliOption("--diff-override-non-packages") { - Description = $"Location of the list of packages known not to be a valid package (local path only).", + Description = "Location of the list of packages known not to be a valid package (local path only).", }.AcceptExistingOnly(); public TemplateDiscoveryCommand() : base("template-discovery", "Generates the template package search cache file based on the packages available on NuGet.org.") @@ -105,22 +104,22 @@ public TemplateDiscoveryCommand() : base("template-discovery", "Generates the te Options.Add(_diffOverrideNonPackagesOption); TreatUnmatchedTokensAsErrors = true; - SetHandler(async (InvocationContext ctx, CancellationToken cancellationToken) => + SetAction(async (ParseResult parseResult, CancellationToken cancellationToken) => { - var config = new CommandArgs(ctx.ParseResult.GetValue(_basePathOption) ?? throw new Exception("Output path is not set")) + var config = new CommandArgs(parseResult.GetValue(_basePathOption) ?? throw new Exception("Output path is not set")) { - LocalPackagePath = ctx.ParseResult.GetValue(_packagesPathOption), - PageSize = ctx.ParseResult.GetValue(_pageSizeOption), - SaveCandidatePacks = ctx.ParseResult.GetValue(_savePacksOption), - RunOnlyOnePage = ctx.ParseResult.GetValue(_onePageOption), - IncludePreviewPacks = ctx.ParseResult.GetValue(_allowPreviewPacksOption), - DontFilterOnTemplateJson = ctx.ParseResult.GetValue(_noTemplateJsonFilterOption), - Verbose = ctx.ParseResult.GetValue(_verboseOption), - TestEnabled = ctx.ParseResult.GetValue(_testOption), - Queries = ctx.ParseResult.GetValue(_queriesOption) ?? Array.Empty(), - DiffMode = ctx.ParseResult.GetValue(_diffOption), - DiffOverrideSearchCacheLocation = ctx.ParseResult.GetValue(_diffOverrideCacheOption), - DiffOverrideKnownPackagesLocation = ctx.ParseResult.GetValue(_diffOverrideNonPackagesOption), + LocalPackagePath = parseResult.GetValue(_packagesPathOption), + PageSize = parseResult.GetValue(_pageSizeOption), + SaveCandidatePacks = parseResult.GetValue(_savePacksOption), + RunOnlyOnePage = parseResult.GetValue(_onePageOption), + IncludePreviewPacks = parseResult.GetValue(_allowPreviewPacksOption), + DontFilterOnTemplateJson = parseResult.GetValue(_noTemplateJsonFilterOption), + Verbose = parseResult.GetValue(_verboseOption), + TestEnabled = parseResult.GetValue(_testOption), + Queries = parseResult.GetValue(_queriesOption) ?? Array.Empty(), + DiffMode = parseResult.GetValue(_diffOption), + DiffOverrideSearchCacheLocation = parseResult.GetValue(_diffOverrideCacheOption), + DiffOverrideKnownPackagesLocation = parseResult.GetValue(_diffOverrideNonPackagesOption), }; await ExecuteAsync(config, cancellationToken).ConfigureAwait(false);