diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 2de195e6ff..c9de94dad0 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -420,6 +420,8 @@ namespace AppInstaller::CLI return Argument{ type, Resource::String::DependenciesOnlyArgumentDescription, ArgumentType::Flag, false }; case Args::Type::IgnoreLocalArchiveMalwareScan: return Argument{ type, Resource::String::IgnoreLocalArchiveMalwareScanArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::LocalArchiveMalwareScanOverride, Settings::BoolAdminSetting::LocalArchiveMalwareScanOverride }; + case Args::Type::IgnoreUnavailable: + return Argument{ type, Resource::String::IgnoreUnavailableArgumentDescription, ArgumentType::Flag }; case Args::Type::SourceName: return Argument{ type, Resource::String::SourceNameArgumentDescription, ArgumentType::Positional, false }; case Args::Type::SourceArg: diff --git a/src/AppInstallerCLICore/Commands/ImportCommand.cpp b/src/AppInstallerCLICore/Commands/ImportCommand.cpp index 39e4d9b4ab..ec8c638f1c 100644 --- a/src/AppInstallerCLICore/Commands/ImportCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ImportCommand.cpp @@ -17,7 +17,7 @@ namespace AppInstaller::CLI { return { Argument{ Execution::Args::Type::ImportFile, Resource::String::ImportFileArgumentDescription, ArgumentType::Positional, true }, - Argument{ Execution::Args::Type::IgnoreUnavailable, Resource::String::ImportIgnoreUnavailableArgumentDescription, ArgumentType::Flag }, + Argument::ForType(Execution::Args::Type::IgnoreUnavailable), Argument{ Execution::Args::Type::IgnoreVersions, Resource::String::ImportIgnorePackageVersionsArgumentDescription, ArgumentType::Flag }, Argument::ForType(Execution::Args::Type::NoUpgrade), Argument::ForType(Execution::Args::Type::AcceptPackageAgreements), diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index e96139186e..ab52f9076c 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -58,6 +58,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::UninstallPrevious), Argument::ForType(Args::Type::Force), Argument{ Args::Type::IncludeUnknown, Resource::String::IncludeUnknownArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden}, + Argument::ForType(Args::Type::IgnoreUnavailable), }; } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index b35f9b3dc9..175ad24c8c 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -353,6 +353,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(IdArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IgnoreLocalArchiveMalwareScanArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IgnoreResumeLimitArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(IgnoreUnavailableArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IgnoreWarningsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandReportDependencies); @@ -360,7 +361,6 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ImportFileArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportFileHasInvalidSchema); WINGET_DEFINE_RESOURCE_STRINGID(ImportIgnorePackageVersionsArgumentDescription); - WINGET_DEFINE_RESOURCE_STRINGID(ImportIgnoreUnavailableArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportInstallFailed); WINGET_DEFINE_RESOURCE_STRINGID(ImportSourceNotInstalled); WINGET_DEFINE_RESOURCE_STRINGID(IncludePinnedArgumentDescription); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index b562e3022a..7ce1edd567 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -894,7 +894,7 @@ They can be configured through the settings file 'winget settings'. Package is already installed: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. - + Ignore unavailable packages diff --git a/src/AppInstallerCLITests/InstallFlow.cpp b/src/AppInstallerCLITests/InstallFlow.cpp index 64fb767bbc..88e1a8f89f 100644 --- a/src/AppInstallerCLITests/InstallFlow.cpp +++ b/src/AppInstallerCLITests/InstallFlow.cpp @@ -234,7 +234,7 @@ TEST_CASE("InstallFlow_UnsupportedArguments_Error", "[InstallFlow][workflow]") install.Execute(context); INFO(installOutput.str()); - // Verify unsupported arguments error message is shown + // Verify unsupported arguments error message is shown REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UnsupportedArgument).get()) != std::string::npos); @@ -1343,6 +1343,29 @@ TEST_CASE("InstallFlow_InstallMultiple_SearchFailed", "[InstallFlow][workflow][M REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); } +TEST_CASE("InstallFlow_InstallMultiple_IgnoreUnavailable", "[InstallFlow][workflow][MultiQuery]") +{ + TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForShellExecute(context); + OverrideForOpenSource(context, CreateTestSource({ TSR::TestInstaller_Exe }), true); + context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query); + context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query); + context.Args.AddArg(Execution::Args::Type::IgnoreUnavailable); + + InstallCommand installCommand({}); + installCommand.Execute(context); + INFO(installOutput.str()); + + // Verify the available package was installed despite the missing one, + // and the unavailable package was reported as not found. + REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::MultiQueryPackageNotFound(LocIndString{ TSR::TestInstaller_Msix.Query })).get()) != std::string::npos); +} + TEST_CASE("InstallFlow_InstallAcquiresLock", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt");