diff --git a/Directory.Packages.props b/Directory.Packages.props index eca68c8c53b..2761dc14cdd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -53,7 +53,6 @@ - @@ -179,7 +178,6 @@ <_allowBuildFromSourcePackage Include="Microsoft.CSharp" /> <_allowBuildFromSourcePackage Include="Microsoft.DotNet.Build.Tasks.Feed" /> <_allowBuildFromSourcePackage Include="Microsoft.DotNet.XliffTasks" /> - <_allowBuildFromSourcePackage Include="Microsoft.Extensions.CommandLineUtils.Sources" /> <_allowBuildFromSourcePackage Include="Microsoft.Extensions.FileProviders.Abstractions" /> <_allowBuildFromSourcePackage Include="Microsoft.Extensions.FileSystemGlobbing" /> <_allowBuildFromSourcePackage Include="Microsoft.Web.Xdt" /> diff --git a/NuGet.Config b/NuGet.Config index 00d231892f6..4c7e64e6419 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -5,7 +5,6 @@ - @@ -90,9 +89,6 @@ - - - diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/CommandConstants.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/CommandConstants.cs index 836cd19be7f..af106877253 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/CommandConstants.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/CommandConstants.cs @@ -3,7 +3,7 @@ #nullable enable -namespace NuGet.CommandLine.XPlat +namespace NuGet.CommandLine.XPlat.Commands { internal static class CommandConstants { diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/CommandParsers.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/CommandParsers.cs deleted file mode 100644 index cca3af72ba0..00000000000 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/CommandParsers.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -#nullable enable - -using System; -using Microsoft.Extensions.CommandLineUtils; -using NuGet.Common; - -namespace NuGet.CommandLine.XPlat -{ - internal static class CommandParsers - { - public static void Register(CommandLineApplication app, Func getLogger) - { - AddVerbParser.Register(app, getLogger); - DisableVerbParser.Register(app, getLogger); - EnableVerbParser.Register(app, getLogger); - ListVerbParser.Register(app, getLogger); - RemoveVerbParser.Register(app, getLogger); - UpdateVerbParser.Register(app, getLogger); - } - } -} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/ConfigCommands/ConfigCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/ConfigCommands/ConfigCommand.cs index 48e4afb32c4..58a7acbe8ae 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/ConfigCommands/ConfigCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/ConfigCommands/ConfigCommand.cs @@ -5,9 +5,7 @@ using System; using System.CommandLine; -using System.CommandLine.Help; using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; using NuGet.CommandLine.XPlat.Commands; using NuGet.Common; @@ -15,53 +13,6 @@ namespace NuGet.CommandLine.XPlat { internal class ConfigCommand { - private static HelpOption HelpOption = new HelpOption() - { - Arity = ArgumentArity.Zero - }; - - private static Argument SetConfigKeyArgument = new Argument(name: "config-key") - { - Arity = ArgumentArity.ExactlyOne, - Description = Strings.ConfigSetConfigKeyDescription, - }; - - private static Argument UnsetConfigKeyArgument = new Argument(name: "config-key") - { - Arity = ArgumentArity.ExactlyOne, - Description = Strings.ConfigUnsetConfigKeyDescription, - }; - - private static Argument ConfigValueArgument = new Argument(name: "config-value") - { - Arity = ArgumentArity.ExactlyOne, - Description = Strings.ConfigSetConfigValueDescription, - }; - - private static Argument AllOrConfigKeyArgument = new Argument(name: "all-or-config-key") - { - Arity = ArgumentArity.ExactlyOne, - Description = Strings.ConfigGetAllOrConfigKeyDescription - }; - - private static Option WorkingDirectory = new Option(name: "--working-directory") - { - Arity = ArgumentArity.ZeroOrOne, - Description = Strings.ConfigPathsWorkingDirectoryDescription - }; - - private static Option ShowPathOption = new Option(name: "--show-path") - { - Arity = ArgumentArity.Zero, - Description = Strings.ConfigGetShowPathDescription, - }; - - private static Option ConfigFileOption = new Option(name: "--configfile") - { - Arity = ArgumentArity.ZeroOrOne, - Description = Strings.Option_ConfigFile, - }; - internal static void LogException(Exception e, ILogger log) { // Log the error @@ -78,18 +29,9 @@ internal static void LogException(Exception e, ILogger log) log.LogVerbose(e.ToString()); } - internal static void Register(CommandLineApplication app) - { - app.Command("config", configCmd => - { - configCmd.Description = Strings.Config_Description; - }); - } - - internal static Command Register(Command app, Func getLogger) + internal static Command Register(Command app, Func getLogger) { var ConfigCmd = new DocumentedCommand(name: "config", description: Strings.Config_Description, "https://aka.ms/dotnet/nuget/config"); - ConfigCmd.Options.Add(HelpOption); // Options directly under the verb 'config' @@ -132,14 +74,19 @@ internal static Command Register(Command app, Func getLogger) private static void RegisterOptionsForCommandConfigPaths(Command cmd, Func getLogger) { - cmd.Options.Add(WorkingDirectory); - cmd.Options.Add(HelpOption); + var workingDirectory = new Option(name: "--working-directory") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.ConfigPathsWorkingDirectoryDescription + }; + + cmd.Options.Add(workingDirectory); // Create handler delegate handler for cmd cmd.SetAction((parseResult, cancellationToken) => { var args = new ConfigPathsArgs() { - WorkingDirectory = parseResult.GetValue(WorkingDirectory), + WorkingDirectory = parseResult.GetValue(workingDirectory), }; int exitCode = ConfigPathsRunner.Run(args, getLogger); @@ -149,19 +96,36 @@ private static void RegisterOptionsForCommandConfigPaths(Command cmd, Func getLogger) { - cmd.Arguments.Add(AllOrConfigKeyArgument); - cmd.Options.Add(WorkingDirectory); - cmd.Options.Add(ShowPathOption); - cmd.Options.Add(HelpOption); + var allOrConfigKeyArgument = new Argument(name: "all-or-config-key") + { + Arity = ArgumentArity.ExactlyOne, + Description = Strings.ConfigGetAllOrConfigKeyDescription + }; + + var workingDirectory = new Option(name: "--working-directory") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.ConfigPathsWorkingDirectoryDescription + }; + + var showPathOption = new Option(name: "--show-path") + { + Arity = ArgumentArity.Zero, + Description = Strings.ConfigGetShowPathDescription, + }; + + cmd.Arguments.Add(allOrConfigKeyArgument); + cmd.Options.Add(workingDirectory); + cmd.Options.Add(showPathOption); // Create handler delegate handler for cmd cmd.SetAction((parseResult, cancellationToken) => { var args = new ConfigGetArgs() { - AllOrConfigKey = parseResult.GetValue(AllOrConfigKeyArgument), - WorkingDirectory = parseResult.GetValue(WorkingDirectory), - ShowPath = parseResult.GetValue(ShowPathOption), + AllOrConfigKey = parseResult.GetValue(allOrConfigKeyArgument), + WorkingDirectory = parseResult.GetValue(workingDirectory), + ShowPath = parseResult.GetValue(showPathOption), }; int exitCode = ConfigGetRunner.Run(args, getLogger); @@ -171,18 +135,35 @@ private static void RegisterOptionsForCommandConfigGet(Command cmd, Func getLogger) { - cmd.Arguments.Add(SetConfigKeyArgument); - cmd.Arguments.Add(ConfigValueArgument); - cmd.Options.Add(ConfigFileOption); - cmd.Options.Add(HelpOption); + var setConfigKeyArgument = new Argument(name: "config-key") + { + Arity = ArgumentArity.ExactlyOne, + Description = Strings.ConfigSetConfigKeyDescription, + }; + + var configValueArgument = new Argument(name: "config-value") + { + Arity = ArgumentArity.ExactlyOne, + Description = Strings.ConfigSetConfigValueDescription, + }; + + var configFileOption = new Option(name: "--configfile") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.Option_ConfigFile, + }; + + cmd.Arguments.Add(setConfigKeyArgument); + cmd.Arguments.Add(configValueArgument); + cmd.Options.Add(configFileOption); // Create handler delegate handler for cmd cmd.SetAction((parseResult, cancellationToken) => { var args = new ConfigSetArgs() { - ConfigKey = parseResult.GetValue(SetConfigKeyArgument), - ConfigValue = parseResult.GetValue(ConfigValueArgument), - ConfigFile = parseResult.GetValue(ConfigFileOption), + ConfigKey = parseResult.GetValue(setConfigKeyArgument), + ConfigValue = parseResult.GetValue(configValueArgument), + ConfigFile = parseResult.GetValue(configFileOption), }; int exitCode = ConfigSetRunner.Run(args, getLogger); @@ -192,16 +173,27 @@ private static void RegisterOptionsForCommandConfigSet(Command cmd, Func getLogger) { - cmd.Arguments.Add(UnsetConfigKeyArgument); - cmd.Options.Add(ConfigFileOption); - cmd.Options.Add(HelpOption); + var unsetConfigKeyArgument = new Argument(name: "config-key") + { + Arity = ArgumentArity.ExactlyOne, + Description = Strings.ConfigUnsetConfigKeyDescription, + }; + + var configFileOption = new Option(name: "--configfile") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.Option_ConfigFile, + }; + + cmd.Arguments.Add(unsetConfigKeyArgument); + cmd.Options.Add(configFileOption); // Create handler delegate handler for cmd cmd.SetAction((parseResult, cancellationToken) => { var args = new ConfigUnsetArgs() { - ConfigKey = parseResult.GetValue(UnsetConfigKeyArgument), - ConfigFile = parseResult.GetValue(ConfigFileOption), + ConfigKey = parseResult.GetValue(unsetConfigKeyArgument), + ConfigFile = parseResult.GetValue(configFileOption), }; int exitCode = ConfigUnsetRunner.Run(args, getLogger); diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/DeleteCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/DeleteCommand.cs index c7c2e765cc3..a279e83db8a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/DeleteCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/DeleteCommand.cs @@ -4,10 +4,9 @@ #nullable disable using System; +using System.CommandLine; using System.Globalization; -using Microsoft.Extensions.CommandLineUtils; using NuGet.Commands; -using NuGet.Common; using NuGet.Configuration; using NuGet.Credentials; @@ -15,83 +14,92 @@ namespace NuGet.CommandLine.XPlat { internal static class DeleteCommand { - public static void Register(CommandLineApplication app, Func getLogger) + internal static void Register(Command parent, Func getLogger) { - app.Command("delete", delete => + var deleteCmd = new Command("delete", Strings.Delete_Description); + + var sourceOption = new Option("--source", "-s") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.Source_Description, + }; + + var nonInteractiveOption = new Option("--non-interactive") + { + Arity = ArgumentArity.Zero, + Description = Strings.NonInteractive_Description, + }; + + var apiKeyOption = new Option("--api-key", "-k") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.ApiKey_Description, + }; + + var noServiceEndpointOption = new Option("--no-service-endpoint") + { + Arity = ArgumentArity.Zero, + Description = Strings.NoServiceEndpoint_Description, + }; + + var interactiveOption = new Option("--interactive") + { + Arity = ArgumentArity.Zero, + Description = Strings.NuGetXplatCommand_Interactive, + }; + + var packageIdArgument = new Argument("PackageId") + { + Arity = ArgumentArity.ExactlyOne, + Description = Strings.Delete_PackageIdAndVersion_Description, + }; + + var packageVersionArgument = new Argument("PackageVersion") + { + Arity = ArgumentArity.ExactlyOne, + Description = Strings.Delete_PackageIdAndVersion_Description, + }; + + deleteCmd.Options.Add(sourceOption); + deleteCmd.Options.Add(nonInteractiveOption); + deleteCmd.Options.Add(apiKeyOption); + deleteCmd.Options.Add(noServiceEndpointOption); + deleteCmd.Options.Add(interactiveOption); + deleteCmd.Arguments.Add(packageIdArgument); + deleteCmd.Arguments.Add(packageVersionArgument); + + deleteCmd.SetAction(async (parseResult, cancellationToken) => { - delete.Description = Strings.Delete_Description; - delete.HelpOption(XPlatUtility.HelpOption); - - delete.Option( - CommandConstants.ForceEnglishOutputOption, - Strings.ForceEnglishOutput_Description, - CommandOptionType.NoValue); - - var source = delete.Option( - "-s|--source ", - Strings.Source_Description, - CommandOptionType.SingleValue); - - var nonInteractive = delete.Option( - "--non-interactive", - Strings.NonInteractive_Description, - CommandOptionType.NoValue); - - var apikey = delete.Option( - "-k|--api-key ", - Strings.ApiKey_Description, - CommandOptionType.SingleValue); - - var arguments = delete.Argument( - "[root]", - Strings.Delete_PackageIdAndVersion_Description, - multipleValues: true); - - var noServiceEndpointDescription = delete.Option( - "--no-service-endpoint", - Strings.NoServiceEndpoint_Description, - CommandOptionType.NoValue); - - var interactive = delete.Option( - "--interactive", - Strings.NuGetXplatCommand_Interactive, - CommandOptionType.NoValue); - - delete.OnExecute(async () => - { - if (arguments.Values.Count < 2) - { - throw new ArgumentException(Strings.Delete_MissingArguments); - } - - string packageId = arguments.Values[0]; - string packageVersion = arguments.Values[1]; - string sourcePath = source.Value(); - string apiKeyValue = apikey.Value(); - bool nonInteractiveValue = nonInteractive.HasValue(); - bool noServiceEndpoint = noServiceEndpointDescription.HasValue(); - - DefaultCredentialServiceUtility.SetupDefaultCredentialService(getLogger(), !interactive.HasValue()); + string packageId = parseResult.GetValue(packageIdArgument); + string packageVersion = parseResult.GetValue(packageVersionArgument); + string sourcePath = parseResult.GetValue(sourceOption); + string apiKeyValue = parseResult.GetValue(apiKeyOption); + bool nonInteractiveValue = parseResult.GetValue(nonInteractiveOption); + bool noServiceEndpoint = parseResult.GetValue(noServiceEndpointOption); + bool interactiveValue = parseResult.GetValue(interactiveOption); + + DefaultCredentialServiceUtility.SetupDefaultCredentialService(getLogger(), !interactiveValue); #pragma warning disable CS0618 // Type or member is obsolete - PackageSourceProvider sourceProvider = new PackageSourceProvider(XPlatUtility.GetSettingsForCurrentWorkingDirectory(), enablePackageSourcesChangedEvent: false); + PackageSourceProvider sourceProvider = new PackageSourceProvider(XPlatUtility.GetSettingsForCurrentWorkingDirectory(), enablePackageSourcesChangedEvent: false); #pragma warning restore CS0618 // Type or member is obsolete - await DeleteRunner.Run( - sourceProvider.Settings, - sourceProvider, - packageId, - packageVersion, - sourcePath, - apiKeyValue, - nonInteractiveValue, - noServiceEndpoint, - Confirm, - getLogger()); - - return 0; - }); + await DeleteRunner.Run( + sourceProvider.Settings, + sourceProvider, + packageId, + packageVersion, + sourcePath, + apiKeyValue, + nonInteractiveValue, + noServiceEndpoint, + Confirm, + getLogger()); + + return 0; }); + + parent.Subcommands.Add(deleteCmd); } private static bool Confirm(string description) @@ -103,7 +111,7 @@ private static bool Confirm(string description) Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(string.Format(CultureInfo.CurrentCulture, Strings.ConsoleConfirmMessage, description)); var result = Console.ReadLine(); - return result.StartsWith(Strings.ConsoleConfirmMessageAccept, StringComparison.OrdinalIgnoreCase); + return result != null && result.StartsWith(Strings.ConsoleConfirmMessageAccept, StringComparison.OrdinalIgnoreCase); } finally { diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/LocalsCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/LocalsCommand.cs index 4b54b532bee..1ee2cf961b7 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/LocalsCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/LocalsCommand.cs @@ -4,79 +4,84 @@ #nullable enable using System; +using System.CommandLine; +using System.Collections.Generic; using System.Globalization; -using Microsoft.Extensions.CommandLineUtils; +using System.Threading.Tasks; using NuGet.Commands; -using NuGet.Common; namespace NuGet.CommandLine.XPlat { internal static class LocalsCommand { - public static void Register(CommandLineApplication app, Func getLogger) + internal static void Register(Command parent, Func getLogger) { - app.Command("locals", locals => + var localsCmd = new Command("locals", Strings.LocalsCommand_Description); + + var clearOption = new Option("--clear", "-c") { - locals.Description = Strings.LocalsCommand_Description; - locals.HelpOption(XPlatUtility.HelpOption); + Arity = ArgumentArity.Zero, + Description = Strings.LocalsCommand_ClearDescription, + }; - locals.Option( - CommandConstants.ForceEnglishOutputOption, - Strings.ForceEnglishOutput_Description, - CommandOptionType.NoValue); + var listOption = new Option("--list", "-l") + { + Arity = ArgumentArity.Zero, + Description = Strings.LocalsCommand_ListDescription, + }; - var clear = locals.Option( - "-c|--clear", - Strings.LocalsCommand_ClearDescription, - CommandOptionType.NoValue); + var cacheLocationArgument = new Argument("Cache Location(s)") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.LocalsCommand_ArgumentDescription, + }; - var list = locals.Option( - "-l|--list", - Strings.LocalsCommand_ListDescription, - CommandOptionType.NoValue); + localsCmd.Options.Add(clearOption); + localsCmd.Options.Add(listOption); + localsCmd.Arguments.Add(cacheLocationArgument); - var arguments = locals.Argument( - "Cache Location(s)", - Strings.LocalsCommand_ArgumentDescription, - multipleValues: false); + localsCmd.SetAction((parseResult, cancellationToken) => + { + var logger = getLogger(); + var setting = XPlatUtility.GetSettingsForCurrentWorkingDirectory(); - locals.OnExecute(() => - { - var logger = getLogger(); - var setting = XPlatUtility.GetSettingsForCurrentWorkingDirectory(); + string? cacheLocation = parseResult.GetValue(cacheLocationArgument); + bool clear = parseResult.GetValue(clearOption); + bool list = parseResult.GetValue(listOption); - // Using both -clear and -list command options, or neither one of them, is not supported. - // We use MinArgs = 0 even though the first argument is required, - // to avoid throwing a command argument validation exception and - // immediately show usage help for this command instead. - if ((arguments.Values.Count < 1) || string.IsNullOrWhiteSpace(arguments.Values[0])) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.LocalsCommand_NoArguments)); - } - else if (clear.HasValue() && list.HasValue()) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.LocalsCommand_MultipleOperations)); - } - else if (!clear.HasValue() && !list.HasValue()) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.LocalsCommand_NoOperation)); - } - else - { - var localsArgs = new LocalsArgs(arguments.Values, - setting, - logger.LogInformation, - logger.LogError, - clear.HasValue(), - list.HasValue()); + // Using both -clear and -list command options, or neither one of them, is not supported. + // We use MinArgs = 0 even though the first argument is required, + // to avoid throwing a command argument validation exception and + // immediately show usage help for this command instead. + if (string.IsNullOrWhiteSpace(cacheLocation)) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.LocalsCommand_NoArguments)); + } + else if (clear && list) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.LocalsCommand_MultipleOperations)); + } + else if (!clear && !list) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.LocalsCommand_NoOperation)); + } + else + { + var localsArgs = new LocalsArgs(new List { cacheLocation }, + setting, + logger.LogInformation, + logger.LogError, + clear, + list); - var localsCommandRunner = new LocalsCommandRunner(); - localsCommandRunner.ExecuteCommand(localsArgs); - } + var localsCommandRunner = new LocalsCommandRunner(); + localsCommandRunner.ExecuteCommand(localsArgs); + } - return 0; - }); + return Task.FromResult(0); }); + + parent.Subcommands.Add(localsCmd); } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Add/DotnetNuGetAddCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Add/DotnetNuGetAddCommand.cs new file mode 100644 index 00000000000..6a9e5207a05 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Add/DotnetNuGetAddCommand.cs @@ -0,0 +1,118 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.CommandLine; +using System.Threading.Tasks; +using NuGet.Commands; + +namespace NuGet.CommandLine.XPlat.Commands.NuGet.Add +{ + internal static class DotnetNuGetAddCommand + { + internal static void Register(Command parent, Func getLogger) + { + var addCmd = new Command("add", Strings.Add_Description); + + RegisterAddSource(addCmd, getLogger); + RegisterAddClientCert(addCmd, getLogger); + + parent.Subcommands.Add(addCmd); + } + + private static void RegisterAddSource(Command parent, Func getLogger) + { + var sourceCmd = new Command("source", Strings.AddSourceCommandDescription); + + var sourceArg = new Argument("PackageSourcePath") { Description = Strings.SourcesCommandSourceDescription }; + var name = new Option("--name", "-n") { Description = Strings.SourcesCommandNameDescription }; + var username = new Option("--username", "-u") { Description = Strings.SourcesCommandUsernameDescription }; + var password = new Option("--password", "-p") { Description = Strings.SourcesCommandPasswordDescription }; + var storePasswordInClearText = new Option("--store-password-in-clear-text") { Description = Strings.SourcesCommandStorePasswordInClearTextDescription }; + var validAuthenticationTypes = new Option("--valid-authentication-types") { Description = Strings.SourcesCommandValidAuthenticationTypesDescription }; + var protocolVersion = new Option("--protocol-version") { Description = Strings.SourcesCommandProtocolVersionDescription }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + var allowInsecureConnections = new Option("--allow-insecure-connections") { Description = Strings.SourcesCommandAllowInsecureConnectionsDescription }; + + sourceCmd.Arguments.Add(sourceArg); + sourceCmd.Options.Add(name); + sourceCmd.Options.Add(username); + sourceCmd.Options.Add(password); + sourceCmd.Options.Add(storePasswordInClearText); + sourceCmd.Options.Add(validAuthenticationTypes); + sourceCmd.Options.Add(protocolVersion); + sourceCmd.Options.Add(configfile); + sourceCmd.Options.Add(allowInsecureConnections); + + sourceCmd.SetAction((parseResult, cancellationToken) => + { + var args = new AddSourceArgs() + { + Source = parseResult.GetValue(sourceArg), + Name = parseResult.GetValue(name), + Username = parseResult.GetValue(username), + Password = parseResult.GetValue(password), + StorePasswordInClearText = parseResult.GetValue(storePasswordInClearText), + ValidAuthenticationTypes = parseResult.GetValue(validAuthenticationTypes), + ProtocolVersion = parseResult.GetValue(protocolVersion), + Configfile = parseResult.GetValue(configfile), + AllowInsecureConnections = parseResult.GetValue(allowInsecureConnections), + }; + + AddSourceRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(sourceCmd); + } + + private static void RegisterAddClientCert(Command parent, Func getLogger) + { + var clientCertCmd = new Command("client-cert", Strings.AddClientCertCommandDescription); + + var packagesource = new Option("--package-source", "-s") { Description = Strings.Option_PackageSource }; + var path = new Option("--path") { Description = Strings.Option_Path }; + var password = new Option("--password") { Description = Strings.Option_Password }; + var storePasswordInClearText = new Option("--store-password-in-clear-text") { Description = Strings.Option_StorePasswordInClearText }; + var storeLocation = new Option("--store-location") { Description = Strings.Option_StoreLocation }; + var storeName = new Option("--store-name") { Description = Strings.Option_StoreName }; + var findBy = new Option("--find-by") { Description = Strings.Option_FindBy }; + var findValue = new Option("--find-value") { Description = Strings.Option_FindValue }; + var force = new Option("--force", "-f") { Description = Strings.Option_Force }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + clientCertCmd.Options.Add(packagesource); + clientCertCmd.Options.Add(path); + clientCertCmd.Options.Add(password); + clientCertCmd.Options.Add(storePasswordInClearText); + clientCertCmd.Options.Add(storeLocation); + clientCertCmd.Options.Add(storeName); + clientCertCmd.Options.Add(findBy); + clientCertCmd.Options.Add(findValue); + clientCertCmd.Options.Add(force); + clientCertCmd.Options.Add(configfile); + + clientCertCmd.SetAction((parseResult, cancellationToken) => + { + var args = new AddClientCertArgs() + { + PackageSource = parseResult.GetValue(packagesource), + Path = parseResult.GetValue(path), + Password = parseResult.GetValue(password), + StorePasswordInClearText = parseResult.GetValue(storePasswordInClearText), + StoreLocation = parseResult.GetValue(storeLocation), + StoreName = parseResult.GetValue(storeName), + FindBy = parseResult.GetValue(findBy), + FindValue = parseResult.GetValue(findValue), + Force = parseResult.GetValue(force), + Configfile = parseResult.GetValue(configfile), + }; + + AddClientCertRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(clientCertCmd); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Disable/DotnetNuGetDisableCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Disable/DotnetNuGetDisableCommand.cs new file mode 100644 index 00000000000..72b76f240fc --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Disable/DotnetNuGetDisableCommand.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.CommandLine; +using System.Threading.Tasks; +using NuGet.Commands; + +namespace NuGet.CommandLine.XPlat.Commands.NuGet.Disable +{ + internal static class DotnetNuGetDisableCommand + { + internal static void Register(Command parent, Func getLogger) + { + var disableCmd = new Command("disable", Strings.Disable_Description); + + var sourceCmd = new Command("source", Strings.DisableSourceCommandDescription); + + var nameArg = new Argument("name") { Description = Strings.SourcesCommandNameDescription }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + sourceCmd.Arguments.Add(nameArg); + sourceCmd.Options.Add(configfile); + + sourceCmd.SetAction((parseResult, cancellationToken) => + { + var args = new DisableSourceArgs() + { + Name = parseResult.GetValue(nameArg), + Configfile = parseResult.GetValue(configfile), + }; + + DisableSourceRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + disableCmd.Subcommands.Add(sourceCmd); + parent.Subcommands.Add(disableCmd); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Enable/DotnetNuGetEnableCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Enable/DotnetNuGetEnableCommand.cs new file mode 100644 index 00000000000..a785a3f53ad --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Enable/DotnetNuGetEnableCommand.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.CommandLine; +using System.Threading.Tasks; +using NuGet.Commands; + +namespace NuGet.CommandLine.XPlat.Commands.NuGet.Enable +{ + internal static class DotnetNuGetEnableCommand + { + internal static void Register(Command parent, Func getLogger) + { + var enableCmd = new Command("enable", Strings.Enable_Description); + + var sourceCmd = new Command("source", Strings.EnableSourceCommandDescription); + + var nameArg = new Argument("name") { Description = Strings.SourcesCommandNameDescription }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + sourceCmd.Arguments.Add(nameArg); + sourceCmd.Options.Add(configfile); + + sourceCmd.SetAction((parseResult, cancellationToken) => + { + var args = new EnableSourceArgs() + { + Name = parseResult.GetValue(nameArg), + Configfile = parseResult.GetValue(configfile), + }; + + EnableSourceRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + enableCmd.Subcommands.Add(sourceCmd); + parent.Subcommands.Add(enableCmd); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/List/DotnetNuGetListCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/List/DotnetNuGetListCommand.cs new file mode 100644 index 00000000000..6972006ef04 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/List/DotnetNuGetListCommand.cs @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.CommandLine; +using System.Threading.Tasks; +using NuGet.Commands; + +namespace NuGet.CommandLine.XPlat.Commands.NuGet.List +{ + internal static class DotnetNuGetListCommand + { + internal static void Register(Command parent, Func getLogger) + { + var listCmd = new Command("list", Strings.List_Description); + + RegisterListSource(listCmd, getLogger); + RegisterListClientCert(listCmd, getLogger); + + parent.Subcommands.Add(listCmd); + } + + private static void RegisterListSource(Command parent, Func getLogger) + { + var sourceCmd = new Command("source", Strings.ListSourceCommandDescription); + + var format = new Option("--format") { Description = Strings.SourcesCommandFormatDescription }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + sourceCmd.Options.Add(format); + sourceCmd.Options.Add(configfile); + + sourceCmd.SetAction((parseResult, cancellationToken) => + { + var args = new ListSourceArgs() + { + Format = parseResult.GetValue(format), + Configfile = parseResult.GetValue(configfile), + }; + + ListSourceRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(sourceCmd); + } + + private static void RegisterListClientCert(Command parent, Func getLogger) + { + var clientCertCmd = new Command("client-cert", Strings.ListClientCertCommandDescription); + + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + clientCertCmd.Options.Add(configfile); + + clientCertCmd.SetAction((parseResult, cancellationToken) => + { + var args = new ListClientCertArgs() + { + Configfile = parseResult.GetValue(configfile), + }; + + ListClientCertRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(clientCertCmd); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Remove/DotnetNuGetRemoveCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Remove/DotnetNuGetRemoveCommand.cs new file mode 100644 index 00000000000..7b299175775 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Remove/DotnetNuGetRemoveCommand.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.CommandLine; +using System.Threading.Tasks; +using NuGet.Commands; + +namespace NuGet.CommandLine.XPlat.Commands.NuGet.Remove +{ + internal static class DotnetNuGetRemoveCommand + { + internal static void Register(Command parent, Func getLogger) + { + var removeCmd = new Command("remove", Strings.Remove_Description); + + RegisterRemoveSource(removeCmd, getLogger); + RegisterRemoveClientCert(removeCmd, getLogger); + + parent.Subcommands.Add(removeCmd); + } + + private static void RegisterRemoveSource(Command parent, Func getLogger) + { + var sourceCmd = new Command("source", Strings.RemoveSourceCommandDescription); + + var nameArg = new Argument("name") { Description = Strings.SourcesCommandNameDescription }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + sourceCmd.Arguments.Add(nameArg); + sourceCmd.Options.Add(configfile); + + sourceCmd.SetAction((parseResult, cancellationToken) => + { + var args = new RemoveSourceArgs() + { + Name = parseResult.GetValue(nameArg), + Configfile = parseResult.GetValue(configfile), + }; + + RemoveSourceRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(sourceCmd); + } + + private static void RegisterRemoveClientCert(Command parent, Func getLogger) + { + var clientCertCmd = new Command("client-cert", Strings.RemoveClientCertCommandDescription); + + var packagesource = new Option("--package-source", "-s") { Description = Strings.Option_PackageSource }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + clientCertCmd.Options.Add(packagesource); + clientCertCmd.Options.Add(configfile); + + clientCertCmd.SetAction((parseResult, cancellationToken) => + { + var args = new RemoveClientCertArgs() + { + PackageSource = parseResult.GetValue(packagesource), + Configfile = parseResult.GetValue(configfile), + }; + + RemoveClientCertRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(clientCertCmd); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Update/DotnetNuGetUpdateCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Update/DotnetNuGetUpdateCommand.cs new file mode 100644 index 00000000000..e30ea29eb87 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/NuGet/Update/DotnetNuGetUpdateCommand.cs @@ -0,0 +1,118 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.CommandLine; +using System.Threading.Tasks; +using NuGet.Commands; + +namespace NuGet.CommandLine.XPlat.Commands.NuGet.Update +{ + internal static class DotnetNuGetUpdateCommand + { + internal static void Register(Command parent, Func getLogger) + { + var updateCmd = new Command("update", Strings.Update_Description); + + RegisterUpdateSource(updateCmd, getLogger); + RegisterUpdateClientCert(updateCmd, getLogger); + + parent.Subcommands.Add(updateCmd); + } + + private static void RegisterUpdateSource(Command parent, Func getLogger) + { + var sourceCmd = new Command("source", Strings.UpdateSourceCommandDescription); + + var nameArg = new Argument("name") { Description = Strings.SourcesCommandNameDescription }; + var source = new Option("--source", "-s") { Description = Strings.SourcesCommandSourceDescription }; + var username = new Option("--username", "-u") { Description = Strings.SourcesCommandUsernameDescription }; + var password = new Option("--password", "-p") { Description = Strings.SourcesCommandPasswordDescription }; + var storePasswordInClearText = new Option("--store-password-in-clear-text") { Description = Strings.SourcesCommandStorePasswordInClearTextDescription }; + var validAuthenticationTypes = new Option("--valid-authentication-types") { Description = Strings.SourcesCommandValidAuthenticationTypesDescription }; + var protocolVersion = new Option("--protocol-version") { Description = Strings.SourcesCommandProtocolVersionDescription }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + var allowInsecureConnections = new Option("--allow-insecure-connections") { Description = Strings.SourcesCommandAllowInsecureConnectionsDescription }; + + sourceCmd.Arguments.Add(nameArg); + sourceCmd.Options.Add(source); + sourceCmd.Options.Add(username); + sourceCmd.Options.Add(password); + sourceCmd.Options.Add(storePasswordInClearText); + sourceCmd.Options.Add(validAuthenticationTypes); + sourceCmd.Options.Add(protocolVersion); + sourceCmd.Options.Add(configfile); + sourceCmd.Options.Add(allowInsecureConnections); + + sourceCmd.SetAction((parseResult, cancellationToken) => + { + var args = new UpdateSourceArgs() + { + Name = parseResult.GetValue(nameArg), + Source = parseResult.GetValue(source), + Username = parseResult.GetValue(username), + Password = parseResult.GetValue(password), + StorePasswordInClearText = parseResult.GetValue(storePasswordInClearText), + ValidAuthenticationTypes = parseResult.GetValue(validAuthenticationTypes), + ProtocolVersion = parseResult.GetValue(protocolVersion), + Configfile = parseResult.GetValue(configfile), + AllowInsecureConnections = parseResult.GetValue(allowInsecureConnections), + }; + + UpdateSourceRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(sourceCmd); + } + + private static void RegisterUpdateClientCert(Command parent, Func getLogger) + { + var clientCertCmd = new Command("client-cert", Strings.UpdateClientCertCommandDescription); + + var packagesource = new Option("--package-source", "-s") { Description = Strings.Option_PackageSource }; + var path = new Option("--path") { Description = Strings.Option_Path }; + var password = new Option("--password") { Description = Strings.Option_Password }; + var storePasswordInClearText = new Option("--store-password-in-clear-text") { Description = Strings.Option_StorePasswordInClearText }; + var storeLocation = new Option("--store-location") { Description = Strings.Option_StoreLocation }; + var storeName = new Option("--store-name") { Description = Strings.Option_StoreName }; + var findBy = new Option("--find-by") { Description = Strings.Option_FindBy }; + var findValue = new Option("--find-value") { Description = Strings.Option_FindValue }; + var force = new Option("--force", "-f") { Description = Strings.Option_Force }; + var configfile = new Option("--configfile") { Description = Strings.Option_ConfigFile }; + + clientCertCmd.Options.Add(packagesource); + clientCertCmd.Options.Add(path); + clientCertCmd.Options.Add(password); + clientCertCmd.Options.Add(storePasswordInClearText); + clientCertCmd.Options.Add(storeLocation); + clientCertCmd.Options.Add(storeName); + clientCertCmd.Options.Add(findBy); + clientCertCmd.Options.Add(findValue); + clientCertCmd.Options.Add(force); + clientCertCmd.Options.Add(configfile); + + clientCertCmd.SetAction((parseResult, cancellationToken) => + { + var args = new UpdateClientCertArgs() + { + PackageSource = parseResult.GetValue(packagesource), + Path = parseResult.GetValue(path), + Password = parseResult.GetValue(password), + StorePasswordInClearText = parseResult.GetValue(storePasswordInClearText), + StoreLocation = parseResult.GetValue(storeLocation), + StoreName = parseResult.GetValue(storeName), + FindBy = parseResult.GetValue(findBy), + FindValue = parseResult.GetValue(findValue), + Force = parseResult.GetValue(force), + Configfile = parseResult.GetValue(configfile), + }; + + UpdateClientCertRunner.Run(args, () => getLogger()); + return Task.FromResult(0); + }); + + parent.Subcommands.Add(clientCertCmd); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs index 5824f320029..51d199b1e0f 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs @@ -4,116 +4,141 @@ #nullable enable using System; +using System.CommandLine; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; -using Microsoft.Extensions.CommandLineUtils; -using NuGet.Common; using NuGet.Packaging.Signing; namespace NuGet.CommandLine.XPlat { internal static class AddPackageReferenceCommand { - public static void Register(CommandLineApplication app, Func getLogger, + internal static void Register(Command parent, Func getLogger, Func getCommandRunner, Func? getVirtualProjectBuilder = null) { - app.Command("add", addpkg => + var addCommand = new Command("add", Strings.AddPkg_Description); + + var id = new Option("--package") + { + Description = Strings.AddPkg_PackageIdDescription, + Arity = ArgumentArity.ExactlyOne + }; + + var version = new Option("--version") + { + Description = Strings.AddPkg_PackageVersionDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var dgFilePath = new Option("--dg-file", "-d") + { + Description = Strings.AddPkg_DgFileDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var projectPath = new Option("--project", "-p") + { + Description = Strings.AddPkg_ProjectPathDescription, + Arity = ArgumentArity.ExactlyOne + }; + + var frameworks = new Option("--framework", "-f") + { + Description = Strings.AddPkg_FrameworksDescription, + Arity = ArgumentArity.OneOrMore + }; + + var noRestore = new Option("--no-restore", "-n") + { + Description = Strings.AddPkg_NoRestoreDescription, + Arity = ArgumentArity.Zero + }; + + var sources = new Option("--source", "-s") + { + Description = Strings.AddPkg_SourcesDescription, + Arity = ArgumentArity.OneOrMore + }; + + var packageDirectory = new Option("--package-directory") + { + Description = Strings.AddPkg_PackageDirectoryDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var interactive = new Option("--interactive") + { + Description = Strings.AddPkg_InteractiveDescription, + Arity = ArgumentArity.Zero + }; + + var prerelease = new Option("--prerelease") + { + Description = Strings.Prerelease_Description, + Arity = ArgumentArity.Zero + }; + + addCommand.Options.Add(id); + addCommand.Options.Add(version); + addCommand.Options.Add(dgFilePath); + addCommand.Options.Add(projectPath); + addCommand.Options.Add(frameworks); + addCommand.Options.Add(noRestore); + addCommand.Options.Add(sources); + addCommand.Options.Add(packageDirectory); + addCommand.Options.Add(interactive); + addCommand.Options.Add(prerelease); + + addCommand.SetAction(async (parseResult, cancellationToken) => { - addpkg.Description = Strings.AddPkg_Description; - addpkg.HelpOption(XPlatUtility.HelpOption); - - addpkg.Option( - CommandConstants.ForceEnglishOutputOption, - Strings.ForceEnglishOutput_Description, - CommandOptionType.NoValue); - - var id = addpkg.Option( - "--package", - Strings.AddPkg_PackageIdDescription, - CommandOptionType.SingleValue); - - var version = addpkg.Option( - "--version", - Strings.AddPkg_PackageVersionDescription, - CommandOptionType.SingleValue); - - var dgFilePath = addpkg.Option( - "-d|--dg-file", - Strings.AddPkg_DgFileDescription, - CommandOptionType.SingleValue); - - var projectPath = addpkg.Option( - "-p|--project", - Strings.AddPkg_ProjectPathDescription, - CommandOptionType.SingleValue); - - var frameworks = addpkg.Option( - "-f|--framework", - Strings.AddPkg_FrameworksDescription, - CommandOptionType.MultipleValue); - - var noRestore = addpkg.Option( - "-n|--no-restore", - Strings.AddPkg_NoRestoreDescription, - CommandOptionType.NoValue); - - var sources = addpkg.Option( - "-s|--source", - Strings.AddPkg_SourcesDescription, - CommandOptionType.MultipleValue); - - var packageDirectory = addpkg.Option( - "--package-directory", - Strings.AddPkg_PackageDirectoryDescription, - CommandOptionType.SingleValue); - - var interactive = addpkg.Option( - "--interactive", - Strings.AddPkg_InteractiveDescription, - CommandOptionType.NoValue); - - var prerelease = addpkg.Option( - "--prerelease", - Strings.Prerelease_Description, - CommandOptionType.NoValue); - - addpkg.OnExecute(() => + var virtualProjectBuilder = getVirtualProjectBuilder?.Invoke(); + + var idValue = parseResult.GetValue(id); + var projectPathValue = parseResult.GetValue(projectPath); + var dgFilePathValue = parseResult.GetValue(dgFilePath); + var noRestoreValue = parseResult.GetValue(noRestore); + var prereleaseValue = parseResult.GetValue(prerelease); + var versionValue = parseResult.GetValue(version); + + ValidateArgument(idValue, "--package", "add"); + ValidateArgument(projectPathValue, "--project", "add"); + ValidateProjectPath(projectPathValue, "add", virtualProjectBuilder); + if (!noRestoreValue) + { + ValidateArgument(dgFilePathValue, "--dg-file", "add"); + } + var logger = getLogger(); + var noVersion = string.IsNullOrEmpty(versionValue); + var packageVersion = !string.IsNullOrEmpty(versionValue) ? versionValue : null; + ValidatePrerelease(prereleaseValue, noVersion, "add"); + + var frameworkValues = parseResult.GetValue(frameworks) ?? Array.Empty(); + var sourceValues = parseResult.GetValue(sources) ?? Array.Empty(); + + var packageRefArgs = new PackageReferenceArgs(projectPathValue, logger) { - var virtualProjectBuilder = getVirtualProjectBuilder?.Invoke(); - - ValidateArgument(id, addpkg.Name); - ValidateArgument(projectPath, addpkg.Name); - ValidateProjectPath(projectPath, addpkg.Name, virtualProjectBuilder); - if (!noRestore.HasValue()) - { - ValidateArgument(dgFilePath, addpkg.Name); - } - var logger = getLogger(); - var noVersion = !version.HasValue(); - var packageVersion = version.HasValue() ? version.Value() : null; - ValidatePrerelease(prerelease.HasValue(), noVersion, addpkg.Name); - var packageRefArgs = new PackageReferenceArgs(projectPath.Value(), logger) - { - Frameworks = CommandLineUtility.SplitAndJoinAcrossMultipleValues(frameworks.Values), - Sources = CommandLineUtility.SplitAndJoinAcrossMultipleValues(sources.Values), - PackageDirectory = packageDirectory.Value(), - NoRestore = noRestore.HasValue(), - NoVersion = noVersion, - DgFilePath = dgFilePath.Value(), - Interactive = interactive.HasValue(), - Prerelease = prerelease.HasValue(), - PackageVersion = packageVersion, - PackageId = id.Values[0] - }; - var msBuild = new MSBuildAPIUtility(logger, virtualProjectBuilder); - - X509TrustStore.InitializeForDotNetSdk(logger); - - var addPackageRefCommandRunner = getCommandRunner(); - return addPackageRefCommandRunner.ExecuteCommand(packageRefArgs, msBuild); - }); + Frameworks = CommandLineUtility.SplitAndJoinAcrossMultipleValues(frameworkValues), + Sources = CommandLineUtility.SplitAndJoinAcrossMultipleValues(sourceValues), + PackageDirectory = parseResult.GetValue(packageDirectory) ?? string.Empty, + NoRestore = noRestoreValue, + NoVersion = noVersion, + DgFilePath = dgFilePathValue, + Interactive = parseResult.GetValue(interactive), + Prerelease = prereleaseValue, + PackageVersion = packageVersion!, + PackageId = idValue + }; + var msBuild = new MSBuildAPIUtility(logger, virtualProjectBuilder!); + + X509TrustStore.InitializeForDotNetSdk(logger); + + var addPackageRefCommandRunner = getCommandRunner(); + return await addPackageRefCommandRunner.ExecuteCommand(packageRefArgs, msBuild); }); + + parent.Subcommands.Add(addCommand); } private static void ValidatePrerelease(bool prerelease, bool noVersion, string commandName) @@ -125,26 +150,26 @@ private static void ValidatePrerelease(bool prerelease, bool noVersion, string c } } - private static void ValidateArgument(CommandOption arg, string commandName) + private static void ValidateArgument([NotNull] string? value, string optionName, string commandName) { - if (arg.Values.Count < 1) + if (string.IsNullOrEmpty(value)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_PkgMissingArgument, commandName, - arg.Template)); + optionName)); } } - private static void ValidateProjectPath(CommandOption projectPath, string commandName, IVirtualProjectBuilder? virtualProjectBuilder) + private static void ValidateProjectPath(string projectPath, string commandName, IVirtualProjectBuilder? virtualProjectBuilder) { - if (!File.Exists(projectPath.Value()) - || (!projectPath.Value().EndsWith("proj", StringComparison.OrdinalIgnoreCase) - && virtualProjectBuilder?.IsValidEntryPointPath(projectPath.Value()) != true)) + if (!File.Exists(projectPath) + || (!projectPath.EndsWith("proj", StringComparison.OrdinalIgnoreCase) + && virtualProjectBuilder?.IsValidEntryPointPath(projectPath) != true)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_PkgMissingOrInvalidProjectFile, commandName, - projectPath.Value())); + projectPath)); } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/ListPackage/ListPackageCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/ListPackage/ListPackageCommand.cs index 3d37371dc42..1a5bab20e3e 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/ListPackage/ListPackageCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/ListPackage/ListPackageCommand.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Generic; +using System.CommandLine; using System.Globalization; using System.IO; using System.Linq; using System.Threading; -using Microsoft.Extensions.CommandLineUtils; using NuGet.CommandLine.XPlat.ListPackage; using NuGet.Commands; using NuGet.Common; @@ -20,137 +20,169 @@ namespace NuGet.CommandLine.XPlat { internal static class ListPackageCommand { - public static void Register( - CommandLineApplication app, - Func getLogger, + internal static void Register( + Command parent, + Func getLogger, Action setLogLevel, - Func getCommandRunner) + Func getCommandRunner, + TextWriter? consoleOut = null, + TextWriter? consoleError = null) { - app.Command("list", listpkg => + var listCommand = new Command("list", Strings.ListPkg_Description); + + var path = new Argument("") { - listpkg.Description = Strings.ListPkg_Description; - listpkg.HelpOption(XPlatUtility.HelpOption); - - listpkg.Option( - CommandConstants.ForceEnglishOutputOption, - Strings.ForceEnglishOutput_Description, - CommandOptionType.NoValue); - - var path = listpkg.Argument( - "", - Strings.ListPkg_PathDescription, - multipleValues: false); - - var framework = listpkg.Option( - "--framework", - Strings.ListPkg_FrameworkDescription, - CommandOptionType.MultipleValue); - - var deprecatedReport = listpkg.Option( - "--deprecated", - Strings.ListPkg_DeprecatedDescription, - CommandOptionType.NoValue); - - var outdatedReport = listpkg.Option( - "--outdated", - Strings.ListPkg_OutdatedDescription, - CommandOptionType.NoValue); - - var vulnerableReport = listpkg.Option( - "--vulnerable", - Strings.ListPkg_VulnerableDescription, - CommandOptionType.NoValue); - - var includeTransitive = listpkg.Option( - "--include-transitive", - Strings.ListPkg_TransitiveDescription, - CommandOptionType.NoValue); - - var prerelease = listpkg.Option( - "--include-prerelease", - Strings.ListPkg_PrereleaseDescription, - CommandOptionType.NoValue); - - var highestPatch = listpkg.Option( - "--highest-patch", - Strings.ListPkg_HighestPatchDescription, - CommandOptionType.NoValue); - - var highestMinor = listpkg.Option( - "--highest-minor", - Strings.ListPkg_HighestMinorDescription, - CommandOptionType.NoValue); - - var source = listpkg.Option( - "--source", - Strings.ListPkg_SourceDescription, - CommandOptionType.MultipleValue); - - var config = listpkg.Option( - "--config", - Strings.ListPkg_ConfigDescription, - CommandOptionType.SingleValue); - - var outputFormat = listpkg.Option( - "--format", - Strings.ListPkg_OutputFormatDescription, - CommandOptionType.SingleValue); - - var outputVersion = listpkg.Option( - "--output-version", - Strings.ListPkg_OutputVersionDescription, - CommandOptionType.SingleValue); - - var interactive = listpkg.Option( - "--interactive", - Strings.NuGetXplatCommand_Interactive, - CommandOptionType.NoValue); - - var verbosity = listpkg.Option( - "-v|--verbosity", - Strings.Verbosity_Description, - CommandOptionType.SingleValue); - - listpkg.OnExecute(async () => - { - var logger = getLogger(); - - setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value())); - - var settings = ProcessConfigFile(config.Value(), path.Value); - var sources = source.Values; - - var packageSources = GetPackageSources(settings, sources, config); - - var reportType = GetReportType( - isOutdated: outdatedReport.HasValue(), - isDeprecated: deprecatedReport.HasValue(), - isVulnerable: vulnerableReport.HasValue()); - - IReportRenderer reportRenderer = GetOutputType(app.Out, app.Error, outputFormat.Value(), outputVersionOption: outputVersion.Value()); - var provider = new PackageSourceProvider(settings); - var packageRefArgs = new ListPackageArgs( - path.Value, - packageSources, - framework.Values, - reportType, - reportRenderer, - includeTransitive.HasValue(), - prerelease.HasValue(), - highestPatch.HasValue(), - highestMinor.HasValue(), - provider.LoadAuditSources(), - logger, - CancellationToken.None); - - WarnAboutIncompatibleOptions(packageRefArgs, reportRenderer); - - DefaultCredentialServiceUtility.SetupDefaultCredentialService(getLogger(), !interactive.HasValue()); - - var listPackageCommandRunner = getCommandRunner(); - return await listPackageCommandRunner.ExecuteCommandAsync(packageRefArgs); - }); + Description = Strings.ListPkg_PathDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var framework = new Option("--framework") + { + Description = Strings.ListPkg_FrameworkDescription, + Arity = ArgumentArity.OneOrMore + }; + + var deprecatedReport = new Option("--deprecated") + { + Description = Strings.ListPkg_DeprecatedDescription, + Arity = ArgumentArity.Zero + }; + + var outdatedReport = new Option("--outdated") + { + Description = Strings.ListPkg_OutdatedDescription, + Arity = ArgumentArity.Zero + }; + + var vulnerableReport = new Option("--vulnerable") + { + Description = Strings.ListPkg_VulnerableDescription, + Arity = ArgumentArity.Zero + }; + + var includeTransitive = new Option("--include-transitive") + { + Description = Strings.ListPkg_TransitiveDescription, + Arity = ArgumentArity.Zero + }; + + var prerelease = new Option("--include-prerelease") + { + Description = Strings.ListPkg_PrereleaseDescription, + Arity = ArgumentArity.Zero + }; + + var highestPatch = new Option("--highest-patch") + { + Description = Strings.ListPkg_HighestPatchDescription, + Arity = ArgumentArity.Zero + }; + + var highestMinor = new Option("--highest-minor") + { + Description = Strings.ListPkg_HighestMinorDescription, + Arity = ArgumentArity.Zero + }; + + var source = new Option("--source") + { + Description = Strings.ListPkg_SourceDescription, + Arity = ArgumentArity.OneOrMore + }; + + var config = new Option("--config") + { + Description = Strings.ListPkg_ConfigDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var outputFormat = new Option("--format") + { + Description = Strings.ListPkg_OutputFormatDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var outputVersion = new Option("--output-version") + { + Description = Strings.ListPkg_OutputVersionDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var interactive = new Option("--interactive") + { + Description = Strings.NuGetXplatCommand_Interactive, + Arity = ArgumentArity.Zero + }; + + var verbosity = new Option("--verbosity", "-v") + { + Description = Strings.Verbosity_Description, + Arity = ArgumentArity.ZeroOrOne + }; + + listCommand.Arguments.Add(path); + listCommand.Options.Add(framework); + listCommand.Options.Add(deprecatedReport); + listCommand.Options.Add(outdatedReport); + listCommand.Options.Add(vulnerableReport); + listCommand.Options.Add(includeTransitive); + listCommand.Options.Add(prerelease); + listCommand.Options.Add(highestPatch); + listCommand.Options.Add(highestMinor); + listCommand.Options.Add(source); + listCommand.Options.Add(config); + listCommand.Options.Add(outputFormat); + listCommand.Options.Add(outputVersion); + listCommand.Options.Add(interactive); + listCommand.Options.Add(verbosity); + + listCommand.SetAction(async (parseResult, cancellationToken) => + { + var logger = getLogger(); + + var verbosityValue = parseResult.GetValue(verbosity) ?? string.Empty; + setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosityValue)); + + var pathValue = parseResult.GetValue(path) ?? string.Empty; + var configValue = parseResult.GetValue(config); + var hasConfig = !string.IsNullOrEmpty(configValue); + + var settings = ProcessConfigFile(configValue, pathValue); + var sourceValues = parseResult.GetValue(source) ?? Array.Empty(); + + var packageSources = GetPackageSources(settings, sourceValues, hasConfig); + + var reportType = GetReportType( + isOutdated: parseResult.GetValue(outdatedReport), + isDeprecated: parseResult.GetValue(deprecatedReport), + isVulnerable: parseResult.GetValue(vulnerableReport)); + + IReportRenderer reportRenderer = GetOutputType(consoleOut ?? Console.Out, consoleError ?? Console.Error, parseResult.GetValue(outputFormat), outputVersionOption: parseResult.GetValue(outputVersion)); + var provider = new PackageSourceProvider(settings); + var frameworkValues = parseResult.GetValue(framework) ?? Array.Empty(); + var packageRefArgs = new ListPackageArgs( + pathValue, + packageSources, + frameworkValues.ToList(), + reportType, + reportRenderer, + parseResult.GetValue(includeTransitive), + parseResult.GetValue(prerelease), + parseResult.GetValue(highestPatch), + parseResult.GetValue(highestMinor), + provider.LoadAuditSources(), + logger, + CancellationToken.None); + + WarnAboutIncompatibleOptions(packageRefArgs, reportRenderer); + + DefaultCredentialServiceUtility.SetupDefaultCredentialService(getLogger(), !parseResult.GetValue(interactive)); + + var listPackageCommandRunner = getCommandRunner(); + return await listPackageCommandRunner.ExecuteCommandAsync(packageRefArgs); }); + + parent.Subcommands.Add(listCommand); } private static ReportType GetReportType(bool isDeprecated, bool isOutdated, bool isVulnerable) @@ -171,7 +203,7 @@ private static ReportType GetReportType(bool isDeprecated, bool isOutdated, bool throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.ListPkg_InvalidOptions)); } - private static IReportRenderer GetOutputType(TextWriter consoleOut, TextWriter consoleError, string outputFormatOption, string outputVersionOption) + private static IReportRenderer GetOutputType(TextWriter consoleOut, TextWriter consoleError, string? outputFormatOption, string? outputVersionOption) { ReportOutputFormat outputFormat = ReportOutputFormat.Console; if (!string.IsNullOrEmpty(outputFormatOption) && @@ -193,7 +225,6 @@ private static IReportRenderer GetOutputType(TextWriter consoleOut, TextWriter c IReportRenderer jsonReportRenderer; var currentlySupportedReportVersions = new List { "1" }; - // If customer pass unsupported version then error out instead of defaulting to version probably unsupported by customer machine. if (!string.IsNullOrEmpty(outputVersionOption) && !currentlySupportedReportVersions.Contains(outputVersionOption)) { throw new ArgumentException(string.Format(Strings.ListPkg_InvalidOutputVersion, outputVersionOption, string.Join(" ,", currentlySupportedReportVersions))); @@ -215,7 +246,7 @@ private static void WarnAboutIncompatibleOptions(ListPackageArgs packageRefArgs, } } - private static ISettings ProcessConfigFile(string configFile, string projectOrSolution) + private static ISettings ProcessConfigFile(string? configFile, string? projectOrSolution) { if (string.IsNullOrEmpty(configFile)) { @@ -229,10 +260,9 @@ private static ISettings ProcessConfigFile(string configFile, string projectOrSo directory, configFileName, machineWideSettings: new XPlatMachineWideSetting()); - } - private static List GetPackageSources(ISettings settings, IEnumerable sources, CommandOption config) + private static List GetPackageSources(ISettings settings, IEnumerable sources, bool hasConfig) { var availableSources = PackageSourceProvider.LoadPackageSources(settings).Where(source => source.IsEnabled); var uniqueSources = new HashSet(); @@ -247,7 +277,7 @@ private static List GetPackageSources(ISettings settings, IEnumer } } - if (packageSources.Count == 0 || config.HasValue()) + if (packageSources.Count == 0 || hasConfig) { packageSources.AddRange(availableSources); } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/RemovePackageReferenceCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/RemovePackageReferenceCommand.cs index 1d52ac7c25e..8752c1e38a3 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/RemovePackageReferenceCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/RemovePackageReferenceCommand.cs @@ -4,84 +4,87 @@ #nullable enable using System; +using System.CommandLine; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; -using Microsoft.Extensions.CommandLineUtils; -using NuGet.Common; namespace NuGet.CommandLine.XPlat { - internal class RemovePackageReferenceCommand + internal static class RemovePackageReferenceCommand { - public static void Register(CommandLineApplication app, Func getLogger, + internal static void Register(Command parent, Func getLogger, Func getCommandRunner, Func? getVirtualProjectBuilder = null) { - app.Command("remove", removePkg => + var removeCommand = new Command("remove", Strings.RemovePkg_Description); + + var id = new Option("--package") { - removePkg.Description = Strings.RemovePkg_Description; - removePkg.HelpOption(XPlatUtility.HelpOption); + Description = Strings.RemovePkg_PackageIdDescription, + Arity = ArgumentArity.ExactlyOne + }; - removePkg.Option( - CommandConstants.ForceEnglishOutputOption, - Strings.ForceEnglishOutput_Description, - CommandOptionType.NoValue); + var projectPath = new Option("--project", "-p") + { + Description = Strings.RemovePkg_ProjectPathDescription, + Arity = ArgumentArity.ExactlyOne + }; - var id = removePkg.Option( - "--package", - Strings.RemovePkg_PackageIdDescription, - CommandOptionType.SingleValue); + var interactive = new Option("--interactive") + { + Description = Strings.AddPkg_InteractiveDescription, + Arity = ArgumentArity.Zero + }; - var projectPath = removePkg.Option( - "-p|--project", - Strings.RemovePkg_ProjectPathDescription, - CommandOptionType.SingleValue); + removeCommand.Options.Add(id); + removeCommand.Options.Add(projectPath); + removeCommand.Options.Add(interactive); - var interactive = removePkg.Option( - "--interactive", - Strings.AddPkg_InteractiveDescription, - CommandOptionType.NoValue); + removeCommand.SetAction(async (parseResult, cancellationToken) => + { + var virtualProjectBuilder = getVirtualProjectBuilder?.Invoke(); - removePkg.OnExecute(() => - { - var virtualProjectBuilder = getVirtualProjectBuilder?.Invoke(); + var idValue = parseResult.GetValue(id); + var projectPathValue = parseResult.GetValue(projectPath); - ValidateArgument(id, removePkg.Name); - ValidateArgument(projectPath, removePkg.Name); - ValidateProjectPath(projectPath, removePkg.Name, virtualProjectBuilder); - var logger = getLogger(); - var packageRefArgs = new PackageReferenceArgs(projectPath.Value(), logger) - { - Interactive = interactive.HasValue(), - PackageId = id.Value() - }; - var msBuild = new MSBuildAPIUtility(logger, virtualProjectBuilder); - var removePackageRefCommandRunner = getCommandRunner(); - return removePackageRefCommandRunner.ExecuteCommand(packageRefArgs, msBuild); - }); + ValidateArgument(idValue, "--package", "remove"); + ValidateArgument(projectPathValue, "--project", "remove"); + ValidateProjectPath(projectPathValue, "remove", virtualProjectBuilder); + var logger = getLogger(); + var packageRefArgs = new PackageReferenceArgs(projectPathValue, logger) + { + Interactive = parseResult.GetValue(interactive), + PackageId = idValue + }; + var msBuild = new MSBuildAPIUtility(logger, virtualProjectBuilder!); + var removePackageRefCommandRunner = getCommandRunner(); + return await removePackageRefCommandRunner.ExecuteCommand(packageRefArgs, msBuild); }); + + parent.Subcommands.Add(removeCommand); } - private static void ValidateArgument(CommandOption arg, string commandName) + private static void ValidateArgument([NotNull] string? value, string optionName, string commandName) { - if (arg.Values.Count < 1) + if (string.IsNullOrEmpty(value)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_PkgMissingArgument, commandName, - arg.Template)); + optionName)); } } - private static void ValidateProjectPath(CommandOption projectPath, string commandName, IVirtualProjectBuilder? virtualProjectBuilder) + private static void ValidateProjectPath(string projectPath, string commandName, IVirtualProjectBuilder? virtualProjectBuilder) { - if (!File.Exists(projectPath.Value()) - || (!projectPath.Value().EndsWith("proj", StringComparison.OrdinalIgnoreCase) - && virtualProjectBuilder?.IsValidEntryPointPath(projectPath.Value()) != true)) + if (!File.Exists(projectPath) + || (!projectPath.EndsWith("proj", StringComparison.OrdinalIgnoreCase) + && virtualProjectBuilder?.IsValidEntryPointPath(projectPath) != true)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_PkgMissingOrInvalidProjectFile, commandName, - projectPath.Value())); + projectPath)); } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageSearch/PackageSearchCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageSearch/PackageSearchCommand.cs index 407f6d03163..c25f6f74f21 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageSearch/PackageSearchCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageSearch/PackageSearchCommand.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Help; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -87,11 +86,6 @@ public static void Register(Command rootCommand, Func getLogge Arity = ArgumentArity.ExactlyOne }; - var help = new HelpOption() - { - Arity = ArgumentArity.Zero - }; - searchCommand.Arguments.Add(searchTerm); searchCommand.Options.Add(sources); searchCommand.Options.Add(exactMatch); @@ -102,7 +96,6 @@ public static void Register(Command rootCommand, Func getLogge searchCommand.Options.Add(format); searchCommand.Options.Add(verbosity); searchCommand.Options.Add(configFile); - searchCommand.Options.Add(help); searchCommand.SetAction(async (parserResult, cancelationToken) => { diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PushCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PushCommand.cs index bc09a2f2578..3b55aeb92f4 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PushCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PushCommand.cs @@ -4,11 +4,9 @@ #nullable enable using System; -using System.Collections.Generic; +using System.CommandLine; using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; using NuGet.Commands; -using NuGet.Common; using NuGet.Configuration; using NuGet.Credentials; @@ -16,139 +14,162 @@ namespace NuGet.CommandLine.XPlat { internal static class PushCommand { - public static void Register(CommandLineApplication app, Func getLogger) + internal static void Register(Command parent, Func getLogger) { - app.Command("push", push => + var pushCmd = new Command("push", Strings.Push_Description); + + var sourceOption = new Option("--source", "-s") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.Source_Description, + }; + + var allowInsecureConnectionsOption = new Option("--allow-insecure-connections") + { + Arity = ArgumentArity.Zero, + Description = Strings.AllowInsecureConnections_Description, + }; + + var symbolSourceOption = new Option("--symbol-source", "-ss") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.SymbolSource_Description, + }; + + var timeoutOption = new Option("--timeout", "-t") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.Push_Timeout_Description, + }; + + var apiKeyOption = new Option("--api-key", "-k") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.ApiKey_Description, + }; + + var symbolApiKeyOption = new Option("--symbol-api-key", "-sk") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.SymbolApiKey_Description, + }; + + var disableBufferingOption = new Option("--disable-buffering", "-d") + { + Arity = ArgumentArity.Zero, + Description = Strings.DisableBuffering_Description, + }; + + var noSymbolsOption = new Option("--no-symbols", "-n") { - push.Description = Strings.Push_Description; - push.HelpOption(XPlatUtility.HelpOption); - - push.Option( - CommandConstants.ForceEnglishOutputOption, - Strings.ForceEnglishOutput_Description, - CommandOptionType.NoValue); - - var source = push.Option( - "-s|--source ", - Strings.Source_Description, - CommandOptionType.SingleValue); - - var allowInsecureConnections = push.Option( - "--allow-insecure-connections", - Strings.AllowInsecureConnections_Description, - CommandOptionType.NoValue); - - var symbolSource = push.Option( - "-ss|--symbol-source ", - Strings.SymbolSource_Description, - CommandOptionType.SingleValue); - - var timeout = push.Option( - "-t|--timeout ", - Strings.Push_Timeout_Description, - CommandOptionType.SingleValue); - - var apikey = push.Option( - "-k|--api-key ", - Strings.ApiKey_Description, - CommandOptionType.SingleValue); - - var symbolApiKey = push.Option( - "-sk|--symbol-api-key ", - Strings.SymbolApiKey_Description, - CommandOptionType.SingleValue); - - var disableBuffering = push.Option( - "-d|--disable-buffering", - Strings.DisableBuffering_Description, - CommandOptionType.NoValue); - - var noSymbols = push.Option( - "-n|--no-symbols", - Strings.NoSymbols_Description, - CommandOptionType.NoValue); - - var arguments = push.Argument( - "[root]", - Strings.Push_Package_ApiKey_Description, - multipleValues: true); - - var noServiceEndpointDescription = push.Option( - "--no-service-endpoint", - Strings.NoServiceEndpoint_Description, - CommandOptionType.NoValue); - - var interactive = push.Option( - "--interactive", - Strings.NuGetXplatCommand_Interactive, - CommandOptionType.NoValue); - - var skipDuplicate = push.Option( - "--skip-duplicate", - Strings.PushCommandSkipDuplicateDescription, - CommandOptionType.NoValue); - - var configurationFile = push.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - push.OnExecute(async () => + Arity = ArgumentArity.Zero, + Description = Strings.NoSymbols_Description, + }; + + var noServiceEndpointOption = new Option("--no-service-endpoint") + { + Arity = ArgumentArity.Zero, + Description = Strings.NoServiceEndpoint_Description, + }; + + var interactiveOption = new Option("--interactive") + { + Arity = ArgumentArity.Zero, + Description = Strings.NuGetXplatCommand_Interactive, + }; + + var skipDuplicateOption = new Option("--skip-duplicate") + { + Arity = ArgumentArity.Zero, + Description = Strings.PushCommandSkipDuplicateDescription, + }; + + var configFileOption = new Option("--configfile") + { + Arity = ArgumentArity.ZeroOrOne, + Description = Strings.Option_ConfigFile, + }; + + var packagePathsArgument = new Argument("package-paths") + { + Arity = ArgumentArity.OneOrMore, + Description = Strings.Push_Package_ApiKey_Description, + }; + + pushCmd.Options.Add(sourceOption); + pushCmd.Options.Add(allowInsecureConnectionsOption); + pushCmd.Options.Add(symbolSourceOption); + pushCmd.Options.Add(timeoutOption); + pushCmd.Options.Add(apiKeyOption); + pushCmd.Options.Add(symbolApiKeyOption); + pushCmd.Options.Add(disableBufferingOption); + pushCmd.Options.Add(noSymbolsOption); + pushCmd.Options.Add(noServiceEndpointOption); + pushCmd.Options.Add(interactiveOption); + pushCmd.Options.Add(skipDuplicateOption); + pushCmd.Options.Add(configFileOption); + pushCmd.Arguments.Add(packagePathsArgument); + + pushCmd.SetAction(async (parseResult, cancellationToken) => + { + string[]? packagePaths = parseResult.GetValue(packagePathsArgument); + if (packagePaths == null || packagePaths.Length < 1) { - if (arguments.Values.Count < 1) - { - throw new ArgumentException(Strings.Push_MissingArguments); - } - - IList packagePaths = arguments.Values; - string sourcePath = source.Value(); - string apiKeyValue = apikey.Value(); - string symbolSourcePath = symbolSource.Value(); - string symbolApiKeyValue = symbolApiKey.Value(); - bool disableBufferingValue = disableBuffering.HasValue(); - bool noSymbolsValue = noSymbols.HasValue(); - bool noServiceEndpoint = noServiceEndpointDescription.HasValue(); - bool skipDuplicateValue = skipDuplicate.HasValue(); - bool allowInsecureConnectionsValue = allowInsecureConnections.HasValue(); - int timeoutSeconds = 0; - - if (timeout.HasValue() && !int.TryParse(timeout.Value(), out timeoutSeconds)) - { - throw new ArgumentException(Strings.Push_InvalidTimeout); - } + throw new ArgumentException(Strings.Push_MissingArguments); + } + + string? sourcePath = parseResult.GetValue(sourceOption); + string? apiKeyValue = parseResult.GetValue(apiKeyOption); + string? symbolSourcePath = parseResult.GetValue(symbolSourceOption); + string? symbolApiKeyValue = parseResult.GetValue(symbolApiKeyOption); + bool disableBufferingValue = parseResult.GetValue(disableBufferingOption); + bool noSymbolsValue = parseResult.GetValue(noSymbolsOption); + bool noServiceEndpoint = parseResult.GetValue(noServiceEndpointOption); + bool skipDuplicateValue = parseResult.GetValue(skipDuplicateOption); + bool allowInsecureConnectionsValue = parseResult.GetValue(allowInsecureConnectionsOption); + bool interactiveValue = parseResult.GetValue(interactiveOption); + string? timeoutValue = parseResult.GetValue(timeoutOption); + string? configFile = parseResult.GetValue(configFileOption); + int timeoutSeconds = 0; + + if (!string.IsNullOrEmpty(timeoutValue) && !int.TryParse(timeoutValue, out timeoutSeconds)) + { + throw new ArgumentException(Strings.Push_InvalidTimeout); + } #pragma warning disable CS0618 // Type or member is obsolete - var sourceProvider = new PackageSourceProvider(XPlatUtility.ProcessConfigFile(configurationFile.Value()), enablePackageSourcesChangedEvent: false); + var sourceProvider = new PackageSourceProvider(XPlatUtility.ProcessConfigFile(configFile), enablePackageSourcesChangedEvent: false); #pragma warning restore CS0618 // Type or member is obsolete - try - { - DefaultCredentialServiceUtility.SetupDefaultCredentialService(getLogger(), !interactive.HasValue()); - - await PushRunner.Run( - sourceProvider.Settings, - sourceProvider, - packagePaths, - sourcePath, - apiKeyValue, - symbolSourcePath, - symbolApiKeyValue, - timeoutSeconds, - disableBufferingValue, - noSymbolsValue, - noServiceEndpoint, - skipDuplicateValue, - allowInsecureConnectionsValue, - getLogger()); - } - catch (TaskCanceledException ex) - { - throw new AggregateException(ex, new Exception(Strings.Push_Timeout_Error)); - } - - return 0; - }); + try + { + DefaultCredentialServiceUtility.SetupDefaultCredentialService(getLogger(), !interactiveValue); + + await PushRunner.Run( + sourceProvider.Settings, + sourceProvider, + packagePaths, + sourcePath, + apiKeyValue, + symbolSourcePath, + symbolApiKeyValue, + timeoutSeconds, + disableBufferingValue, + noSymbolsValue, + noServiceEndpoint, + skipDuplicateValue, + allowInsecureConnectionsValue, + getLogger()); + } + catch (TaskCanceledException ex) + { + throw new AggregateException(ex, new Exception(Strings.Push_Timeout_Error)); + } + + return 0; }); + + parent.Subcommands.Add(pushCmd); } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/SignCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/SignCommand.cs index 4041a3ddbe2..0abdca7d8b2 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/SignCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/SignCommand.cs @@ -4,11 +4,13 @@ #nullable enable using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Security.Cryptography.X509Certificates; -using Microsoft.Extensions.CommandLineUtils; using NuGet.Commands; using NuGet.Common; using NuGet.Packaging.Signing; @@ -19,177 +21,212 @@ internal static class SignCommand { private const string CommandName = "sign"; - internal static void Register(CommandLineApplication app, - Func getLogger, + internal static void Register(Command parent, + Func getLogger, Action setLogLevel, Func getCommandRunner) { - app.Command(CommandName, signCmd => + var signCmd = new Command(CommandName, Strings.SignCommandDescription); + + var packagePaths = new Argument("package-paths") + { + Description = Strings.SignCommandPackagePathDescription, + Arity = ArgumentArity.OneOrMore + }; + + var outputDirectory = new Option("--output", "-o") + { + Description = Strings.SignCommandOutputDirectoryDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var path = new Option("--certificate-path") + { + Description = Strings.SignCommandCertificatePathDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var store = new Option("--certificate-store-name") + { + Description = Strings.SignCommandCertificateStoreNameDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var location = new Option("--certificate-store-location") + { + Description = Strings.SignCommandCertificateStoreLocationDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var subject = new Option("--certificate-subject-name") + { + Description = Strings.SignCommandCertificateSubjectNameDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var fingerprint = new Option("--certificate-fingerprint") + { + Description = Strings.SignCommandCertificateFingerprintDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var password = new Option("--certificate-password") { - CommandArgument packagePaths = signCmd.Argument( - "", - Strings.SignCommandPackagePathDescription, - multipleValues: true); - - CommandOption outputDirectory = signCmd.Option( - "-o|--output", - Strings.SignCommandOutputDirectoryDescription, - CommandOptionType.SingleValue); - - CommandOption path = signCmd.Option( - "--certificate-path", - Strings.SignCommandCertificatePathDescription, - CommandOptionType.SingleValue); - - CommandOption store = signCmd.Option( - "--certificate-store-name", - Strings.SignCommandCertificateStoreNameDescription, - CommandOptionType.SingleValue); - - CommandOption location = signCmd.Option( - "--certificate-store-location", - Strings.SignCommandCertificateStoreLocationDescription, - CommandOptionType.SingleValue); - - CommandOption subject = signCmd.Option( - "--certificate-subject-name", - Strings.SignCommandCertificateSubjectNameDescription, - CommandOptionType.SingleValue); - - CommandOption fingerprint = signCmd.Option( - "--certificate-fingerprint", - Strings.SignCommandCertificateFingerprintDescription, - CommandOptionType.SingleValue); - - CommandOption password = signCmd.Option( - "--certificate-password", - Strings.SignCommandCertificatePasswordDescription, - CommandOptionType.SingleValue); - - CommandOption algorithm = signCmd.Option( - "--hash-algorithm", - Strings.SignCommandHashAlgorithmDescription, - CommandOptionType.SingleValue); - - CommandOption timestamper = signCmd.Option( - "--timestamper", - Strings.SignCommandTimestamperDescription, - CommandOptionType.SingleValue); - - CommandOption timestamperAlgorithm = signCmd.Option( - "--timestamp-hash-algorithm", - Strings.SignCommandTimestampHashAlgorithmDescription, - CommandOptionType.SingleValue); - - CommandOption overwrite = signCmd.Option( - "--overwrite", - Strings.SignCommandOverwriteDescription, - CommandOptionType.NoValue); - - CommandOption allowUntrustedRoot = signCmd.Option( - "--allow-untrusted-root", - Strings.SignCommandAllowUntrustedRootDescription, - CommandOptionType.NoValue); - - CommandOption verbosity = signCmd.Option( - "-v|--verbosity", - Strings.Verbosity_Description, - CommandOptionType.SingleValue); - - signCmd.HelpOption(XPlatUtility.HelpOption); - - signCmd.Description = Strings.SignCommandDescription; - - signCmd.OnExecute(async () => + Description = Strings.SignCommandCertificatePasswordDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var algorithm = new Option("--hash-algorithm") + { + Description = Strings.SignCommandHashAlgorithmDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var timestamper = new Option("--timestamper") + { + Description = Strings.SignCommandTimestamperDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var timestamperAlgorithm = new Option("--timestamp-hash-algorithm") + { + Description = Strings.SignCommandTimestampHashAlgorithmDescription, + Arity = ArgumentArity.ZeroOrOne + }; + + var overwrite = new Option("--overwrite") + { + Description = Strings.SignCommandOverwriteDescription, + Arity = ArgumentArity.Zero + }; + + var allowUntrustedRoot = new Option("--allow-untrusted-root") + { + Description = Strings.SignCommandAllowUntrustedRootDescription, + Arity = ArgumentArity.Zero + }; + + var verbosity = new Option("--verbosity", "-v") + { + Description = Strings.Verbosity_Description, + Arity = ArgumentArity.ZeroOrOne + }; + + signCmd.Arguments.Add(packagePaths); + signCmd.Options.Add(outputDirectory); + signCmd.Options.Add(path); + signCmd.Options.Add(store); + signCmd.Options.Add(location); + signCmd.Options.Add(subject); + signCmd.Options.Add(fingerprint); + signCmd.Options.Add(password); + signCmd.Options.Add(algorithm); + signCmd.Options.Add(timestamper); + signCmd.Options.Add(timestamperAlgorithm); + signCmd.Options.Add(overwrite); + signCmd.Options.Add(allowUntrustedRoot); + signCmd.Options.Add(verbosity); + + signCmd.SetAction(async (parseResult, cancellationToken) => + { + ILogger logger = getLogger(); + + string[]? packagePathValues = parseResult.GetValue(packagePaths); + string? pathValue = parseResult.GetValue(path); + string? fingerprintValue = parseResult.GetValue(fingerprint); + string? subjectValue = parseResult.GetValue(subject); + string? storeValue = parseResult.GetValue(store); + string? locationValue = parseResult.GetValue(location); + string? outputDirectoryValue = parseResult.GetValue(outputDirectory); + string? timestamperValue = parseResult.GetValue(timestamper); + string? algorithmValue = parseResult.GetValue(algorithm); + string? timestamperAlgorithmValue = parseResult.GetValue(timestamperAlgorithm); + + ValidatePackagePaths(packagePathValues, "package-paths"); + WarnIfNoTimestamper(logger, timestamperValue); + ValidateCertificateInputs(pathValue, fingerprintValue, subjectValue, storeValue, locationValue, logger); + ValidateAndCreateOutputDirectory(outputDirectoryValue); + + SigningSpecificationsV1 signingSpec = SigningSpecifications.V1; + StoreLocation storeLocation = ValidateAndParseStoreLocation(locationValue); + StoreName storeName = ValidateAndParseStoreName(storeValue); + HashAlgorithmName hashAlgorithm = CommandLineUtility.ParseAndValidateHashAlgorithm(algorithmValue, "--hash-algorithm", signingSpec); + HashAlgorithmName timestampHashAlgorithm = CommandLineUtility.ParseAndValidateHashAlgorithm(timestamperAlgorithmValue, "--timestamp-hash-algorithm", signingSpec); + + var args = new SignArgs() { - ILogger logger = getLogger(); - - ValidatePackagePaths(packagePaths); - WarnIfNoTimestamper(logger, timestamper); - ValidateCertificateInputs(path, fingerprint, subject, store, location, logger); - ValidateAndCreateOutputDirectory(outputDirectory); - - SigningSpecificationsV1 signingSpec = SigningSpecifications.V1; - StoreLocation storeLocation = ValidateAndParseStoreLocation(location); - StoreName storeName = ValidateAndParseStoreName(store); - HashAlgorithmName hashAlgorithm = CommandLineUtility.ParseAndValidateHashAlgorithm(algorithm.Value(), algorithm.LongName, signingSpec); - HashAlgorithmName timestampHashAlgorithm = CommandLineUtility.ParseAndValidateHashAlgorithm(timestamperAlgorithm.Value(), timestamperAlgorithm.LongName, signingSpec); - - var args = new SignArgs() - { - PackagePaths = packagePaths.Values, - OutputDirectory = outputDirectory.Value(), - CertificatePath = path.Value(), - CertificateStoreName = storeName, - CertificateStoreLocation = storeLocation, - CertificateSubjectName = subject.Value(), - CertificateFingerprint = fingerprint.Value(), - CertificatePassword = password.Value(), - SignatureHashAlgorithm = hashAlgorithm, - Logger = logger, - Overwrite = overwrite.HasValue(), - AllowUntrustedRoot = allowUntrustedRoot.HasValue(), - //The interactive option is not enabled at first, so the NonInteractive is always set to true. This is tracked by https://github.com/NuGet/Home/issues/10620 - NonInteractive = true, - Timestamper = timestamper.Value(), - TimestampHashAlgorithm = timestampHashAlgorithm - }; - - setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value())); - - X509TrustStore.InitializeForDotNetSdk(args.Logger); - - ISignCommandRunner runner = getCommandRunner(); - int result = await runner.ExecuteCommandAsync(args); - return result; - }); + PackagePaths = new List(packagePathValues), + OutputDirectory = outputDirectoryValue, + CertificatePath = pathValue, + CertificateStoreName = storeName, + CertificateStoreLocation = storeLocation, + CertificateSubjectName = subjectValue, + CertificateFingerprint = fingerprintValue, + CertificatePassword = parseResult.GetValue(password), + SignatureHashAlgorithm = hashAlgorithm, + Logger = logger, + Overwrite = parseResult.GetValue(overwrite), + AllowUntrustedRoot = parseResult.GetValue(allowUntrustedRoot), + //The interactive option is not enabled at first, so the NonInteractive is always set to true. This is tracked by https://github.com/NuGet/Home/issues/10620 + NonInteractive = true, + Timestamper = timestamperValue, + TimestampHashAlgorithm = timestampHashAlgorithm + }; + + setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(parseResult.GetValue(verbosity))); + + X509TrustStore.InitializeForDotNetSdk(args.Logger); + + ISignCommandRunner runner = getCommandRunner(); + int result = await runner.ExecuteCommandAsync(args); + return result; }); + + parent.Subcommands.Add(signCmd); } - private static void ValidatePackagePaths(CommandArgument argument) + private static void ValidatePackagePaths([NotNull] string[]? packagePaths, string argumentName) { - if (argument.Values.Count == 0 || - argument.Values.Any(packagePath => string.IsNullOrEmpty(packagePath))) + if (packagePaths == null || + packagePaths.Length == 0 || + packagePaths.Any(packagePath => string.IsNullOrEmpty(packagePath))) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_PkgMissingArgument, CommandName, - argument.Name)); + argumentName)); } } - private static void WarnIfNoTimestamper(ILogger logger, CommandOption timeStamper) + private static void WarnIfNoTimestamper(ILogger logger, string? timestamper) { - if (!timeStamper.HasValue()) + if (string.IsNullOrEmpty(timestamper)) { logger.Log(LogMessage.CreateWarning(NuGetLogCode.NU3002, Strings.SignCommandNoTimestamperWarning)); } } - private static void ValidateAndCreateOutputDirectory(CommandOption output) + private static void ValidateAndCreateOutputDirectory(string? outputDirectory) { - if (output.HasValue()) + if (!string.IsNullOrEmpty(outputDirectory)) { - string outputDir = output.Value(); - - if (!Directory.Exists(outputDir)) + if (!Directory.Exists(outputDirectory)) { - Directory.CreateDirectory(outputDir); + Directory.CreateDirectory(outputDirectory); } } } - private static StoreLocation ValidateAndParseStoreLocation(CommandOption location) + private static StoreLocation ValidateAndParseStoreLocation(string? locationValue) { StoreLocation storeLocation = StoreLocation.CurrentUser; - if (location.HasValue()) + if (!string.IsNullOrEmpty(locationValue)) { - if (!string.IsNullOrEmpty(location.Value()) && - !Enum.TryParse(location.Value(), ignoreCase: true, result: out storeLocation)) + if (!Enum.TryParse(locationValue, ignoreCase: true, result: out storeLocation)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Err_InvalidValue, - location.LongName, + "--certificate-store-location", string.Join(",", Enum.GetValues().ToList()))); } } @@ -197,18 +234,17 @@ private static StoreLocation ValidateAndParseStoreLocation(CommandOption locatio return storeLocation; } - private static StoreName ValidateAndParseStoreName(CommandOption store) + private static StoreName ValidateAndParseStoreName(string? storeValue) { StoreName storeName = StoreName.My; - if (store.HasValue()) + if (!string.IsNullOrEmpty(storeValue)) { - if (!string.IsNullOrEmpty(store.Value()) && - !Enum.TryParse(store.Value(), ignoreCase: true, result: out storeName)) + if (!Enum.TryParse(storeValue, ignoreCase: true, result: out storeName)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Err_InvalidValue, - store.LongName, + "--certificate-store-name", string.Join(",", Enum.GetValues().ToList()))); } } @@ -216,33 +252,33 @@ private static StoreName ValidateAndParseStoreName(CommandOption store) return storeName; } - private static void ValidateCertificateInputs(CommandOption path, CommandOption fingerprint, - CommandOption subject, CommandOption store, CommandOption location, ILogger logger) + private static void ValidateCertificateInputs(string? path, string? fingerprint, + string? subject, string? store, string? location, ILogger logger) { - if (string.IsNullOrEmpty(path.Value()) && - string.IsNullOrEmpty(fingerprint.Value()) && - string.IsNullOrEmpty(subject.Value())) + if (string.IsNullOrEmpty(path) && + string.IsNullOrEmpty(fingerprint) && + string.IsNullOrEmpty(subject)) { // Throw if user gave no certificate input throw new ArgumentException(Strings.SignCommandNoCertificateException); } - else if (!string.IsNullOrEmpty(path.Value()) && - (!string.IsNullOrEmpty(fingerprint.Value()) || - !string.IsNullOrEmpty(subject.Value()) || - !string.IsNullOrEmpty(location.Value()) || - !string.IsNullOrEmpty(store.Value()))) + else if (!string.IsNullOrEmpty(path) && + (!string.IsNullOrEmpty(fingerprint) || + !string.IsNullOrEmpty(subject) || + !string.IsNullOrEmpty(location) || + !string.IsNullOrEmpty(store))) { // Throw if the user provided a path and any one of the other options throw new ArgumentException(Strings.SignCommandMultipleCertificateException); } - else if (!string.IsNullOrEmpty(fingerprint.Value()) && !string.IsNullOrEmpty(subject.Value())) + else if (!string.IsNullOrEmpty(fingerprint) && !string.IsNullOrEmpty(subject)) { // Throw if the user provided a fingerprint and a subject throw new ArgumentException(Strings.SignCommandMultipleCertificateException); } - else if (fingerprint.Value() != null) + else if (fingerprint != null) { - bool isValidFingerprint = CertificateUtility.TryDeduceHashAlgorithm(fingerprint.Value(), out HashAlgorithmName hashAlgorithmName); + bool isValidFingerprint = CertificateUtility.TryDeduceHashAlgorithm(fingerprint, out HashAlgorithmName hashAlgorithmName); bool isSHA1 = hashAlgorithmName == HashAlgorithmName.SHA1; string message = string.Format(CultureInfo.CurrentCulture, Strings.SignCommandInvalidCertificateFingerprint, NuGetLogCode.NU3043); diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/TrustedSignersCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/TrustedSignersCommand.cs index a94852d97f5..2b0a00f8e54 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/TrustedSignersCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/TrustedSignersCommand.cs @@ -4,9 +4,9 @@ #nullable disable using System; +using System.CommandLine; using System.Globalization; using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; using NuGet.Commands; using NuGet.Common; using NuGet.Configuration; @@ -17,228 +17,179 @@ namespace NuGet.CommandLine.XPlat { internal static class TrustedSignersCommand { - internal static void Register(CommandLineApplication app, - Func getLogger, + internal static void Register(Command parent, + Func getLogger, Action setLogLevel) { - app.Command("trust", trustedSignersCmd => - { - // sub-commands - trustedSignersCmd.Command("list", (listCommand) => - { - listCommand.Description = Strings.TrustListCommandDescription; - CommandOption configFile = listCommand.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); + var trustedSignersCmd = new Command("trust", Strings.TrustCommandDescription); - listCommand.HelpOption(XPlatUtility.HelpOption); + // --- list subcommand --- + var listCommand = new Command("list", Strings.TrustListCommandDescription); + { + var configFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var verbosity = CreateVerbosityOption(); - CommandOption verbosity = listCommand.VerbosityOption(); + listCommand.Options.Add(configFile); + listCommand.Options.Add(verbosity); - listCommand.OnExecute(async () => - { - return await ExecuteCommand(TrustCommand.List, algorithm: null, allowUntrustedRootOption: false, owners: null, verbosity, configFile, getLogger, setLogLevel); - }); - }); - - trustedSignersCmd.Command("sync", (syncCommand) => + listCommand.SetAction(async (parseResult, cancellationToken) => { - syncCommand.Description = Strings.TrustSyncCommandDescription; - CommandOption configFile = syncCommand.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - syncCommand.HelpOption(XPlatUtility.HelpOption); - - CommandOption verbosity = syncCommand.VerbosityOption(); ; + return await ExecuteCommand(TrustCommand.List, algorithm: null, allowUntrustedRootOption: false, owners: null, parseResult.GetValue(verbosity), parseResult.GetValue(configFile), getLogger, setLogLevel); + }); + } + trustedSignersCmd.Subcommands.Add(listCommand); - CommandArgument name = syncCommand.Argument("", - Strings.TrustedSignerNameExists); + // --- sync subcommand --- + var syncCommand = new Command("sync", Strings.TrustSyncCommandDescription); + { + var name = new Argument("NAME") { Description = Strings.TrustedSignerNameExists }; + var configFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var verbosity = CreateVerbosityOption(); - syncCommand.OnExecute(async () => - { - return await ExecuteCommand(TrustCommand.Sync, algorithm: null, allowUntrustedRootOption: false, owners: null, verbosity, configFile, getLogger, setLogLevel, name: name.Value); - }); - }); + syncCommand.Arguments.Add(name); + syncCommand.Options.Add(configFile); + syncCommand.Options.Add(verbosity); - trustedSignersCmd.Command("remove", (syncCommand) => + syncCommand.SetAction(async (parseResult, cancellationToken) => { - syncCommand.Description = Strings.TrustRemoveCommandDescription; - CommandOption configFile = syncCommand.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - syncCommand.HelpOption(XPlatUtility.HelpOption); + return await ExecuteCommand(TrustCommand.Sync, algorithm: null, allowUntrustedRootOption: false, owners: null, parseResult.GetValue(verbosity), parseResult.GetValue(configFile), getLogger, setLogLevel, name: parseResult.GetValue(name)); + }); + } + trustedSignersCmd.Subcommands.Add(syncCommand); - CommandOption verbosity = syncCommand.VerbosityOption(); + // --- remove subcommand --- + var removeCommand = new Command("remove", Strings.TrustRemoveCommandDescription); + { + var name = new Argument("NAME") { Description = Strings.TrustedSignerNameToRemove }; + var configFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var verbosity = CreateVerbosityOption(); - CommandArgument name = syncCommand.Argument("", - Strings.TrustedSignerNameToRemove); + removeCommand.Arguments.Add(name); + removeCommand.Options.Add(configFile); + removeCommand.Options.Add(verbosity); - syncCommand.OnExecute(async () => - { - return await ExecuteCommand(TrustCommand.Remove, algorithm: null, allowUntrustedRootOption: false, owners: null, verbosity, configFile, getLogger, setLogLevel, name: name.Value); - }); + removeCommand.SetAction(async (parseResult, cancellationToken) => + { + return await ExecuteCommand(TrustCommand.Remove, algorithm: null, allowUntrustedRootOption: false, owners: null, parseResult.GetValue(verbosity), parseResult.GetValue(configFile), getLogger, setLogLevel, name: parseResult.GetValue(name)); }); + } + trustedSignersCmd.Subcommands.Add(removeCommand); - trustedSignersCmd.Command("author", (authorCommand) => + // --- author subcommand --- + var authorCommand = new Command("author", Strings.TrustAuthorCommandDescription); + { + var name = new Argument("NAME") { Description = Strings.TrustedSignerNameToAdd }; + var package = new Argument("PACKAGE") { Description = Strings.TrustLocalSignedNupkgPath }; + var allowUntrustedRootOption = new Option("--allow-untrusted-root") { Description = Strings.TrustCommandAllowUntrustedRoot, Arity = ArgumentArity.Zero }; + var configFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var verbosity = CreateVerbosityOption(); + + authorCommand.Arguments.Add(name); + authorCommand.Arguments.Add(package); + authorCommand.Options.Add(allowUntrustedRootOption); + authorCommand.Options.Add(configFile); + authorCommand.Options.Add(verbosity); + + authorCommand.SetAction(async (parseResult, cancellationToken) => { - authorCommand.Description = Strings.TrustAuthorCommandDescription; - - CommandOption allowUntrustedRootOption = authorCommand.Option( - "--allow-untrusted-root", - Strings.TrustCommandAllowUntrustedRoot, - CommandOptionType.NoValue); - - CommandOption configFile = authorCommand.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - authorCommand.HelpOption(XPlatUtility.HelpOption); - - CommandOption verbosity = authorCommand.VerbosityOption(); - - CommandArgument name = authorCommand.Argument("", - Strings.TrustedSignerNameToAdd); - CommandArgument package = authorCommand.Argument("", - Strings.TrustLocalSignedNupkgPath); - - authorCommand.OnExecute(async () => - { - return await ExecuteCommand(TrustCommand.Author, algorithm: null, allowUntrustedRootOption.HasValue(), owners: null, verbosity, configFile, getLogger, setLogLevel, name: name.Value, sourceUrl: null, packagePath: package.Value); - }); + return await ExecuteCommand(TrustCommand.Author, algorithm: null, parseResult.GetValue(allowUntrustedRootOption), owners: null, parseResult.GetValue(verbosity), parseResult.GetValue(configFile), getLogger, setLogLevel, name: parseResult.GetValue(name), sourceUrl: null, packagePath: parseResult.GetValue(package)); }); + } + trustedSignersCmd.Subcommands.Add(authorCommand); - trustedSignersCmd.Command("repository", (repositoryCommand) => + // --- repository subcommand --- + var repositoryCommand = new Command("repository", Strings.TrustRepositoryCommandDescription); + { + var name = new Argument("NAME") { Description = Strings.TrustedSignerNameToAdd }; + var package = new Argument("PACKAGE") { Description = Strings.TrustLocalSignedNupkgPath }; + var allowUntrustedRootOption = new Option("--allow-untrusted-root") { Description = Strings.TrustCommandAllowUntrustedRoot, Arity = ArgumentArity.Zero }; + var owners = new Option("--owners") { Description = Strings.TrustCommandOwners, Arity = ArgumentArity.ZeroOrOne }; + var configFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var verbosity = CreateVerbosityOption(); + + repositoryCommand.Arguments.Add(name); + repositoryCommand.Arguments.Add(package); + repositoryCommand.Options.Add(allowUntrustedRootOption); + repositoryCommand.Options.Add(owners); + repositoryCommand.Options.Add(configFile); + repositoryCommand.Options.Add(verbosity); + + repositoryCommand.SetAction(async (parseResult, cancellationToken) => { - repositoryCommand.Description = Strings.TrustRepositoryCommandDescription; - - CommandOption allowUntrustedRootOption = repositoryCommand.Option( - "--allow-untrusted-root", - Strings.TrustCommandAllowUntrustedRoot, - CommandOptionType.NoValue); - - CommandOption configFile = repositoryCommand.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - repositoryCommand.HelpOption(XPlatUtility.HelpOption); - - CommandOption owners = repositoryCommand.Option( - "--owners", - Strings.TrustCommandOwners, - CommandOptionType.SingleValue); - - CommandOption verbosity = repositoryCommand.VerbosityOption(); - - CommandArgument name = repositoryCommand.Argument("", - Strings.TrustedSignerNameToAdd); - CommandArgument package = repositoryCommand.Argument("", - Strings.TrustLocalSignedNupkgPath); - - repositoryCommand.OnExecute(async () => - { - return await ExecuteCommand(TrustCommand.Repository, algorithm: null, allowUntrustedRootOption.HasValue(), owners: owners, verbosity, configFile, getLogger, setLogLevel, name: name.Value, sourceUrl: null, packagePath: package.Value); - }); + return await ExecuteCommand(TrustCommand.Repository, algorithm: null, parseResult.GetValue(allowUntrustedRootOption), owners: parseResult.GetValue(owners), parseResult.GetValue(verbosity), parseResult.GetValue(configFile), getLogger, setLogLevel, name: parseResult.GetValue(name), sourceUrl: null, packagePath: parseResult.GetValue(package)); }); + } + trustedSignersCmd.Subcommands.Add(repositoryCommand); - trustedSignersCmd.Command("certificate", (certificateCommand) => + // --- certificate subcommand --- + var certificateCommand = new Command("certificate", Strings.TrustRepositoryCommandDescription); + { + var name = new Argument("NAME") { Description = Strings.TrustedCertificateSignerNameToAdd }; + var fingerprint = new Argument("FINGERPRINT") { Description = Strings.TrustCertificateFingerprint }; + var algorithm = new Option("--algorithm") { Description = Strings.TrustCommandAlgorithm, Arity = ArgumentArity.ZeroOrOne }; + var allowUntrustedRootOption = new Option("--allow-untrusted-root") { Description = Strings.TrustCommandAllowUntrustedRoot, Arity = ArgumentArity.Zero }; + var configFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var verbosity = CreateVerbosityOption(); + + certificateCommand.Arguments.Add(name); + certificateCommand.Arguments.Add(fingerprint); + certificateCommand.Options.Add(algorithm); + certificateCommand.Options.Add(allowUntrustedRootOption); + certificateCommand.Options.Add(configFile); + certificateCommand.Options.Add(verbosity); + + certificateCommand.SetAction(async (parseResult, cancellationToken) => { - certificateCommand.Description = Strings.TrustRepositoryCommandDescription; - - CommandOption algorithm = certificateCommand.Option( - "--algorithm", - Strings.TrustCommandAlgorithm, - CommandOptionType.SingleValue); - - CommandOption allowUntrustedRootOption = certificateCommand.Option( - "--allow-untrusted-root", - Strings.TrustCommandAllowUntrustedRoot, - CommandOptionType.NoValue); - - CommandOption configFile = certificateCommand.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - certificateCommand.HelpOption(XPlatUtility.HelpOption); - - CommandOption verbosity = certificateCommand.VerbosityOption(); - - CommandArgument name = certificateCommand.Argument("", - Strings.TrustedCertificateSignerNameToAdd); - CommandArgument fingerprint = certificateCommand.Argument("", - Strings.TrustCertificateFingerprint); - - certificateCommand.OnExecute(async () => - { - return await ExecuteCommand(TrustCommand.Certificate, algorithm, allowUntrustedRootOption.HasValue(), owners: null, verbosity, configFile, getLogger, setLogLevel, name: name.Value, sourceUrl: null, packagePath: null, fingerprint: fingerprint.Value); - }); + return await ExecuteCommand(TrustCommand.Certificate, algorithm: parseResult.GetValue(algorithm), parseResult.GetValue(allowUntrustedRootOption), owners: null, parseResult.GetValue(verbosity), parseResult.GetValue(configFile), getLogger, setLogLevel, name: parseResult.GetValue(name), sourceUrl: null, packagePath: null, fingerprint: parseResult.GetValue(fingerprint)); }); + } + trustedSignersCmd.Subcommands.Add(certificateCommand); - trustedSignersCmd.Command("source", (sourceCommand) => + // --- source subcommand --- + var sourceCommand = new Command("source", Strings.TrustSourceCommandDescription); + { + var name = new Argument("NAME") { Description = Strings.TrustSourceSignerName }; + var sourceUrl = new Option("--source-url") { Description = Strings.TrustSourceUrl, Arity = ArgumentArity.ZeroOrOne }; + var owners = new Option("--owners") { Description = Strings.TrustCommandOwners, Arity = ArgumentArity.ZeroOrOne }; + var configFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var verbosity = CreateVerbosityOption(); + + sourceCommand.Arguments.Add(name); + sourceCommand.Options.Add(sourceUrl); + sourceCommand.Options.Add(owners); + sourceCommand.Options.Add(configFile); + sourceCommand.Options.Add(verbosity); + + sourceCommand.SetAction(async (parseResult, cancellationToken) => { - sourceCommand.Description = Strings.TrustSourceCommandDescription; - - CommandOption configFile = sourceCommand.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - sourceCommand.HelpOption(XPlatUtility.HelpOption); - - CommandOption owners = sourceCommand.Option( - "--owners", - Strings.TrustCommandOwners, - CommandOptionType.SingleValue); - - CommandOption sourceUrl = sourceCommand.Option( - "--source-url", - Strings.TrustSourceUrl, - CommandOptionType.SingleValue); - - CommandOption verbosity = sourceCommand.VerbosityOption(); - - CommandArgument name = sourceCommand.Argument("", - Strings.TrustSourceSignerName); - - sourceCommand.OnExecute(async () => - { - return await ExecuteCommand(TrustCommand.Source, algorithm: null, allowUntrustedRootOption: false, owners, verbosity, configFile, getLogger, setLogLevel, name: name.Value, sourceUrl: sourceUrl.Value()); - }); + return await ExecuteCommand(TrustCommand.Source, algorithm: null, allowUntrustedRootOption: false, owners: parseResult.GetValue(owners), parseResult.GetValue(verbosity), parseResult.GetValue(configFile), getLogger, setLogLevel, name: parseResult.GetValue(name), sourceUrl: parseResult.GetValue(sourceUrl)); }); + } + trustedSignersCmd.Subcommands.Add(sourceCommand); - // Main command - trustedSignersCmd.Description = Strings.TrustCommandDescription; - CommandOption mainConfigFile = trustedSignersCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - trustedSignersCmd.HelpOption(XPlatUtility.HelpOption); + // --- Main command (defaults to list behavior) --- + var mainConfigFile = new Option("--configfile") { Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ZeroOrOne }; + var mainVerbosity = CreateVerbosityOption(); - CommandOption mainVerbosity = trustedSignersCmd.VerbosityOption(); + trustedSignersCmd.Options.Add(mainConfigFile); + trustedSignersCmd.Options.Add(mainVerbosity); - trustedSignersCmd.OnExecute(async () => - { - // If no command specified then default to List command. - return await ExecuteCommand(TrustCommand.List, algorithm: null, allowUntrustedRootOption: false, owners: null, mainVerbosity, mainConfigFile, getLogger, setLogLevel); - }); + trustedSignersCmd.SetAction(async (parseResult, cancellationToken) => + { + // If no command specified then default to List command. + return await ExecuteCommand(TrustCommand.List, algorithm: null, allowUntrustedRootOption: false, owners: null, parseResult.GetValue(mainVerbosity), parseResult.GetValue(mainConfigFile), getLogger, setLogLevel); }); + + parent.Subcommands.Add(trustedSignersCmd); } private static async Task ExecuteCommand(TrustCommand action, - CommandOption algorithm, + string algorithm, bool allowUntrustedRootOption, - CommandOption owners, - CommandOption verbosity, - CommandOption configFile, - Func getLogger, + string owners, + string verbosity, + string configFile, + Func getLogger, Action setLogLevel, string name = null, string sourceUrl = null, @@ -249,7 +200,7 @@ private static async Task ExecuteCommand(TrustCommand action, try { - ISettings settings = XPlatUtility.ProcessConfigFile(configFile.Value()); + ISettings settings = XPlatUtility.ProcessConfigFile(configFile); var trustedSignersArgs = new TrustedSignersArgs() { @@ -258,15 +209,15 @@ private static async Task ExecuteCommand(TrustCommand action, Name = name, ServiceIndex = sourceUrl, CertificateFingerprint = fingerprint, - FingerprintAlgorithm = algorithm?.Value(), + FingerprintAlgorithm = algorithm, AllowUntrustedRoot = allowUntrustedRootOption, Author = action == TrustCommand.Author, Repository = action == TrustCommand.Repository, - Owners = CommandLineUtility.SplitAndJoinAcrossMultipleValues(owners?.Values), + Owners = CommandLineUtility.SplitAndJoinAcrossMultipleValues(owners != null ? new[] { owners } : null), Logger = logger }; - setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value())); + setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity)); // Add is the only action which does certificate chain building. if (trustedSignersArgs.Action == TrustedSignersAction.Add) @@ -322,12 +273,13 @@ private static async Task ExecuteCommand(TrustCommand action, } } - private static CommandOption VerbosityOption(this CommandLineApplication command) + private static Option CreateVerbosityOption() { - return command.Option( - "-v|--verbosity", - Strings.Verbosity_Description, - CommandOptionType.SingleValue); + return new Option("--verbosity", "-v") + { + Description = Strings.Verbosity_Description, + Arity = ArgumentArity.ZeroOrOne + }; } private static TrustedSignersAction MapTrustEnumAction(TrustCommand trustCommand) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/VerifyCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/VerifyCommand.cs index 3560d550bae..87c4db4c2eb 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/VerifyCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/VerifyCommand.cs @@ -5,9 +5,10 @@ using System; using System.Collections.Generic; +using System.CommandLine; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; -using Microsoft.Extensions.CommandLineUtils; using NuGet.Commands; using NuGet.Common; using NuGet.Packaging.Signing; @@ -17,74 +18,84 @@ namespace NuGet.CommandLine.XPlat { internal static class VerifyCommand { - internal static void Register(CommandLineApplication app, - Func getLogger, + internal static void Register(Command parent, + Func getLogger, Action setLogLevel, Func getCommandRunner) { - app.Command("verify", verifyCmd => + var verifyCmd = new Command("verify", Strings.VerifyCommandDescription); + + var packagePaths = new Argument("package-paths") + { + Description = Strings.VerifyCommandPackagePathDescription, + Arity = ArgumentArity.OneOrMore + }; + + var all = new Option("--all") + { + Description = Strings.VerifyCommandAllDescription, + Arity = ArgumentArity.Zero + }; + + var fingerPrint = new Option("--certificate-fingerprint") + { + Description = Strings.VerifyCommandCertificateFingerprintDescription, + Arity = ArgumentArity.OneOrMore + }; + + var configFile = new Option("--configfile") + { + Description = Strings.Option_ConfigFile, + Arity = ArgumentArity.ZeroOrOne + }; + + var verbosity = new Option("--verbosity", "-v") { - CommandArgument packagePaths = verifyCmd.Argument( - "", - Strings.VerifyCommandPackagePathDescription, - multipleValues: true); - - CommandOption all = verifyCmd.Option( - "--all", - Strings.VerifyCommandAllDescription, - CommandOptionType.NoValue); - - CommandOption fingerPrint = verifyCmd.Option( - "--certificate-fingerprint", - Strings.VerifyCommandCertificateFingerprintDescription, - CommandOptionType.MultipleValue); - - CommandOption configFile = verifyCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - - CommandOption verbosity = verifyCmd.Option( - "-v|--verbosity", - Strings.Verbosity_Description, - CommandOptionType.SingleValue); - - verifyCmd.HelpOption(XPlatUtility.HelpOption); - verifyCmd.Description = Strings.VerifyCommandDescription; - - verifyCmd.OnExecute(async () => - { - ValidatePackagePaths(packagePaths); - - VerifyArgs args = new VerifyArgs(); - args.PackagePaths = packagePaths.Values; - args.Verifications = all.HasValue() ? - new List() { Verification.All } : - new List() { Verification.Signatures }; - args.CertificateFingerprint = fingerPrint.Values; - args.Logger = getLogger(); - args.Settings = XPlatUtility.ProcessConfigFile(configFile.Value()); - setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value())); - - X509TrustStore.InitializeForDotNetSdk(args.Logger); - - var runner = getCommandRunner(); - var verifyTask = runner.ExecuteCommandAsync(args); - await verifyTask; - - return verifyTask.Result; - }); + Description = Strings.Verbosity_Description, + Arity = ArgumentArity.ZeroOrOne + }; + + verifyCmd.Arguments.Add(packagePaths); + verifyCmd.Options.Add(all); + verifyCmd.Options.Add(fingerPrint); + verifyCmd.Options.Add(configFile); + verifyCmd.Options.Add(verbosity); + + verifyCmd.SetAction(async (parseResult, cancellationToken) => + { + string[]? packagePathValues = parseResult.GetValue(packagePaths); + ValidatePackagePaths(packagePathValues, "package-paths"); + + VerifyArgs args = new VerifyArgs(); + args.PackagePaths = new List(packagePathValues); + args.Verifications = parseResult.GetValue(all) ? + new List() { Verification.All } : + new List() { Verification.Signatures }; + args.CertificateFingerprint = new List(parseResult.GetValue(fingerPrint) ?? Array.Empty()); + args.Logger = getLogger(); + args.Settings = XPlatUtility.ProcessConfigFile(parseResult.GetValue(configFile)); + setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(parseResult.GetValue(verbosity))); + + X509TrustStore.InitializeForDotNetSdk(args.Logger); + + var runner = getCommandRunner(); + int result = await runner.ExecuteCommandAsync(args); + + return result; }); + + parent.Subcommands.Add(verifyCmd); } - private static void ValidatePackagePaths(CommandArgument argument) + private static void ValidatePackagePaths([NotNull] string[]? packagePaths, string argumentName) { - if (argument.Values.Count == 0 || - argument.Values.Any(packagePath => string.IsNullOrEmpty(packagePath))) + if (packagePaths == null || + packagePaths.Length == 0 || + packagePaths.Any(packagePath => string.IsNullOrEmpty(packagePath))) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_PkgMissingArgument, "verify", - argument.Name)); + argumentName)); } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Verbs.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Verbs.cs deleted file mode 100644 index 892e959f0df..00000000000 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Verbs.cs +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -#nullable enable - -using System; -using Microsoft.Extensions.CommandLineUtils; -using NuGet.Commands; -using NuGet.Common; - -namespace NuGet.CommandLine.XPlat -{ - internal partial class AddVerbParser - { - internal static void Register(CommandLineApplication app, - Func getLogger) - { - app.Command("add", AddCmd => - { - AddCmd.Command("source", SourceCmd => - { - CommandArgument Source = SourceCmd.Argument( - "PackageSourcePath", Strings.SourcesCommandSourceDescription); - CommandOption name = SourceCmd.Option( - "-n|--name", - Strings.SourcesCommandNameDescription, - CommandOptionType.SingleValue); - CommandOption username = SourceCmd.Option( - "-u|--username", - Strings.SourcesCommandUsernameDescription, - CommandOptionType.SingleValue); - CommandOption password = SourceCmd.Option( - "-p|--password", - Strings.SourcesCommandPasswordDescription, - CommandOptionType.SingleValue); - CommandOption storePasswordInClearText = SourceCmd.Option( - "--store-password-in-clear-text", - Strings.SourcesCommandStorePasswordInClearTextDescription, - CommandOptionType.NoValue); - CommandOption validAuthenticationTypes = SourceCmd.Option( - "--valid-authentication-types", - Strings.SourcesCommandValidAuthenticationTypesDescription, - CommandOptionType.SingleValue); - CommandOption protocolVersion = SourceCmd.Option( - "--protocol-version", - Strings.SourcesCommandProtocolVersionDescription, - CommandOptionType.SingleValue); - CommandOption configfile = SourceCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - CommandOption allowInsecureConnections = SourceCmd.Option( - "--allow-insecure-connections", - Strings.SourcesCommandAllowInsecureConnectionsDescription, - CommandOptionType.NoValue); - SourceCmd.HelpOption("-h|--help"); - SourceCmd.Description = Strings.AddSourceCommandDescription; - SourceCmd.OnExecute(() => - { - var args = new AddSourceArgs() - { - Source = Source.Value, - Name = name.Value(), - Username = username.Value(), - Password = password.Value(), - StorePasswordInClearText = storePasswordInClearText.HasValue(), - ValidAuthenticationTypes = validAuthenticationTypes.Value(), - ProtocolVersion = protocolVersion.Value(), - Configfile = configfile.Value(), - AllowInsecureConnections = allowInsecureConnections.HasValue(), - }; - - AddSourceRunner.Run(args, getLogger); - return 0; - }); - }); - AddCmd.Command("client-cert", ClientCertCmd => - { - CommandOption packagesource = ClientCertCmd.Option( - "-s|--package-source", - Strings.Option_PackageSource, - CommandOptionType.SingleValue); - CommandOption path = ClientCertCmd.Option( - "--path", - Strings.Option_Path, - CommandOptionType.SingleValue); - CommandOption password = ClientCertCmd.Option( - "--password", - Strings.Option_Password, - CommandOptionType.SingleValue); - CommandOption storepasswordincleartext = ClientCertCmd.Option( - "--store-password-in-clear-text", - Strings.Option_StorePasswordInClearText, - CommandOptionType.NoValue); - CommandOption storelocation = ClientCertCmd.Option( - "--store-location", - Strings.Option_StoreLocation, - CommandOptionType.SingleValue); - CommandOption storename = ClientCertCmd.Option( - "--store-name", - Strings.Option_StoreName, - CommandOptionType.SingleValue); - CommandOption findby = ClientCertCmd.Option( - "--find-by", - Strings.Option_FindBy, - CommandOptionType.SingleValue); - CommandOption findvalue = ClientCertCmd.Option( - "--find-value", - Strings.Option_FindValue, - CommandOptionType.SingleValue); - CommandOption force = ClientCertCmd.Option( - "-f|--force", - Strings.Option_Force, - CommandOptionType.NoValue); - CommandOption configfile = ClientCertCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - ClientCertCmd.HelpOption("-h|--help"); - ClientCertCmd.Description = Strings.AddClientCertCommandDescription; - ClientCertCmd.OnExecute(() => - { - var args = new AddClientCertArgs() - { - PackageSource = packagesource.Value(), - Path = path.Value(), - Password = password.Value(), - StorePasswordInClearText = storepasswordincleartext.HasValue(), - StoreLocation = storelocation.Value(), - StoreName = storename.Value(), - FindBy = findby.Value(), - FindValue = findvalue.Value(), - Force = force.HasValue(), - Configfile = configfile.Value(), - }; - - AddClientCertRunner.Run(args, getLogger); - return 0; - }); - }); - AddCmd.HelpOption("-h|--help"); - AddCmd.Description = Strings.Add_Description; - AddCmd.OnExecute(() => - { - app.ShowHelp("add"); - return 0; - }); - }); - } - } - - internal partial class DisableVerbParser - { - internal static void Register(CommandLineApplication app, - Func getLogger) - { - app.Command("disable", DisableCmd => - { - DisableCmd.Command("source", SourceCmd => - { - CommandArgument name = SourceCmd.Argument( - "name", Strings.SourcesCommandNameDescription); - CommandOption configfile = SourceCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - SourceCmd.HelpOption("-h|--help"); - SourceCmd.Description = Strings.DisableSourceCommandDescription; - SourceCmd.OnExecute(() => - { - var args = new DisableSourceArgs() - { - Name = name.Value, - Configfile = configfile.Value(), - }; - - DisableSourceRunner.Run(args, getLogger); - return 0; - }); - }); - DisableCmd.HelpOption("-h|--help"); - DisableCmd.Description = Strings.Disable_Description; - DisableCmd.OnExecute(() => - { - app.ShowHelp("disable"); - return 0; - }); - }); - } - } - - internal partial class EnableVerbParser - { - internal static void Register(CommandLineApplication app, - Func getLogger) - { - app.Command("enable", EnableCmd => - { - EnableCmd.Command("source", SourceCmd => - { - CommandArgument name = SourceCmd.Argument( - "name", Strings.SourcesCommandNameDescription); - CommandOption configfile = SourceCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - SourceCmd.HelpOption("-h|--help"); - SourceCmd.Description = Strings.EnableSourceCommandDescription; - SourceCmd.OnExecute(() => - { - var args = new EnableSourceArgs() - { - Name = name.Value, - Configfile = configfile.Value(), - }; - - EnableSourceRunner.Run(args, getLogger); - return 0; - }); - }); - EnableCmd.HelpOption("-h|--help"); - EnableCmd.Description = Strings.Enable_Description; - EnableCmd.OnExecute(() => - { - app.ShowHelp("enable"); - return 0; - }); - }); - } - } - - internal partial class ListVerbParser - { - internal static void Register(CommandLineApplication app, - Func getLogger) - { - app.Command("list", ListCmd => - { - ListCmd.Command("source", SourceCmd => - { - CommandOption format = SourceCmd.Option( - "--format", - Strings.SourcesCommandFormatDescription, - CommandOptionType.SingleValue); - CommandOption configfile = SourceCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - SourceCmd.HelpOption("-h|--help"); - SourceCmd.Description = Strings.ListSourceCommandDescription; - SourceCmd.OnExecute(() => - { - var args = new ListSourceArgs() - { - Format = format.Value(), - Configfile = configfile.Value(), - }; - - ListSourceRunner.Run(args, getLogger); - return 0; - }); - }); - ListCmd.Command("client-cert", ClientCertCmd => - { - CommandOption configfile = ClientCertCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - ClientCertCmd.HelpOption("-h|--help"); - ClientCertCmd.Description = Strings.ListClientCertCommandDescription; - ClientCertCmd.OnExecute(() => - { - var args = new ListClientCertArgs() - { - Configfile = configfile.Value(), - }; - - ListClientCertRunner.Run(args, getLogger); - return 0; - }); - }); - ListCmd.HelpOption("-h|--help"); - ListCmd.Description = Strings.List_Description; - ListCmd.OnExecute(() => - { - app.ShowHelp("list"); - return 0; - }); - }); - } - } - - internal partial class RemoveVerbParser - { - internal static void Register(CommandLineApplication app, - Func getLogger) - { - app.Command("remove", RemoveCmd => - { - RemoveCmd.Command("source", SourceCmd => - { - CommandArgument name = SourceCmd.Argument( - "name", Strings.SourcesCommandNameDescription); - CommandOption configfile = SourceCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - SourceCmd.HelpOption("-h|--help"); - SourceCmd.Description = Strings.RemoveSourceCommandDescription; - SourceCmd.OnExecute(() => - { - var args = new RemoveSourceArgs() - { - Name = name.Value, - Configfile = configfile.Value(), - }; - - RemoveSourceRunner.Run(args, getLogger); - return 0; - }); - }); - RemoveCmd.Command("client-cert", ClientCertCmd => - { - CommandOption packagesource = ClientCertCmd.Option( - "-s|--package-source", - Strings.Option_PackageSource, - CommandOptionType.SingleValue); - CommandOption configfile = ClientCertCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - ClientCertCmd.HelpOption("-h|--help"); - ClientCertCmd.Description = Strings.RemoveClientCertCommandDescription; - ClientCertCmd.OnExecute(() => - { - var args = new RemoveClientCertArgs() - { - PackageSource = packagesource.Value(), - Configfile = configfile.Value(), - }; - - RemoveClientCertRunner.Run(args, getLogger); - return 0; - }); - }); - RemoveCmd.HelpOption("-h|--help"); - RemoveCmd.Description = Strings.Remove_Description; - RemoveCmd.OnExecute(() => - { - app.ShowHelp("remove"); - return 0; - }); - }); - } - } - - internal partial class UpdateVerbParser - { - internal static void Register(CommandLineApplication app, - Func getLogger) - { - app.Command("update", UpdateCmd => - { - UpdateCmd.Command("source", SourceCmd => - { - CommandArgument name = SourceCmd.Argument( - "name", Strings.SourcesCommandNameDescription); - CommandOption source = SourceCmd.Option( - "-s|--source", - Strings.SourcesCommandSourceDescription, - CommandOptionType.SingleValue); - CommandOption username = SourceCmd.Option( - "-u|--username", - Strings.SourcesCommandUsernameDescription, - CommandOptionType.SingleValue); - CommandOption password = SourceCmd.Option( - "-p|--password", - Strings.SourcesCommandPasswordDescription, - CommandOptionType.SingleValue); - CommandOption storePasswordInClearText = SourceCmd.Option( - "--store-password-in-clear-text", - Strings.SourcesCommandStorePasswordInClearTextDescription, - CommandOptionType.NoValue); - CommandOption validAuthenticationTypes = SourceCmd.Option( - "--valid-authentication-types", - Strings.SourcesCommandValidAuthenticationTypesDescription, - CommandOptionType.SingleValue); - CommandOption protocolVersion = SourceCmd.Option( - "--protocol-version", - Strings.SourcesCommandProtocolVersionDescription, - CommandOptionType.SingleValue); - CommandOption configfile = SourceCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - CommandOption allowInsecureConnections = SourceCmd.Option( - "--allow-insecure-connections", - Strings.SourcesCommandAllowInsecureConnectionsDescription, - CommandOptionType.NoValue); - SourceCmd.HelpOption("-h|--help"); - SourceCmd.Description = Strings.UpdateSourceCommandDescription; - SourceCmd.OnExecute(() => - { - var args = new UpdateSourceArgs() - { - Name = name.Value, - Source = source.Value(), - Username = username.Value(), - Password = password.Value(), - StorePasswordInClearText = storePasswordInClearText.HasValue(), - ValidAuthenticationTypes = validAuthenticationTypes.Value(), - ProtocolVersion = protocolVersion.Value(), - Configfile = configfile.Value(), - AllowInsecureConnections = allowInsecureConnections.HasValue(), - }; - - UpdateSourceRunner.Run(args, getLogger); - return 0; - }); - }); - UpdateCmd.Command("client-cert", ClientCertCmd => - { - CommandOption packagesource = ClientCertCmd.Option( - "-s|--package-source", - Strings.Option_PackageSource, - CommandOptionType.SingleValue); - CommandOption path = ClientCertCmd.Option( - "--path", - Strings.Option_Path, - CommandOptionType.SingleValue); - CommandOption password = ClientCertCmd.Option( - "--password", - Strings.Option_Password, - CommandOptionType.SingleValue); - CommandOption storepasswordincleartext = ClientCertCmd.Option( - "--store-password-in-clear-text", - Strings.Option_StorePasswordInClearText, - CommandOptionType.NoValue); - CommandOption storelocation = ClientCertCmd.Option( - "--store-location", - Strings.Option_StoreLocation, - CommandOptionType.SingleValue); - CommandOption storename = ClientCertCmd.Option( - "--store-name", - Strings.Option_StoreName, - CommandOptionType.SingleValue); - CommandOption findby = ClientCertCmd.Option( - "--find-by", - Strings.Option_FindBy, - CommandOptionType.SingleValue); - CommandOption findvalue = ClientCertCmd.Option( - "--find-value", - Strings.Option_FindValue, - CommandOptionType.SingleValue); - CommandOption force = ClientCertCmd.Option( - "-f|--force", - Strings.Option_Force, - CommandOptionType.NoValue); - CommandOption configfile = ClientCertCmd.Option( - "--configfile", - Strings.Option_ConfigFile, - CommandOptionType.SingleValue); - ClientCertCmd.HelpOption("-h|--help"); - ClientCertCmd.Description = Strings.UpdateClientCertCommandDescription; - ClientCertCmd.OnExecute(() => - { - var args = new UpdateClientCertArgs() - { - PackageSource = packagesource.Value(), - Path = path.Value(), - Password = password.Value(), - StorePasswordInClearText = storepasswordincleartext.HasValue(), - StoreLocation = storelocation.Value(), - StoreName = storename.Value(), - FindBy = findby.Value(), - FindValue = findvalue.Value(), - Force = force.HasValue(), - Configfile = configfile.Value(), - }; - - UpdateClientCertRunner.Run(args, getLogger); - return 0; - }); - }); - UpdateCmd.HelpOption("-h|--help"); - UpdateCmd.Description = Strings.Update_Description; - UpdateCmd.OnExecute(() => - { - app.ShowHelp("update"); - return 0; - }); - }); - } - } - -} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/WhyCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/WhyCommand.cs index 8beb68d88ab..dabccf89994 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/WhyCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/WhyCommand.cs @@ -6,11 +6,9 @@ using System; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Help; using System.CommandLine.Parsing; using System.IO; using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; using NuGet.Common; using Spectre.Console; @@ -18,14 +16,6 @@ namespace NuGet.CommandLine.XPlat.Commands.Why { public static class WhyCommand { - internal static void Register(CommandLineApplication app) - { - app.Command("why", whyCmd => - { - whyCmd.Description = Strings.WhyCommand_Description; - }); - } - internal static void Register(Command rootCommand, Lazy console, IVirtualProjectBuilder? virtualProjectBuilder = null) { Register(rootCommand, console, @@ -109,15 +99,9 @@ bool HasPathArgument(ArgumentResult ar) Arity = ArgumentArity.OneOrMore }; - HelpOption help = new HelpOption() - { - Arity = ArgumentArity.Zero - }; - whyCommand.Arguments.Add(path); whyCommand.Arguments.Add(package); whyCommand.Options.Add(frameworks); - whyCommand.Options.Add(help); whyCommand.SetAction(async (parseResult, cancellationToken) => { diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/GlobalSuppressions.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/GlobalSuppressions.cs index 9c80ebf159e..657214d6d62 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/GlobalSuppressions.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/GlobalSuppressions.cs @@ -8,7 +8,6 @@ using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void AddPackageReferenceCommand.Register(CommandLineApplication app, Func getLogger, Func getCommandRunner)', validate parameter 'app' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.AddPackageReferenceCommand.Register(Microsoft.Extensions.CommandLineUtils.CommandLineApplication,System.Func{NuGet.Common.ILogger},System.Func{NuGet.CommandLine.XPlat.IPackageReferenceCommandRunner},System.Func{NuGet.CommandLine.XPlat.IVirtualProjectBuilder})")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'Task AddPackageReferenceCommandRunner.ExecuteCommand(PackageReferenceArgs packageReferenceArgs, MSBuildAPIUtility msBuild)', validate parameter 'msBuild' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.AddPackageReferenceCommandRunner.ExecuteCommand(NuGet.CommandLine.XPlat.PackageReferenceArgs,NuGet.CommandLine.XPlat.MSBuildAPIUtility)~System.Threading.Tasks.Task{System.Int32}")] [assembly: SuppressMessage("Build", "CA1308:In method 'LogInternal', replace the call to 'ToLowerInvariant' with 'ToUpperInvariant'.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.CommandOutputLogger.LogInternal(NuGet.Common.LogLevel,System.String)")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void CommandOutputLogger.LogInternal(LogLevel logLevel, string message)', validate parameter 'message' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.CommandOutputLogger.LogInternal(NuGet.Common.LogLevel,System.String)")] @@ -20,7 +19,6 @@ [assembly: SuppressMessage("Build", "CA1308:In method 'MainInternal', replace the call to 'ToLowerInvariant' with 'ToUpperInvariant'.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.Program.MainInternal(System.String[],NuGet.CommandLine.XPlat.CommandOutputLogger,NuGet.Common.IEnvironmentVariableReader,NuGet.CommandLine.XPlat.IVirtualProjectBuilder)~System.Int32")] [assembly: SuppressMessage("Build", "CA1031:Modify 'MainInternal' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.Program.MainInternal(System.String[],NuGet.CommandLine.XPlat.CommandOutputLogger,NuGet.Common.IEnvironmentVariableReader,NuGet.CommandLine.XPlat.IVirtualProjectBuilder)~System.Int32")] [assembly: SuppressMessage("Build", "CA1303:Method 'int Program.MainInternal(string[] args, CommandOutputLogger log)' passes a literal string as parameter 'value' of a call to 'void Console.WriteLine(string value)'. Retrieve the following string(s) from a resource table instead: \"Waiting for debugger to attach.\".", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.Program.MainInternal(System.String[],NuGet.CommandLine.XPlat.CommandOutputLogger,NuGet.Common.IEnvironmentVariableReader,NuGet.CommandLine.XPlat.IVirtualProjectBuilder)~System.Int32")] -[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void RemovePackageReferenceCommand.Register(CommandLineApplication app, Func getLogger, Func getCommandRunner)', validate parameter 'app' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.RemovePackageReferenceCommand.Register(Microsoft.Extensions.CommandLineUtils.CommandLineApplication,System.Func{NuGet.Common.ILogger},System.Func{NuGet.CommandLine.XPlat.IPackageReferenceCommandRunner},System.Func{NuGet.CommandLine.XPlat.IVirtualProjectBuilder})")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'Task RemovePackageReferenceCommandRunner.ExecuteCommand(PackageReferenceArgs packageReferenceArgs, MSBuildAPIUtility msBuild)', validate parameter 'msBuild' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.CommandLine.XPlat.RemovePackageReferenceCommandRunner.ExecuteCommand(NuGet.CommandLine.XPlat.PackageReferenceArgs,NuGet.CommandLine.XPlat.MSBuildAPIUtility)~System.Threading.Tasks.Task{System.Int32}")] [assembly: SuppressMessage("Build", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:NuGet.CommandLine.XPlat.PackageReferenceArgs.Frameworks")] [assembly: SuppressMessage("Build", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:NuGet.CommandLine.XPlat.PackageReferenceArgs.Sources")] diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj index e99661fba57..f72da6b4f1c 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj @@ -8,8 +8,6 @@ true true true - - disable @@ -17,7 +15,6 @@ - diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs index 72c9a12c00c..0e6efaf7539 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs @@ -1,15 +1,18 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#nullable disable - using System; -using System.Collections.Generic; using System.CommandLine; using System.Globalization; using System.Linq; -using System.Threading; -using Microsoft.Extensions.CommandLineUtils; +using NuGet.CommandLine.XPlat.Commands; +using NuGet.CommandLine.XPlat.Commands.NuGet.Add; +using NuGet.CommandLine.XPlat.Commands.NuGet.Disable; +using NuGet.CommandLine.XPlat.Commands.NuGet.Enable; +using NuGet.CommandLine.XPlat.Commands.NuGet.List; +using NuGet.CommandLine.XPlat.Commands.NuGet.Remove; +using NuGet.CommandLine.XPlat.Commands.NuGet.Update; +using NuGet.CommandLine.XPlat.Commands.Why; using NuGet.Commands; using NuGet.Common; @@ -25,17 +28,12 @@ public static class Program #if DEBUG private const string DebugOption = "--debug"; #endif - private const string DotnetNuGetAppName = "dotnet nuget"; - private const string DotnetPackageAppName = "NuGet.CommandLine.XPlat.dll package"; - - private const int DotnetPackageSearchTimeOut = 15; internal static int Main(string[] args) { return MainInternal(args, virtualProjectBuilder: null); } -#nullable enable public static int Run(string[] args, IVirtualProjectBuilder virtualProjectBuilder) { return MainInternal(args, virtualProjectBuilder); @@ -46,12 +44,11 @@ private static int MainInternal(string[] args, IVirtualProjectBuilder? virtualPr var log = new CommandOutputLogger(LogLevel.Information); return MainInternal(args, log, EnvironmentVariableWrapper.Instance, virtualProjectBuilder); } -#nullable disable /// /// Internal Main. This is used for testing. /// - internal static int MainInternal(string[] args, CommandOutputLogger log, IEnvironmentVariableReader environmentVariableReader, IVirtualProjectBuilder virtualProjectBuilder = null) + internal static int MainInternal(string[] args, CommandOutputLogger log, IEnvironmentVariableReader environmentVariableReader, IVirtualProjectBuilder? virtualProjectBuilder = null) { #if USEMSBUILDLOCATOR try @@ -69,7 +66,7 @@ internal static int MainInternal(string[] args, CommandOutputLogger log, IEnviro #endif #if DEBUG - var debugNuGetXPlat = environmentVariableReader.GetEnvironmentVariable("DEBUG_NUGET_XPLAT"); + string? debugNuGetXPlat = environmentVariableReader.GetEnvironmentVariable("DEBUG_NUGET_XPLAT"); if (args.Contains(DebugOption) || string.Equals(bool.TrueString, debugNuGetXPlat, StringComparison.OrdinalIgnoreCase)) { @@ -91,157 +88,90 @@ internal static int MainInternal(string[] args, CommandOutputLogger log, IEnviro NuGet.Common.Migrations.MigrationRunner.Run(); - // TODO: Migrating from Microsoft.Extensions.CommandLineUtils.CommandLineApplication to System.Commandline.Command - // If we are looking to add further commands here, we should also look to redesign this parsing logic at that time - // See related issues: - // - https://github.com/NuGet/Home/issues/11996 - // - https://github.com/NuGet/Home/issues/11997 - // - https://github.com/NuGet/Home/issues/13089 - if (IsSystemCommandLineParsedCommand(args)) + Func getHidePrefixLogger = () => { - Func getHidePrefixLogger = () => - { - log.HidePrefixForInfoAndMinimal = true; - return log; - }; - - RootCommand rootCommand = new RootCommand(); - // Commands called directly from the SDK CLI will use the SDK's common interactive option. - Option interactiveOption = new Option("--interactive"); - interactiveOption.Description = Strings.AddPkg_InteractiveDescription; - interactiveOption.DefaultValueFactory = _ => Console.IsOutputRedirected; + log.HidePrefixForInfoAndMinimal = true; + return log; + }; - if (args[0] == "package") - { - var packageCommand = new Command("package"); - rootCommand.Subcommands.Add(packageCommand); + Action setLogLevel = (logLevel) => log.VerbosityLevel = logLevel; - PackageSearchCommand.Register(packageCommand, getHidePrefixLogger); -#if DEBUG - PackageUpdateCommand.Register(packageCommand, interactiveOption, virtualProjectBuilder); - PackageDownloadCommand.Register(packageCommand, interactiveOption); -#endif - } - else - { - var nugetCommand = new Command("nuget"); - rootCommand.Subcommands.Add(nugetCommand); + RootCommand rootCommand = new RootCommand(); + rootCommand.Options.Add(new Option(CommandConstants.ForceEnglishOutputOption) + { + Description = Strings.ForceEnglishOutput_Description, + Arity = ArgumentArity.Zero, + Recursive = true + }); + Option interactiveOption = new Option("--interactive") + { + Description = Strings.AddPkg_InteractiveDescription, + DefaultValueFactory = _ => Console.IsOutputRedirected + }; - var lazyConsole = new Lazy(() => Spectre.Console.AnsiConsole.Console); + if (args.Length > 0 && args[0] == "package") + { + var packageCommand = new Command("package"); + rootCommand.Subcommands.Add(packageCommand); - ConfigCommand.Register(nugetCommand, getHidePrefixLogger); - ConfigCommand.Register(rootCommand, getHidePrefixLogger); - Commands.Why.WhyCommand.Register(nugetCommand, lazyConsole, virtualProjectBuilder); - Commands.Why.WhyCommand.Register(rootCommand, lazyConsole, virtualProjectBuilder); - } + var msbuild = new MSBuildAPIUtility(log, virtualProjectBuilder); - CancellationTokenSource tokenSource = new CancellationTokenSource(); - tokenSource.CancelAfter(TimeSpan.FromMinutes(DotnetPackageSearchTimeOut)); - int exitCodeValue = 0; - ParseResult parseResult = rootCommand.Parse(args); + PackageSearchCommand.Register(packageCommand, getHidePrefixLogger); + AddPackageReferenceCommand.Register(packageCommand, () => log, () => new AddPackageReferenceCommandRunner(), () => msbuild.VirtualProjectBuilder); + RemovePackageReferenceCommand.Register(packageCommand, () => log, () => new RemovePackageReferenceCommandRunner(), () => msbuild.VirtualProjectBuilder); + ListPackageCommand.Register(packageCommand, getHidePrefixLogger, setLogLevel, () => new ListPackageCommandRunner(msbuild)); +#if DEBUG + PackageUpdateCommand.Register(packageCommand, interactiveOption, virtualProjectBuilder); + PackageDownloadCommand.Register(packageCommand, interactiveOption); +#endif + } + else + { + var nugetCommand = new Command("nuget"); + rootCommand.Subcommands.Add(nugetCommand); - try - { - exitCodeValue = parseResult.Invoke(); - } - catch (Exception ex) - { - LogException(ex, log); - exitCodeValue = ExitCodes.Error; - } + var lazyConsole = new Lazy(() => Spectre.Console.AnsiConsole.Console); - return exitCodeValue; - } + ConfigCommand.Register(rootCommand, getHidePrefixLogger); + WhyCommand.Register(rootCommand, lazyConsole, virtualProjectBuilder); + DeleteCommand.Register(rootCommand, getHidePrefixLogger); + PushCommand.Register(rootCommand, getHidePrefixLogger); + LocalsCommand.Register(rootCommand, getHidePrefixLogger); + VerifyCommand.Register(rootCommand, getHidePrefixLogger, setLogLevel, () => new VerifyCommandRunner()); + SignCommand.Register(rootCommand, getHidePrefixLogger, setLogLevel, () => new SignCommandRunner()); + TrustedSignersCommand.Register(rootCommand, getHidePrefixLogger, setLogLevel); - var app = InitializeApp(args, log, virtualProjectBuilder); + // Source/client-cert verb commands + DotnetNuGetAddCommand.Register(rootCommand, getHidePrefixLogger); + DotnetNuGetDisableCommand.Register(rootCommand, getHidePrefixLogger); + DotnetNuGetEnableCommand.Register(rootCommand, getHidePrefixLogger); + DotnetNuGetListCommand.Register(rootCommand, getHidePrefixLogger); + DotnetNuGetRemoveCommand.Register(rootCommand, getHidePrefixLogger); + DotnetNuGetUpdateCommand.Register(rootCommand, getHidePrefixLogger); - // Remove the correct item in array for "package" commands. Only do this when "add package", "remove package", etc... are being run. - if (app.Name == DotnetPackageAppName) - { - // package add ... - args[0] = null; - args = args - .Where(e => e != null) - .ToArray(); + // These commands have the same parser as the dotnet CLI, so they can be used interchangeably with "dotnet nuget *" + ConfigCommand.Register(nugetCommand, getHidePrefixLogger); + WhyCommand.Register(nugetCommand, lazyConsole, virtualProjectBuilder); } NetworkProtocolUtility.SetConnectionLimit(); - XPlatUtility.SetUserAgent(); - app.OnExecute(() => - { - app.ShowHelp(); - - return 0; - }); - - log.LogVerbose(string.Format(CultureInfo.CurrentCulture, Strings.OutputNuGetVersion, app.FullName, app.LongVersionGetter())); - int exitCode = 0; + ParseResult parseResult = rootCommand.Parse(args); + var invocationConfig = new InvocationConfiguration + { + EnableDefaultExceptionHandler = false + }; try { - exitCode = app.Execute(args); + exitCode = parseResult.Invoke(invocationConfig); } catch (Exception e) { - bool handled = false; - string verb = null; - if (args.Length > 1) - { - // Redirect users nicely if they do 'dotnet nuget sources add' or 'dotnet nuget add sources' - if (StringComparer.OrdinalIgnoreCase.Compare(args[0], "sources") == 0) - { - verb = args[1]; - } - else if (StringComparer.OrdinalIgnoreCase.Compare(args[1], "sources") == 0) - { - verb = args[0]; - } - - if (verb != null) - { - switch (verb.ToLowerInvariant()) - { - case "add": - case "remove": - case "update": - case "enable": - case "disable": - case "list": - log.LogMinimal(string.Format(CultureInfo.CurrentCulture, - Strings.Sources_Redirect, $"dotnet nuget {verb} source")); - handled = true; - break; - default: - break; - } - } - } - - if (!handled) - { - // Log the error - if (ExceptionLogger.Instance.ShowStack) - { - log.LogError(e.ToString()); - } - else - { - log.LogError(ExceptionUtilities.DisplayMessage(e)); - } - - // Log the stack trace as verbose output. - log.LogVerbose(e.ToString()); - - if (e is CommandParsingException) - { - ShowBestHelp(app, args); - } - - exitCode = 1; - } + LogException(e, log); + exitCode = ExitCodes.InvalidArguments; } // Limit the exit code range to 0-255 to support POSIX @@ -253,38 +183,6 @@ internal static int MainInternal(string[] args, CommandOutputLogger log, IEnviro return exitCode; } - private static bool IsSystemCommandLineParsedCommand(string[] args) - { - if (args.Length == 0) - { - return false; - } - - string arg0 = args[0]; - if (arg0 == "config" || arg0 == "why") - { - return true; - } - - if (args.Length >= 2 && arg0 == "package") - { - string arg1 = args[1]; -#if DEBUG - if (arg1 == "update" || arg1 == "download") - { - return true; - } -#endif - if (arg1 == "search") - { - return true; - } - } - - return false; - } - - internal static void LogException(Exception e, ILogger log) { // Log the error @@ -300,79 +198,5 @@ internal static void LogException(Exception e, ILogger log) // Log the stack trace as verbose output. log.LogVerbose(e.ToString()); } - - private static CommandLineApplication InitializeApp(string[] args, CommandOutputLogger log, IVirtualProjectBuilder virtualProjectBuilder) - { - // Many commands don't want prefixes output. Use this func instead of () => log to set the HidePrefix property first. - Func getHidePrefixLogger = () => - { - log.HidePrefixForInfoAndMinimal = true; - return log; - }; - - // Allow commands to set the NuGet log level - Action setLogLevel = (logLevel) => log.VerbosityLevel = logLevel; - - var app = new CommandLineApplication(); - var msbuild = new MSBuildAPIUtility(log, virtualProjectBuilder); - - if (args.Any() && args[0] == "package") - { - // "dotnet * package" commands - app.Name = DotnetPackageAppName; - AddPackageReferenceCommand.Register(app, () => log, () => new AddPackageReferenceCommandRunner(), () => msbuild.VirtualProjectBuilder); - RemovePackageReferenceCommand.Register(app, () => log, () => new RemovePackageReferenceCommandRunner(), () => msbuild.VirtualProjectBuilder); - ListPackageCommand.Register(app, getHidePrefixLogger, setLogLevel, () => new ListPackageCommandRunner(msbuild)); - } - else - { - // "dotnet nuget *" commands - app.Name = DotnetNuGetAppName; - CommandParsers.Register(app, getHidePrefixLogger); - DeleteCommand.Register(app, getHidePrefixLogger); - PushCommand.Register(app, getHidePrefixLogger); - LocalsCommand.Register(app, getHidePrefixLogger); - VerifyCommand.Register(app, getHidePrefixLogger, setLogLevel, () => new VerifyCommandRunner()); - TrustedSignersCommand.Register(app, getHidePrefixLogger, setLogLevel); - SignCommand.Register(app, getHidePrefixLogger, setLogLevel, () => new SignCommandRunner()); - // The commands below are implemented with System.CommandLine, and are here only for `dotnet nuget --help` - ConfigCommand.Register(app); - Commands.Why.WhyCommand.Register(app); - } - - app.FullName = Strings.App_FullName; - app.HelpOption(XPlatUtility.HelpOption); - app.VersionOption("--version", typeof(Program).Assembly.GetName().Version.ToString()); - - return app; - } - - private static void ShowBestHelp(CommandLineApplication app, string[] args) - { - CommandLineApplication lastCommand = null; - List commands = app.Commands; - // tunnel down into the args, and show the best help possible. - foreach (string arg in args) - { - foreach (CommandLineApplication command in commands) - { - if (arg == command.Name) - { - lastCommand = command; - commands = command.Commands; - break; - } - } - } - - if (lastCommand != null) - { - lastCommand.ShowHelp(); - } - else - { - app.ShowHelp(); - } - } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/CommandLineUtility.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/CommandLineUtility.cs index 6f98f3cd05e..0600bdb32ce 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/CommandLineUtility.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/CommandLineUtility.cs @@ -47,7 +47,7 @@ public static string[] SplitAndJoinAcrossMultipleValues(IList inputs) /// Name of the command line argument /// Signing specification to validate parsed hash algorithm /// Supported hash algorithm - internal static HashAlgorithmName ParseAndValidateHashAlgorithm(string optionValue, string optionName, SigningSpecifications spec) + internal static HashAlgorithmName ParseAndValidateHashAlgorithm(string? optionValue, string optionName, SigningSpecifications spec) { HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA256; diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/XPlatUtility.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/XPlatUtility.cs index 72d2106a849..5322f815665 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/XPlatUtility.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/XPlatUtility.cs @@ -14,14 +14,12 @@ namespace NuGet.CommandLine.XPlat { internal static class XPlatUtility { - public const string HelpOption = "-h|--help"; - /// /// Note that the .NET CLI itself has parameter parsing which limits the values that will be passed here by the /// user. In other words, the default case should only be hit with m or minimal but we use /// as the default case to avoid errors. /// - public static LogLevel MSBuildVerbosityToNuGetLogLevel(string verbosity) + public static LogLevel MSBuildVerbosityToNuGetLogLevel(string? verbosity) { switch (verbosity?.ToUpperInvariant()) { @@ -63,7 +61,7 @@ public static void SetUserAgent() UserAgent.SetUserAgentString(new UserAgentStringBuilder("NuGet xplat")); } - internal static ISettings ProcessConfigFile(string configFile) + internal static ISettings ProcessConfigFile(string? configFile) { if (string.IsNullOrEmpty(configFile)) { diff --git a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetAddPackageTests.cs b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetAddPackageTests.cs index cc70be3db0c..8bdff614619 100644 --- a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetAddPackageTests.cs +++ b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetAddPackageTests.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Microsoft.Extensions.CommandLineUtils; +using System.CommandLine; using Microsoft.Internal.NuGet.Testing.SignedPackages.ChildProcess; using NuGet.CommandLine.XPlat; using NuGet.Common; @@ -100,7 +100,7 @@ public async Task AddPkg_FileBasedApp() // Generate DG file. var dgFile = Path.Join(tempDir, "dg.json"); - _fixture.RunDotnetExpectSuccess(fbaDir, $"build app.cs -t:GenerateRestoreGraphFile -p:RestoreGraphOutputPath={ArgumentEscaper.EscapeAndConcatenate([dgFile])}", testOutputHelper: _testOutputHelper); + _fixture.RunDotnetExpectSuccess(fbaDir, $"build app.cs -t:GenerateRestoreGraphFile -p:RestoreGraphOutputPath=\"{dgFile}\"", testOutputHelper: _testOutputHelper); // Get project content. var virtualProject = _fixture.GetFileBasedAppVirtualProject(appFile, _testOutputHelper); @@ -113,35 +113,21 @@ public async Task AddPkg_FileBasedApp() await SimpleTestPackageUtility.CreateFolderFeedV3Async(pathContext.PackageSource, PackageSaveMode.Defaultv3, packageX); // Add the package. - using var outWriter = new StringWriter(); - using var errorWriter = new StringWriter(); - var testApp = new CommandLineApplication - { - Out = outWriter, - Error = errorWriter, - }; + var testApp = new RootCommand(); AddPackageReferenceCommand.Register( testApp, - () => new TestLogger(_testOutputHelper), + () => new TestCommandOutputLogger(_testOutputHelper), () => new AddPackageReferenceCommandRunner(), () => builder); - int result = testApp.Execute([ + int result = testApp.Parse([ "add", "--project", appFile, "--package", "packageX", "--dg-file", dgFile, - ]); - - var output = outWriter.ToString(); - var error = errorWriter.ToString(); - - _testOutputHelper.WriteLine(output); - _testOutputHelper.WriteLine(error); + ]).Invoke(); Assert.Equal(0, result); - Assert.Empty(error); - var modifiedProjectContent = builder.ModifiedContent; _testOutputHelper.WriteLine("after:\n" + modifiedProjectContent); Assert.Contains("""""", modifiedProjectContent); diff --git a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetListPackageTests.cs b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetListPackageTests.cs index dcbeacf91a4..a18598c4c93 100644 --- a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetListPackageTests.cs +++ b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetListPackageTests.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using System.Xml.Linq; using FluentAssertions; -using Microsoft.Extensions.CommandLineUtils; +using System.CommandLine; using Microsoft.Internal.NuGet.Testing.SignedPackages.ChildProcess; using Newtonsoft.Json.Linq; using NuGet.CommandLine.XPlat; @@ -108,35 +108,27 @@ public async Task DotnetListPackage_FileBasedApp() // List packages. using var outWriter = new StringWriter(); - using var errorWriter = new StringWriter(); - var testApp = new CommandLineApplication - { - Out = outWriter, - Error = errorWriter, - }; - var logger = new TestLogger(_testOutputHelper); + var testApp = new RootCommand(); + var logger = new TestCommandOutputLogger(_testOutputHelper); var msbuild = new MSBuildAPIUtility(logger, builder); ListPackageCommand.Register( testApp, () => logger, (_) => { }, - () => new ListPackageCommandRunner(msbuild)); - int result = testApp.Execute([ + () => new ListPackageCommandRunner(msbuild), + consoleOut: outWriter); + int result = testApp.Parse([ "list", appFile, "--source", pathContext.PackageSource, "--format", "json", - ]); + ]).Invoke(); var output = outWriter.ToString(); - var error = errorWriter.ToString(); _testOutputHelper.WriteLine(output); - _testOutputHelper.WriteLine(error); Assert.Equal(0, result); - Assert.Empty(error); - Assert.Contains("packageX", output); Assert.Contains("1.0.0", output); diff --git a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRemovePackageTests.cs b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRemovePackageTests.cs index edb70291fa7..9e5c1062989 100644 --- a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRemovePackageTests.cs +++ b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRemovePackageTests.cs @@ -3,9 +3,8 @@ using System.IO; using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; +using System.CommandLine; using NuGet.CommandLine.XPlat; -using NuGet.Test.Utility; using NuGet.XPlat.FuncTest; using Xunit; using Xunit.Abstractions; @@ -41,34 +40,20 @@ public async Task RemovePkg_FileBasedApp() using var builder = new TestVirtualProjectBuilder(virtualProject); // Remove the package. - using var outWriter = new StringWriter(); - using var errorWriter = new StringWriter(); - var testApp = new CommandLineApplication - { - Out = outWriter, - Error = errorWriter, - }; + var testApp = new RootCommand(); RemovePackageReferenceCommand.Register( testApp, - () => new TestLogger(_testOutputHelper), + () => new TestCommandOutputLogger(_testOutputHelper), () => new RemovePackageReferenceCommandRunner(), () => builder); - int result = testApp.Execute([ + int result = testApp.Parse([ "remove", "--project", appFile, "--package", "packageX", - ]); - - var output = outWriter.ToString(); - var error = errorWriter.ToString(); - - _testOutputHelper.WriteLine(output); - _testOutputHelper.WriteLine(error); + ]).Invoke(); Assert.Equal(0, result); - Assert.Empty(error); - var modifiedProjectContent = builder.ModifiedContent; _testOutputHelper.WriteLine("after:\n" + modifiedProjectContent); Assert.DoesNotContain("PackageReference", modifiedProjectContent); diff --git a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetSourcesTests.cs b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetSourcesTests.cs index 0c74ae53b6b..64075535bda 100644 --- a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetSourcesTests.cs +++ b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetSourcesTests.cs @@ -1201,8 +1201,9 @@ public void List_Sources_LocalizatedPackagesourceKeys_ConsideredDiffererent() /// /// Verify non-zero status code and proper messages /// - /// Checks invalid arguments message in stderr, check help message in stdout - /// The nuget.exe command name to verify, without "nuget.exe" at the beginning + /// Checks invalid arguments message in stderr or stdout (System.CommandLine may write parse errors to stderr), check help message in stdout + /// The nuget.exe command name to verify, without "nuget.exe" at the beginning + /// Index of the bad argument in the space-split command string internal void TestCommandInvalidArguments(string command, int badCommandIndex) { using (var testDirectory = _fixture.CreateTestDirectory()) @@ -1222,24 +1223,17 @@ internal void TestCommandInvalidArguments(string command, int badCommandIndex) // 3rd - nextParam string badCommand = commandSplit[badCommandIndex]; - // Assert command - Assert.Contains("'" + badCommand + "'", result.Output, StringComparison.InvariantCultureIgnoreCase); + // System.CommandLine may write parse errors to stderr, so check AllOutput + Assert.Contains("'" + badCommand + "'", result.AllOutput, StringComparison.InvariantCultureIgnoreCase); + // System.CommandLine uses "Unrecognized command or argument" or "'X' was not matched" + bool hasUnrecognized = result.AllOutput.Contains("Unrecognized command or argument", StringComparison.OrdinalIgnoreCase); + bool hasNotMatched = result.AllOutput.Contains("was not matched", StringComparison.OrdinalIgnoreCase); + Assert.True(hasUnrecognized || hasNotMatched, + "Expected 'Unrecognized command or argument' or 'was not matched' error. Actual output: " + result.AllOutput); - // Assert invalid argument message - string invalidMessage; - if (badCommand.StartsWith("-")) - { - invalidMessage = ": Unrecognized option"; - } - else - { - invalidMessage = ": Unrecognized command"; - } - - Assert.True(result.Output.Contains(invalidMessage), "Expected error is " + invalidMessage + ". Actual error is " + result.Output); - // Verify traits of help message in stdout - Assert.Contains("Specify --help for a list of available options and commands.", result.Output); + // Verify help message is shown in output + Assert.Contains("Show help and usage information", result.AllOutput); } } } diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/BasicLoggingTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/BasicLoggingTests.cs index ed9a58e2782..dce1218d6e6 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/BasicLoggingTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/BasicLoggingTests.cs @@ -31,7 +31,8 @@ public void BasicLogging_NoParams_ExitCode() var exitCode = NuGet.CommandLine.XPlat.Program.MainInternal(args, log, TestEnvironmentVariableReader.EmptyInstance); // Assert - Assert.Equal(0, exitCode); + // System.CommandLine returns 1 when no subcommand is provided + Assert.Equal(1, exitCode); } } } diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/ListPackageTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/ListPackageTests.cs index 66aba13d5ab..73c6fb08a97 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/ListPackageTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/ListPackageTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.CommandLine; using System.Globalization; using System.IO; using System.Linq; @@ -14,7 +15,6 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using Microsoft.Extensions.CommandLineUtils; using Microsoft.Internal.NuGet.Testing.SignedPackages; using Moq; using NuGet.CommandLine.XPlat; @@ -53,7 +53,7 @@ public void BasicListPackageParsing_Interactive() var argList = new List { "list", "--interactive", projectPath }; // Act - var result = testApp.Execute(argList.ToArray()); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert mockCommandRunner.Verify(); @@ -63,16 +63,20 @@ public void BasicListPackageParsing_Interactive() } [Fact] - public void BasicListPackageParsing_InteractiveTakesNoArguments_ThrowsException() + public void BasicListPackageParsing_InteractiveTakesNoArguments_ReturnsNonZero() { VerifyCommand( (projectPath, mockCommandRunner, testApp, getLogLevel) => { // Arrange + // In System.CommandLine, passing extra unrecognized tokens results in a non-zero exit code var argList = new List() { "list", "--interactive", "no", projectPath }; - // Act & Assert - Assert.Throws(() => testApp.Execute(argList.ToArray())); + // Act + var result = testApp.Parse(argList.ToArray()).Invoke(); + + // Assert + Assert.NotEqual(0, result); }); } @@ -97,7 +101,7 @@ public void BasicListPackageParsing_VerbosityOption(string verbosity, LogLevel l var argList = new List { "list", projectPath, "--verbosity", verbosity }; // Act - var result = testApp.Execute(argList.ToArray()); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert Assert.Equal(logLevel, getLogLevel()); @@ -114,7 +118,7 @@ public void BasicListPackageParsing_NoVerbosityOption() var argList = new List { "list", projectPath }; // Act - var result = testApp.Execute(argList.ToArray()); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert Assert.Equal(LogLevel.Minimal, getLogLevel()); @@ -144,7 +148,7 @@ public void BasicListPackage_OutputFormat_CorrectInput_Parsing_Succeeds(string o argList.Add(projectPath); // Act - var result = testApp.Execute(argList.ToArray()); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert mockCommandRunner.Verify(); @@ -175,7 +179,8 @@ public void BasicListPackage_OutputFormat_BadInput_Parsing_Fails(string outputFo argList.Add(projectPath); // Act & Assert - Assert.Throws(() => testApp.Execute(argList.ToArray())); + var result = testApp.Parse(argList.ToArray()).Invoke(); + Assert.NotEqual(0, result); }); } @@ -549,7 +554,7 @@ public async Task GetReportDataAsync_WhenReportTypeIsVulnerableAuditSourcesWithN } - private void VerifyCommand(Action, CommandLineApplication, Func> verify) + private void VerifyCommand(Action, RootCommand, Func> verify) { // Arrange using (var testDirectory = TestDirectory.Create()) @@ -559,13 +564,12 @@ private void VerifyCommand(Action, Comma var logLevel = LogLevel.Information; var logger = new TestCommandOutputLogger(_testOutputHelper); - var testApp = new CommandLineApplication(); + var testApp = new RootCommand(); var mockCommandRunner = new Mock(); mockCommandRunner .Setup(m => m.ExecuteCommandAsync(It.IsAny())) .Returns(Task.FromResult(0)); - testApp.Name = "dotnet nuget_test"; ListPackageCommand.Register(testApp, () => logger, ll => logLevel = ll, diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatAddPkgTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatAddPkgTests.cs index 3a4eb5bfaaf..9fc8d73f4c0 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatAddPkgTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatAddPkgTests.cs @@ -3,12 +3,12 @@ using System; using System.Collections.Generic; +using System.CommandLine; using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Microsoft.Extensions.CommandLineUtils; using Moq; using NuGet.CommandLine.XPlat; using NuGet.Commands; @@ -116,19 +116,18 @@ public void AddPkg_ArgParsing(string packageOption, string package, string versi } var logger = new TestCommandOutputLogger(_testOutputHelper); - var testApp = new CommandLineApplication(); + var testApp = new RootCommand(); var mockCommandRunner = new Mock(); mockCommandRunner .Setup(m => m.ExecuteCommand(It.IsAny(), It.IsAny())) .ReturnsAsync(0); - testApp.Name = "dotnet nuget_test"; AddPackageReferenceCommand.Register(testApp, () => logger, () => mockCommandRunner.Object); // Act - var result = testApp.Execute(argList.ToArray()); + var result = testApp.Parse(argList.ToArray()).Invoke(); XPlatTestUtils.DisposeTemporaryFile(projectPath); @@ -216,20 +215,19 @@ public void AddPkg_Error_ArgParsingPrerelease(string packageOption, string packa } var logger = new TestCommandOutputLogger(_testOutputHelper); - var testApp = new CommandLineApplication(); + var testApp = new RootCommand(); var mockCommandRunner = new Mock(); mockCommandRunner .Setup(m => m.ExecuteCommand(It.IsAny(), It.IsAny())) .ReturnsAsync(0); - testApp.Name = "dotnet nuget_test"; AddPackageReferenceCommand.Register(testApp, () => logger, () => mockCommandRunner.Object); // Act & Assert - var exception = Assert.Throws(() => testApp.Execute(argList.ToArray())); - Assert.Equal(Strings.Error_PrereleaseWhenVersionSpecified, exception.Message); + var result = testApp.Parse(argList.ToArray()).Invoke(); + Assert.NotEqual(0, result); XPlatTestUtils.DisposeTemporaryFile(projectPath); } } diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatHelpOutputTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatHelpOutputTests.cs index 330ae18ee4a..ad466eabf13 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatHelpOutputTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatHelpOutputTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using NuGet.CommandLine.XPlat; using Test.Utility; using Xunit; @@ -119,11 +118,15 @@ public void MainInternal_ShowsHelp() // Assert var output = consoleOutput.ToString(); - var commandPattern = @"^\s{2}(\w+)\s{2,}"; // Matches lines starting with two spaces, a word (command), followed by at least two spaces - IEnumerable matches = Regex.Matches(output, commandPattern, RegexOptions.Multiline).Select(m => m.ToString().Trim()); + // System.CommandLine returns 1 when no subcommand is provided + Assert.Equal(1, exitCode); + Assert.Contains("Commands:", output); - Assert.Equal(HelpCommands, matches); - Assert.Equal(0, exitCode); + // Verify key commands are present in help output + foreach (var command in HelpCommands) + { + Assert.Contains(command, output); + } } } } diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatLocalsTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatLocalsTests.cs index 6f837e2c83d..17caeadd1e2 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatLocalsTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatLocalsTests.cs @@ -261,8 +261,6 @@ public void Locals_Success_InvalidResourceName_HelpMessage(string args) } [Theory] - [InlineData("locals -list")] - [InlineData("locals -clear")] [InlineData("locals --l")] [InlineData("locals --c")] public void Locals_Success_InvalidFlags_HelpMessage(string args) @@ -271,7 +269,9 @@ public void Locals_Success_InvalidFlags_HelpMessage(string args) XplatDll.Should().NotBeNull(because: "Could not locate the Xplat dll"); // Arrange - var expectedResult = $"Unrecognized option '{args.Split(null)[1]}'"; + // System.CommandLine treats "--l" and "--c" as the cache location argument (not as option prefixes), + // so neither --list nor --clear is set, triggering the "no operation" error. + var expectedResult = "Please specify an operation i.e. --list or --clear."; // Act var result = CommandRunner.Run( @@ -284,6 +284,46 @@ public void Locals_Success_InvalidFlags_HelpMessage(string args) DotnetCliUtil.VerifyResultFailure(result, expectedResult); } + [Fact] + public void Locals_InvalidFlags_SingleDash_List_ParsedAsShortAlias_HelpMessage() + { + DotnetCli.Should().NotBeNull(because: "Could not locate the dotnet CLI"); + XplatDll.Should().NotBeNull(because: "Could not locate the Xplat dll"); + + // Arrange + var expectedResult = "An invalid local resource name was provided. Provide one of the following values: http-cache, temp, global-packages, all."; + + // Act + var result = CommandRunner.Run( + DotnetCli, + Path.GetDirectoryName(XplatDll), + $"{XplatDll} locals -list", + testOutputHelper: _testOutputHelper); + + // Assert + DotnetCliUtil.VerifyResultFailure(result, expectedResult); + } + + [Fact] + public void Locals_InvalidFlags_SingleDash_Clear_ParsedAsShortAlias_HelpMessage() + { + DotnetCli.Should().NotBeNull(because: "Could not locate the dotnet CLI"); + XplatDll.Should().NotBeNull(because: "Could not locate the Xplat dll"); + + // Arrange + var expectedResult = "Both operations, --list and --clear, are not supported in the same command."; + + // Act + var result = CommandRunner.Run( + DotnetCli, + Path.GetDirectoryName(XplatDll), + $"{XplatDll} locals -clear", + testOutputHelper: _testOutputHelper); + + // Assert + DotnetCliUtil.VerifyResultFailure(result, expectedResult); + } + [Theory] [InlineData("locals all")] [InlineData("locals http-cache")] diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatTrustTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatTrustTests.cs index bb3ec3f7d9b..1173a99f657 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatTrustTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatTrustTests.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.CommandLine; using Microsoft.Internal.NuGet.Testing.SignedPackages.ChildProcess; -using Microsoft.Extensions.CommandLineUtils; using NuGet.CommandLine.XPlat; using NuGet.Common; using NuGet.Test.Utility; @@ -44,12 +44,11 @@ public void Trust_UnrecognizedOption_Fails(string unrecognizedOption) // Assert Assert.Equal(1, result.ExitCode); - Assert.True(result.AllOutput.Contains($"Unrecognized option '{unrecognizedOption}'")); + Assert.True(result.AllOutput.Contains($"Unrecognized command or argument '{unrecognizedOption}'")); } } [Theory] - [InlineData("-v")] [InlineData("--algorithm")] [InlineData("--allow-untrusted-root")] [InlineData("--owners")] @@ -119,7 +118,7 @@ public void Trust_VerbosityOption(string option, string verbosity, LogLevel logL var argList = new List { "trust", "list", option, verbosity }; // Act - int result = testApp.Execute(argList.ToArray()); + int result = testApp.Parse(argList.ToArray()).Invoke(); // Assert Assert.Equal(logLevel, getLogLevel()); @@ -127,14 +126,13 @@ public void Trust_VerbosityOption(string option, string verbosity, LogLevel logL }); } - private void TrustCommandArgs(Action> verify) + private void TrustCommandArgs(Action> verify) { // Arrange var logLevel = LogLevel.Information; var logger = new TestCommandOutputLogger(_testOutputHelper); - var testApp = new CommandLineApplication(); + var testApp = new RootCommand(); - testApp.Name = "dotnet nuget_test"; TrustedSignersCommand.Register(testApp, () => logger, ll => logLevel = ll); diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatVerifyTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatVerifyTests.cs index 4fc0452cb29..810ebfa1164 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatVerifyTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XPlatVerifyTests.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; +using System.CommandLine; using Moq; using NuGet.CommandLine.XPlat; using NuGet.Commands; @@ -25,7 +25,7 @@ public XPlatVerifyTests(ITestOutputHelper testOutputHelper) } [Fact] - public void VerifyCommandArgsParsing_MissingPackagePath_Throws() + public void VerifyCommandArgsParsing_MissingPackagePath_ReturnsNonZero() { VerifyCommandArgs( (mockCommandRunner, testApp, getLogLevel) => @@ -34,29 +34,10 @@ public void VerifyCommandArgsParsing_MissingPackagePath_Throws() var argList = new List() { "verify" }; // Act - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert - Assert.IsType(ex.InnerException); - Assert.Equal("Unable to verify package. Argument '' not provided.", ex.InnerException.Message); - }); - } - - [Theory] - [InlineData("-all")] - [InlineData("-Signatures")] - [InlineData("-certificate-fingerprint")] - [InlineData("--h")] - public void VerifyCommandArgsParsing_UnrcognizedOption_Throws(string unrecognizedOption) - { - VerifyCommandArgs( - (mockCommandRunner, testApp, getLogLevel) => - { - //Arrange - string[] args = new string[] { "verify", unrecognizedOption }; - - // Act & Assert - Assert.Throws(() => testApp.Execute(args)); + Assert.NotEqual(0, result); }); } @@ -81,7 +62,7 @@ public void VerifyCommandArgsParsing_VerbosityOption(string option, string verbo var argList = new List { "verify", "packageX.nupkg", option, verbosity }; // Act - var result = testApp.Execute(argList.ToArray()); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert Assert.Equal(logLevel, getLogLevel()); @@ -89,18 +70,17 @@ public void VerifyCommandArgsParsing_VerbosityOption(string option, string verbo }); } - private void VerifyCommandArgs(Action, CommandLineApplication, Func> verify) + private void VerifyCommandArgs(Action, RootCommand, Func> verify) { // Arrange var logLevel = LogLevel.Information; var logger = new TestCommandOutputLogger(_testOutputHelper); - var testApp = new CommandLineApplication(); + var testApp = new RootCommand(); var mockCommandRunner = new Mock(); mockCommandRunner .Setup(m => m.ExecuteCommandAsync(It.IsAny())) .Returns(Task.FromResult(0)); - testApp.Name = "dotnet nuget_test"; VerifyCommand.Register(testApp, () => logger, ll => logLevel = ll, diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatRemovePkgTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatRemovePkgTests.cs index a17310dd1cf..bb6813e3e1b 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatRemovePkgTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatRemovePkgTests.cs @@ -4,10 +4,10 @@ #nullable disable using System.Collections.Generic; +using System.CommandLine; using System.IO; using System.Threading.Tasks; using System.Xml.Linq; -using Microsoft.Extensions.CommandLineUtils; using Moq; using NuGet.CommandLine.XPlat; using NuGet.Packaging; @@ -49,19 +49,18 @@ public void AddPkg_RemoveParsing(string packageOption, string package, projectPath}; var logger = new TestCommandOutputLogger(_testOutputHelper); - var testApp = new CommandLineApplication(); + var testApp = new RootCommand(); var mockCommandRunner = new Mock(); mockCommandRunner .Setup(m => m.ExecuteCommand(It.IsAny(), It.IsAny())) .ReturnsAsync(0); - testApp.Name = "dotnet nuget_test"; RemovePackageReferenceCommand.Register(testApp, () => logger, () => mockCommandRunner.Object); // Act - var result = testApp.Execute(argList.ToArray()); + var result = testApp.Parse(argList.ToArray()).Invoke(); XPlatTestUtils.DisposeTemporaryFile(projectPath); diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatSignTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatSignTests.cs index 3d4de290f2c..fc0f9eced0f 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatSignTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatSignTests.cs @@ -5,15 +5,13 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Microsoft.Extensions.CommandLineUtils; +using System.CommandLine; using Moq; using NuGet.CommandLine.XPlat; using NuGet.Commands; using NuGet.Common; -using NuGet.Packaging.Signing; using Xunit; using Xunit.Abstractions; @@ -22,7 +20,6 @@ namespace NuGet.XPlat.FuncTest [Collection(XPlatCollection.Name)] public class XplatSignTests { - private const string _invalidArgException = "Invalid value provided for '{0}'. The accepted values are {1}."; private readonly ITestOutputHelper _testOutputHelper; private const string Sha256Hash = "A591A6D40BF420404A011733CFB7B190D62C65BF0BCDA32B56C92B409B0F9DCA"; @@ -32,7 +29,7 @@ public XplatSignTests(ITestOutputHelper testOutputHelper) } [Fact] - public void SignCommandArgsParsing_MissingPackagePath_Throws() + public void SignCommandArgsParsing_MissingPackagePath_ReturnsNonZero() { SignCommandArgs( (mockCommandRunner, testApp, getLogLevel, getParsedArg, _) => @@ -41,11 +38,10 @@ public void SignCommandArgsParsing_MissingPackagePath_Throws() var argList = new List() { "sign" }; // Act - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert - Assert.IsType(ex.InnerException); - Assert.Equal("Unable to sign package. Argument '' not provided.", ex.InnerException.Message); + Assert.NotEqual(0, result); }); } @@ -54,7 +50,7 @@ public void SignCommandArgsParsing_MissingPackagePath_Throws() [InlineData("\\path\file.cert", "", "test_cert_fingerprint")] [InlineData("\\path\file.cert", "test_cert_subject", "test_cert_fingerprint")] [InlineData("", "test_cert_subject", "test_cert_fingerprint")] - public void SignCommandArgParsing_MultipleCertificateOptions_Throws( + public void SignCommandArgParsing_MultipleCertificateOptions_ReturnsNonZero( string certificatePath, string certificateSubjectName, string certificateFingerprint) @@ -69,11 +65,10 @@ public void SignCommandArgParsing_MultipleCertificateOptions_Throws( var argList = new List() { "sign", packagePath, "--certificate-path", certificatePath, "--certificate-subject-name", certificateSubjectName, "--certificate-fingerprint", certificateFingerprint, timestamper }; // Act - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert - Assert.IsType(ex.InnerException); - Assert.Equal(Strings.SignCommandMultipleCertificateException, ex.InnerException.Message); + Assert.NotEqual(0, result); }); } @@ -103,7 +98,7 @@ public void SignCommandArgParsing_ValidCertificateStoreName_Succeeds(string stor var argList = new List() { "sign", packagePath, "--certificate-store-name", storeName, "--certificate-fingerprint", certificateFingerprint, "--timestamper", timestamper }; // Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); // Assert Assert.True(parsable); @@ -113,7 +108,7 @@ public void SignCommandArgParsing_ValidCertificateStoreName_Succeeds(string stor } [Fact] - public void SignCommandArgParsing_InvalidCertificateStoreName_Throws() + public void SignCommandArgParsing_InvalidCertificateStoreName_ReturnsNonZero() { // Arrange var packagePath = @"\\path\package.nupkg"; @@ -128,12 +123,10 @@ public void SignCommandArgParsing_InvalidCertificateStoreName_Throws() var argList = new List() { "sign", packagePath, "--certificate-store-name", storeName, "--certificate-fingerprint", certificateFingerprint, "--timestamper", timestamper }; // Act - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert - Assert.IsType(ex.InnerException); - string acceptedStoreNameList = string.Join(",", Enum.GetValues(typeof(StoreName)).Cast().ToList()); - Assert.Equal(string.Format(_invalidArgException, "certificate-store-name", acceptedStoreNameList), ex.InnerException.Message); + Assert.NotEqual(0, result); }); } @@ -159,7 +152,7 @@ public void SignCommandArgParsing_ValidCertificateStoreLocation_Succeeds(string var argList = new List() { "sign", packagePath, "--certificate-fingerprint", certificateFingerprint, "--certificate-store-location", storeLocation, "--timestamper", timestamper }; //Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); //Assert Assert.True(parsable); @@ -169,7 +162,7 @@ public void SignCommandArgParsing_ValidCertificateStoreLocation_Succeeds(string } [Fact] - public void SignCommandArgParsing_InvalidCertificateStoreLocation_Throws() + public void SignCommandArgParsing_InvalidCertificateStoreLocation_ReturnsNonZero() { // Arrange var packagePath = @"\\path\package.nupkg"; @@ -185,12 +178,10 @@ public void SignCommandArgParsing_InvalidCertificateStoreLocation_Throws() var argList = new List() { "sign", packagePath, "--certificate-fingerprint", certificateFingerprint, "--certificate-store-location", storeLocation, "--timestamper", timestamper }; // Act - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); + var result = testApp.Parse(argList.ToArray()).Invoke(); // Assert - Assert.IsType(ex.InnerException); - string acceptedStoreLocationList = string.Join(",", Enum.GetValues(typeof(StoreLocation)).Cast().ToList()); - Assert.Equal(string.Format(_invalidArgException, "certificate-store-location", acceptedStoreLocationList), ex.InnerException.Message); + Assert.NotEqual(0, result); }); } @@ -218,7 +209,7 @@ public void SignCommandArgParsing_ValidHashAlgorithm_Succeeds(string hashAlgorit var argList = new List() { "sign", packagePath, "--certificate-path", certificatePath, "--hash-algorithm", hashAlgorithm, "--timestamper", timestamper }; // Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); // Assert Assert.True(parsable); @@ -228,7 +219,7 @@ public void SignCommandArgParsing_ValidHashAlgorithm_Succeeds(string hashAlgorit } [Fact] - public void SignCommandArgParsing_InvalidHashAlgorithm_Throws() + public void SignCommandArgParsing_InvalidHashAlgorithm_ReturnsNonZero() { // Arrange var packagePath = @"\\path\package.nupkg"; @@ -243,11 +234,11 @@ public void SignCommandArgParsing_InvalidHashAlgorithm_Throws() //Arrange var argList = new List() { "sign", packagePath, "--certificate-path", certificatePath, "--hash-algorithm", hashAlgorithm, "--timestamper", timestamper }; - //Act & Assert - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); - Assert.IsType(ex.InnerException); - var allowedHashAlgorithms = string.Join(",", SigningSpecifications.V1.AllowedHashAlgorithms); - Assert.Equal(string.Format(_invalidArgException, "hash-algorithm", allowedHashAlgorithms), ex.InnerException.Message); + //Act + var result = testApp.Parse(argList.ToArray()).Invoke(); + + //Assert + Assert.NotEqual(0, result); }); } @@ -275,7 +266,7 @@ public void SignCommandArgParsing_ValidTimestampHashAlgorithm_Succeeds(string ti var argList = new List() { "sign", packagePath, "--certificate-path", certificatePath, "--timestamper", timestamper, "--timestamp-hash-algorithm", timestampHashAlgorithm }; // Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); // Assert Assert.True(parsable); @@ -286,7 +277,7 @@ public void SignCommandArgParsing_ValidTimestampHashAlgorithm_Succeeds(string ti } [Fact] - public void SignCommandArgParsing_InvalidTimestampHashAlgorithm_Throws() + public void SignCommandArgParsing_InvalidTimestampHashAlgorithm_ReturnsNonZero() { // Arrange var packagePath = @"\\path\package.nupkg"; @@ -300,11 +291,11 @@ public void SignCommandArgParsing_InvalidTimestampHashAlgorithm_Throws() //Arrange var argList = new List() { "sign", packagePath, "--certificate-path", certificatePath, "--timestamper", timestamper, "--timestamp-hash-algorithm", timestampHashAlgorithm }; - //Act & Assert - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); - Assert.IsType(ex.InnerException); - var allowedHashAlgorithms = string.Join(",", SigningSpecifications.V1.AllowedHashAlgorithms); - Assert.Equal(string.Format(_invalidArgException, "timestamp-hash-algorithm", allowedHashAlgorithms), ex.InnerException.Message); + //Act + var result = testApp.Parse(argList.ToArray()).Invoke(); + + //Assert + Assert.NotEqual(0, result); }); } @@ -335,7 +326,7 @@ public void SignCommandArgParsing_ValidArgsContainsCertFingerprintAsync_Succeeds "--timestamper", timestamper, "--timestamp-hash-algorithm", timestampHashAlgorithm, "--output", outputDir, "--overwrite" }; //Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); //Assert Assert.Null(getParsedArg().CertificatePath); @@ -378,7 +369,7 @@ public void SignCommandArgParsing_ValidArgsContainsCertSubjectNameAsync_Succeeds "--timestamper", timestamper, "--timestamp-hash-algorithm", timestampHashAlgorithm, "--output", outputDir, "--overwrite" }; //Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); //Assert Assert.Null(getParsedArg().CertificatePath); @@ -420,7 +411,7 @@ public void SignCommandArgParsing_ValidArgsContainsCertPathAsync_Succeeds() var argList = new List() { "sign", packagePath, "--certificate-path", certificatePath, "--hash-algorithm", hashAlgorithm, "--timestamper", timestamper, "--timestamp-hash-algorithm", timestampHashAlgorithm, "--output", outputDir, "--overwrite" }; //Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); //Assert Assert.Equal(certificatePath, getParsedArg().CertificatePath, StringComparer.Ordinal); @@ -440,7 +431,7 @@ public void SignCommandArgParsing_ValidArgsContainsCertPathAsync_Succeeds() [InlineData("89967D1DD995010B6C66AE24FF8E66885E6E03A8")] // 40 characters long SHA-1 hash [InlineData("89967D1DD995010B6C66AE24FF8E66885E6E03")] // 39 characters long not SHA-1 hash [InlineData("invalid-certificate-fingerprint")] - public void SignCommandArgParsing_ThrowsAnExceptionForInvalidCertificateFingerprint(string certificateFingerprint) + public void SignCommandArgParsing_InvalidCertificateFingerprint_ReturnsNonZero(string certificateFingerprint) { var packagePath = @"\\path\package.nupkg"; var timestamper = "https://timestamper.test"; @@ -453,10 +444,11 @@ public void SignCommandArgParsing_ThrowsAnExceptionForInvalidCertificateFingerpr //Arrange var argList = new List() { "sign", packagePath, "--certificate-fingerprint", certificateFingerprint, "--certificate-password", "password", "--timestamper", timestamper, "--timestamp-hash-algorithm", timestampHashAlgorithm }; - //Act & Assert - var ex = Assert.Throws(() => testApp.Execute(argList.ToArray())); - Assert.IsType(ex.InnerException); - Assert.True(ex.InnerException.Message.Contains(NuGetLogCode.NU3043.ToString())); + //Act + var result = testApp.Parse(argList.ToArray()).Invoke(); + + //Assert + Assert.NotEqual(0, result); }); } @@ -478,7 +470,7 @@ public void SignCommandArgParsing_DoesNotLogAWarningForSecureCertificateFingerpr var argList = new List() { "sign", packagePath, "--certificate-fingerprint", fingerprint, "--certificate-password", "password", "--timestamper", timestamper, "--timestamp-hash-algorithm", timestampHashAlgorithm }; //Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); //Assert Assert.Equal(expected: 0, actual: logger.Warnings); @@ -498,7 +490,7 @@ public void SignCommandArgParsing_AllowUntrustedRoot_SetsAllowUntrustedRoot() var argList = new List() { "sign", packagePath, "--certificate-fingerprint", certificateFingerprint, "--allow-untrusted-root" }; //Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); //Assert Assert.True(getParsedArg().AllowUntrustedRoot); @@ -518,19 +510,19 @@ public void SignCommandArgParsing_DefaultAllowUntrustedRoot_IsFalse() var argList = new List() { "sign", packagePath, "--certificate-fingerprint", certificateFingerprint }; //Act - testApp.Execute(argList.ToArray()); + testApp.Parse(argList.ToArray()).Invoke(); //Assert Assert.False(getParsedArg().AllowUntrustedRoot); }); } - private void SignCommandArgs(Action, CommandLineApplication, Func, Func, TestCommandOutputLogger> verify) + private void SignCommandArgs(Action, RootCommand, Func, Func, TestCommandOutputLogger> verify) { // Arrange var logLevel = LogLevel.Information; var logger = new TestCommandOutputLogger(_testOutputHelper); - var testApp = new CommandLineApplication(); + var testApp = new RootCommand(); var mockCommandRunner = new Mock(); SignArgs parsedArgs = null; @@ -539,7 +531,6 @@ private void SignCommandArgs(Action, CommandLineApplica .Callback(x => parsedArgs = x) .Returns(Task.FromResult(0)); - testApp.Name = "dotnet nuget_test"; SignCommand.Register(testApp, () => logger, ll => logLevel = ll, diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Why/WhyCommandLineParsingTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Why/WhyCommandLineParsingTests.cs index 5fecd2365a6..3c0ea8f4b64 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Why/WhyCommandLineParsingTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Why/WhyCommandLineParsingTests.cs @@ -186,20 +186,20 @@ public void FrameworkOption_AcceptsMultipleValues() public void HelpOption_ShowsHelp() { // Arrange - Command rootCommand = new("nuget"); + RootCommand rootCommand = new(); + Command nugetCommand = new("nuget"); + rootCommand.Subcommands.Add(nugetCommand); var console = new Lazy(() => new TestConsole()); - WhyCommand.Register(rootCommand, console, whyCommandArgs => + WhyCommand.Register(nugetCommand, console, whyCommandArgs => { - // Assert - whyCommandArgs.Path.Should().Be("my.proj"); - whyCommandArgs.Package.Should().Be("packageid"); - whyCommandArgs.Frameworks.Should().Equal(["net8.0", "net481"]); - return Task.FromResult(0); + throw new InvalidOperationException("Command action should not be invoked when help is requested."); }); // Act var result = rootCommand.Parse($"nuget why -h"); + + // Assert result.Errors.Should().BeEmpty(); result.Action.Should().BeOfType(); }