diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 28fa6150fc..a4cfadf4a7 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "Argument.h" +#include "Command.h" #include "Resources.h" #include @@ -108,6 +109,19 @@ namespace AppInstaller::CLI args.push_back(ForType(Args::Type::VerboseLogs)); } + void Argument::ValidatePackageSelectionArgumentSupplied(const Execution::Args& args) + { + for (Args::Type type : { Args::Type::Query, Args::Type::Manifest, Args::Type::Id, Args::Type::Name, Args::Type::Moniker, Args::Type::Tag, Args::Type::Command }) + { + if (args.Contains(type)) + { + return; + } + } + + throw CommandException(Resource::String::NoPackageSelectionArgumentProvided); + } + Argument::Visibility Argument::GetVisibility() const { if (!ExperimentalFeature::IsEnabled(m_feature)) diff --git a/src/AppInstallerCLICore/Argument.h b/src/AppInstallerCLICore/Argument.h index b23c4cc5ef..77b6f996b0 100644 --- a/src/AppInstallerCLICore/Argument.h +++ b/src/AppInstallerCLICore/Argument.h @@ -81,6 +81,11 @@ namespace AppInstaller::CLI // Gets the common arguments for all commands. static void GetCommon(std::vector& args); + // Static argument validation helpers; throw CommandException when validation fails. + + // Requires that some form of package selection argument is present + static void ValidatePackageSelectionArgumentSupplied(const Execution::Args& args); + // Arguments are not localized at this time. Utility::LocIndView Name() const { return Utility::LocIndView{ m_name }; } char Alias() const { return m_alias; } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 9ae6d2b338..9f198a216f 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -93,6 +93,8 @@ namespace AppInstaller::CLI void InstallCommand::ValidateArgumentsInternal(Args& execArgs) const { + Argument::ValidatePackageSelectionArgumentSupplied(execArgs); + if (execArgs.Contains(Args::Type::Manifest) && (execArgs.Contains(Args::Type::Query) || execArgs.Contains(Args::Type::Id) || @@ -115,7 +117,7 @@ namespace AppInstaller::CLI } if (execArgs.Contains(Args::Type::InstallArchitecture)) { - Utility::Architecture selectedArch = Utility::ConvertToArchitectureEnum(std::string(execArgs.GetArg(Args::Type::InstallArchitecture))); + Utility::Architecture selectedArch = Utility::ConvertToArchitectureEnum(std::string(execArgs.GetArg(Args::Type::InstallArchitecture))); if ((selectedArch == Utility::Architecture::Unknown) || (Utility::IsApplicableArchitecture(selectedArch) == Utility::InapplicableArchitecture)) { std::vector applicableArchitectures; diff --git a/src/AppInstallerCLICore/Commands/SearchCommand.cpp b/src/AppInstallerCLICore/Commands/SearchCommand.cpp index e53e513dab..b027d7d2de 100644 --- a/src/AppInstallerCLICore/Commands/SearchCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SearchCommand.cpp @@ -66,6 +66,11 @@ namespace AppInstaller::CLI return "https://aka.ms/winget-command-search"; } + void SearchCommand::ValidateArgumentsInternal(Args& execArgs) const + { + Argument::ValidatePackageSelectionArgumentSupplied(execArgs); + } + void SearchCommand::ExecuteInternal(Context& context) const { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); diff --git a/src/AppInstallerCLICore/Commands/SearchCommand.h b/src/AppInstallerCLICore/Commands/SearchCommand.h index be91775cb9..e9cbdc90e2 100644 --- a/src/AppInstallerCLICore/Commands/SearchCommand.h +++ b/src/AppInstallerCLICore/Commands/SearchCommand.h @@ -19,6 +19,7 @@ namespace AppInstaller::CLI std::string HelpLink() const override; protected: + void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } diff --git a/src/AppInstallerCLICore/Commands/ShowCommand.cpp b/src/AppInstallerCLICore/Commands/ShowCommand.cpp index cd0eaf8fce..d993c16980 100644 --- a/src/AppInstallerCLICore/Commands/ShowCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ShowCommand.cpp @@ -7,6 +7,8 @@ #include "Workflows/WorkflowBase.h" #include "Resources.h" +using namespace AppInstaller::CLI::Execution; + namespace AppInstaller::CLI { std::vector ShowCommand::GetArguments() const @@ -49,6 +51,24 @@ namespace AppInstaller::CLI return "https://aka.ms/winget-command-show"; } + void ShowCommand::ValidateArgumentsInternal(Args& execArgs) const + { + Argument::ValidatePackageSelectionArgumentSupplied(execArgs); + + if (execArgs.Contains(Args::Type::Manifest) && + (execArgs.Contains(Args::Type::Query) || + execArgs.Contains(Args::Type::Id) || + execArgs.Contains(Args::Type::Name) || + execArgs.Contains(Args::Type::Moniker) || + execArgs.Contains(Args::Type::Version) || + execArgs.Contains(Args::Type::Channel) || + execArgs.Contains(Args::Type::Source) || + execArgs.Contains(Args::Type::Exact))) + { + throw CommandException(Resource::String::BothManifestAndSearchQueryProvided); + } + } + void ShowCommand::ExecuteInternal(Execution::Context& context) const { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); diff --git a/src/AppInstallerCLICore/Commands/ShowCommand.h b/src/AppInstallerCLICore/Commands/ShowCommand.h index eac5ed59d8..f0b914890f 100644 --- a/src/AppInstallerCLICore/Commands/ShowCommand.h +++ b/src/AppInstallerCLICore/Commands/ShowCommand.h @@ -19,6 +19,7 @@ namespace AppInstaller::CLI std::string HelpLink() const override; protected: + void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(AppInstaller::CLI::Execution::Context& context) const override; }; } diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index 1f6b3264f6..7ba9907a88 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -83,6 +83,8 @@ namespace AppInstaller::CLI void UninstallCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { + Argument::ValidatePackageSelectionArgumentSupplied(execArgs); + if (execArgs.Contains(Execution::Args::Type::Manifest) && (execArgs.Contains(Execution::Args::Type::Query) || execArgs.Contains(Execution::Args::Type::Id) || diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 81f41ea833..2984bd9e7f 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -187,6 +187,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(NoExperimentalFeaturesMessage); WINGET_DEFINE_RESOURCE_STRINGID(NoInstalledPackageFound); WINGET_DEFINE_RESOURCE_STRINGID(NoPackageFound); + WINGET_DEFINE_RESOURCE_STRINGID(NoPackageSelectionArgumentProvided); WINGET_DEFINE_RESOURCE_STRINGID(NoPackagesFoundInImportFile); WINGET_DEFINE_RESOURCE_STRINGID(NoUninstallInfoFound); WINGET_DEFINE_RESOURCE_STRINGID(NoVTArgumentDescription); diff --git a/src/AppInstallerCLIE2ETests/GroupPolicy.cs b/src/AppInstallerCLIE2ETests/GroupPolicy.cs index 0b56d2f933..9309fc97c8 100644 --- a/src/AppInstallerCLIE2ETests/GroupPolicy.cs +++ b/src/AppInstallerCLIE2ETests/GroupPolicy.cs @@ -29,7 +29,7 @@ public void TearDown() public void PolicyEnableWinget() { GroupPolicyHelper.EnableWinget.Disable(); - var result = TestCommon.RunAICLICommand("search", string.Empty); + var result = TestCommon.RunAICLICommand("search", "foo"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } diff --git a/src/AppInstallerCLIE2ETests/SearchCommand.cs b/src/AppInstallerCLIE2ETests/SearchCommand.cs index 39831af29f..d342ddf7fe 100644 --- a/src/AppInstallerCLIE2ETests/SearchCommand.cs +++ b/src/AppInstallerCLIE2ETests/SearchCommand.cs @@ -11,10 +11,7 @@ public class SearchCommand : BaseCommand public void SearchWithoutArgs() { var result = TestCommon.RunAICLICommand("search", ""); - Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(result.StdOut.Contains("AppInstallerTest.TestExeInstaller")); - Assert.True(result.StdOut.Contains("AppInstallerTest.TestBurnInstaller")); - Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); + Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, result.ExitCode); } [Test] diff --git a/src/AppInstallerCLIE2ETests/ShowCommand.cs b/src/AppInstallerCLIE2ETests/ShowCommand.cs index 0a301a9a1c..69a65e6043 100644 --- a/src/AppInstallerCLIE2ETests/ShowCommand.cs +++ b/src/AppInstallerCLIE2ETests/ShowCommand.cs @@ -10,13 +10,8 @@ public class ShowCommand : BaseCommand [Test] public void ShowWithNoArgs() { - // Show with no arg lists every app and a warning message var result = TestCommon.RunAICLICommand("show", ""); - Assert.AreEqual(Constants.ErrorCode.ERROR_MULTIPLE_APPLICATIONS_FOUND, result.ExitCode); - Assert.True(result.StdOut.Contains("Multiple packages found matching input criteria. Please refine the input.")); - Assert.True(result.StdOut.Contains("AppInstallerTest.TestExeInstaller")); - Assert.True(result.StdOut.Contains("AppInstallerTest.TestBurnInstaller")); - Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); + Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, result.ExitCode); } [Test] diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 3351e02a73..0168c18eaa 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1287,4 +1287,7 @@ Please specify one of them using the `--source` option to proceed. Prompts the user to press any key before exiting + + No package selection argument was provided; see the help for details about finding a package. + \ No newline at end of file