commit 9acb01b2fbc10ecc943ed56edf92c60084b95c66 parent 2b47250e7c51e49c86a1d508e94b411d37ebbbe2 Author: Kaleb Luedtke <jluedtk@jci.com> Date: Fri, 10 Mar 2023 19:57:20 -0600 Clarify `No installed package matching the input criteria was found` in upgrade flow (#2877) Diffstat:
24 files changed, 189 insertions(+), 130 deletions(-)
diff --git a/src/AppInstallerCLICore/Commands/COMCommand.cpp b/src/AppInstallerCLICore/Commands/COMCommand.cpp @@ -7,13 +7,13 @@ #include "Workflows/UninstallFlow.h" #include "Workflows/WorkflowBase.h" -using namespace AppInstaller::CLI::Execution; -using namespace AppInstaller::CLI::Workflow; -using namespace AppInstaller::Manifest; -using namespace AppInstaller::Utility::literals; - namespace AppInstaller::CLI { + using namespace AppInstaller::CLI::Execution; + using namespace AppInstaller::CLI::Workflow; + using namespace AppInstaller::Manifest; + using namespace AppInstaller::Utility::literals; + // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data void COMDownloadCommand::ExecuteInternal(Context& context) const { diff --git a/src/AppInstallerCLICore/Commands/ExportCommand.cpp b/src/AppInstallerCLICore/Commands/ExportCommand.cpp @@ -9,6 +9,7 @@ namespace AppInstaller::CLI { + using namespace AppInstaller::CLI::Workflow; using namespace std::string_view_literals; std::vector<Argument> ExportCommand::GetArguments() const @@ -56,14 +57,14 @@ namespace AppInstaller::CLI context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); context << - Workflow::ReportExecutionStage(Workflow::ExecutionStage::Discovery) << + Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed) << Workflow::SearchSourceForMany << Workflow::HandleSearchResultFailures << - Workflow::EnsureMatchesFromSearchResult(true) << + Workflow::EnsureMatchesFromSearchResult(OperationType::Export) << Workflow::SelectVersionsToExport << - Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) << + Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::WriteImportFile; } } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -9,13 +9,14 @@ #include "Workflows/WorkflowBase.h" #include "Resources.h" -using namespace AppInstaller::CLI::Execution; -using namespace AppInstaller::CLI::Workflow; -using namespace AppInstaller::Manifest; -using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI { + using namespace AppInstaller::CLI::Execution; + using namespace AppInstaller::CLI::Workflow; + using namespace AppInstaller::Manifest; + using namespace AppInstaller::Utility::literals; + std::vector<Argument> InstallCommand::GetArguments() const { return { @@ -136,7 +137,7 @@ namespace AppInstaller::CLI } else { - context << Workflow::InstallOrUpgradeSinglePackage(false); + context << Workflow::InstallOrUpgradeSinglePackage(OperationType::Install); } } } diff --git a/src/AppInstallerCLICore/Commands/ListCommand.cpp b/src/AppInstallerCLICore/Commands/ListCommand.cpp @@ -8,6 +8,7 @@ namespace AppInstaller::CLI { + using namespace AppInstaller::CLI::Workflow; using namespace std::string_view_literals; std::vector<Argument> ListCommand::GetArguments() const @@ -78,7 +79,7 @@ namespace AppInstaller::CLI Workflow::OpenCompositeSource(Workflow::DetermineInstalledSource(context)) << Workflow::SearchSourceForMany << Workflow::HandleSearchResultFailures << - Workflow::EnsureMatchesFromSearchResult(true) << + Workflow::EnsureMatchesFromSearchResult(OperationType::List) << Workflow::ReportListResult(); } } diff --git a/src/AppInstallerCLICore/Commands/PinCommand.cpp b/src/AppInstallerCLICore/Commands/PinCommand.cpp @@ -10,6 +10,7 @@ namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; + using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Utility::literals; using namespace std::string_view_literals; @@ -117,7 +118,7 @@ namespace AppInstaller::CLI Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed) << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << - Workflow::EnsureOneMatchFromSearchResult(false) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Pin) << Workflow::GetInstalledPackageVersion << Workflow::ReportPackageIdentity << Workflow::OpenPinningIndex() << @@ -189,7 +190,7 @@ namespace AppInstaller::CLI Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed) << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << - Workflow::EnsureOneMatchFromSearchResult(false) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Pin) << Workflow::GetInstalledPackageVersion << Workflow::ReportPackageIdentity << Workflow::OpenPinningIndex() << diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -24,10 +24,9 @@ #include "Resources.h" #include "TableOutput.h" -using namespace AppInstaller::Utility::literals; - namespace AppInstaller::CLI { + using namespace AppInstaller::Utility::literals; using namespace Settings; namespace diff --git a/src/AppInstallerCLICore/Commands/SearchCommand.cpp b/src/AppInstallerCLICore/Commands/SearchCommand.cpp @@ -9,6 +9,7 @@ namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; + using namespace AppInstaller::CLI::Workflow; using namespace std::string_view_literals; std::vector<Argument> SearchCommand::GetArguments() const @@ -84,14 +85,14 @@ namespace AppInstaller::CLI if (context.Args.Contains(Execution::Args::Type::ListVersions)) { context << - Workflow::EnsureOneMatchFromSearchResult(false) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Search) << Workflow::ReportPackageIdentity << Workflow::ShowAppVersions; } else { context << - Workflow::EnsureMatchesFromSearchResult(false) << + Workflow::EnsureMatchesFromSearchResult(OperationType::Search) << Workflow::ReportSearchResult; } diff --git a/src/AppInstallerCLICore/Commands/ShowCommand.cpp b/src/AppInstallerCLICore/Commands/ShowCommand.cpp @@ -7,10 +7,11 @@ #include "Workflows/WorkflowBase.h" #include "Resources.h" -using namespace AppInstaller::CLI::Execution; - namespace AppInstaller::CLI { + using namespace AppInstaller::CLI::Execution; + using namespace AppInstaller::CLI::Workflow; + std::vector<Argument> ShowCommand::GetArguments() const { return { @@ -78,7 +79,7 @@ namespace AppInstaller::CLI Workflow::OpenSource() << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << - Workflow::EnsureOneMatchFromSearchResult(false) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Show) << Workflow::ReportPackageIdentity << Workflow::ShowAppVersions; } @@ -86,7 +87,7 @@ namespace AppInstaller::CLI else { context << - Workflow::GetManifest(/* considerPins */ false) << + GetManifest( /* considerPins */ false) << Workflow::ReportManifestIdentity << Workflow::SelectInstaller << Workflow::ShowManifestInfo; diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -8,11 +8,11 @@ #include "Workflows/MultiQueryFlow.h" #include "Resources.h" -using AppInstaller::CLI::Execution::Args; -using AppInstaller::CLI::Workflow::ExecutionStage; - namespace AppInstaller::CLI { + using AppInstaller::CLI::Execution::Args; + using namespace AppInstaller::CLI::Workflow; + std::vector<Argument> UninstallCommand::GetArguments() const { return @@ -111,7 +111,7 @@ namespace AppInstaller::CLI Workflow::GetManifestFromArg << Workflow::ReportManifestIdentity << Workflow::SearchSourceUsingManifest << - Workflow::EnsureOneMatchFromSearchResult(true) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Uninstall) << Workflow::UninstallSinglePackage; } else @@ -122,7 +122,7 @@ namespace AppInstaller::CLI context << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << - Workflow::EnsureOneMatchFromSearchResult(true) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Uninstall) << Workflow::ReportPackageIdentity << Workflow::UninstallSinglePackage; } @@ -130,7 +130,7 @@ namespace AppInstaller::CLI { context << Workflow::GetMultiSearchRequests << - Workflow::SearchSubContextsForSingle(Workflow::SearchSubContextsForSingle::SearchPurpose::Uninstall) << + Workflow::SearchSubContextsForSingle(OperationType::Uninstall) << Workflow::UninstallMultiplePackages; } } diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -11,13 +11,13 @@ #include "Resources.h" #include <winget/LocIndependent.h> -using namespace AppInstaller::CLI::Execution; -using namespace AppInstaller::Manifest; -using namespace AppInstaller::CLI::Workflow; -using namespace AppInstaller::Utility::literals; - namespace AppInstaller::CLI { + using namespace AppInstaller::CLI::Execution; + using namespace AppInstaller::Manifest; + using namespace AppInstaller::CLI::Workflow; + using namespace AppInstaller::Utility::literals; + namespace { // Determines whether we should list available upgrades, instead @@ -162,7 +162,7 @@ namespace AppInstaller::CLI context << SearchSourceForMany << HandleSearchResultFailures << - EnsureMatchesFromSearchResult(true) << + EnsureMatchesFromSearchResult(OperationType::Upgrade) << ReportListResult(true); } else if (context.Args.Contains(Execution::Args::Type::All)) @@ -171,7 +171,7 @@ namespace AppInstaller::CLI context << SearchSourceForMany << HandleSearchResultFailures << - EnsureMatchesFromSearchResult(true) << + EnsureMatchesFromSearchResult(OperationType::Upgrade) << ReportListResult(true) << UpdateAllApplicable; } @@ -181,7 +181,7 @@ namespace AppInstaller::CLI context << GetManifestFromArg << SearchSourceUsingManifest << - EnsureOneMatchFromSearchResult(true) << + EnsureOneMatchFromSearchResult(OperationType::Upgrade) << GetInstalledPackageVersion << EnsureUpdateVersionApplicable << SelectInstaller << @@ -193,13 +193,13 @@ namespace AppInstaller::CLI // The remaining case: search for specific packages to update if (!context.Args.Contains(Execution::Args::Type::MultiQuery)) { - context << Workflow::InstallOrUpgradeSinglePackage(/* isUpgrade */ true); + context << Workflow::InstallOrUpgradeSinglePackage(OperationType::Upgrade); } else { context << Workflow::GetMultiSearchRequests << - Workflow::SearchSubContextsForSingle(Workflow::SearchSubContextsForSingle::SearchPurpose::Upgrade) << + Workflow::SearchSubContextsForSingle(OperationType::Upgrade) << Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) << Workflow::InstallMultiplePackages( Resource::String::InstallAndUpgradeCommandsReportDependencies, diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h @@ -466,6 +466,9 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UnsupportedArgument); WINGET_DEFINE_RESOURCE_STRINGID(UpdateAllArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(UpdateNotApplicable); + WINGET_DEFINE_RESOURCE_STRINGID(UpdateNotApplicableReason); + WINGET_DEFINE_RESOURCE_STRINGID(UpdateNoPackagesFound); + WINGET_DEFINE_RESOURCE_STRINGID(UpdateNoPackagesFoundReason); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeAvailableForPinned); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeBlockingPinCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandLongDescription); diff --git a/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp b/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp @@ -165,14 +165,14 @@ namespace AppInstaller::CLI::Workflow // Here we require that the standard search finds a single entry, and we list those versions. context << Workflow::SearchSourceForSingle << - Workflow::EnsureOneMatchFromSearchResult(false) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << Workflow::CompleteWithSearchResultVersions; break; case Execution::Args::Type::Channel: // Here we require that the standard search finds a single entry, and we list those channels. context << Workflow::SearchSourceForSingle << - Workflow::EnsureOneMatchFromSearchResult(false) << + Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << Workflow::CompleteWithSearchResultChannels; break; case Execution::Args::Type::Source: diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -216,7 +216,8 @@ namespace AppInstaller::CLI::Workflow if (!installItem) { - context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl; + context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl + << Resource::String::UpdateNotApplicableReason << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } diff --git a/src/AppInstallerCLICore/Workflows/MultiQueryFlow.cpp b/src/AppInstallerCLICore/Workflows/MultiQueryFlow.cpp @@ -69,18 +69,16 @@ namespace AppInstaller::CLI::Workflow SearchRequest searchRequest = searchContext.Get<Execution::Data::SearchRequest>(); searchContext.Add<Execution::Data::SearchResult>(searchContext.Get<Execution::Data::Source>().Search(searchRequest)); - switch (m_searchPurpose) + switch (m_operationType) { - case SearchPurpose::Install: - searchContext << Workflow::SelectSinglePackageVersionForInstallOrUpgrade(/* isUpgrade */ false); + case OperationType::Install: + case OperationType::Upgrade: + searchContext << Workflow::SelectSinglePackageVersionForInstallOrUpgrade(m_operationType); break; - case SearchPurpose::Upgrade: - searchContext << Workflow::SelectSinglePackageVersionForInstallOrUpgrade(/* isUpgrade */ true); - break; - case SearchPurpose::Uninstall: + case OperationType::Uninstall: searchContext << Workflow::HandleSearchResultFailures << - Workflow::EnsureOneMatchFromSearchResult(true); + Workflow::EnsureOneMatchFromSearchResult(m_operationType); break; default: THROW_HR(E_UNEXPECTED); diff --git a/src/AppInstallerCLICore/Workflows/MultiQueryFlow.h b/src/AppInstallerCLICore/Workflows/MultiQueryFlow.h @@ -23,18 +23,11 @@ namespace AppInstaller::CLI::Workflow // SubContext Outputs: SearchResult struct SearchSubContextsForSingle : public WorkflowTask { - enum class SearchPurpose - { - Install, - Upgrade, - Uninstall, - }; - - SearchSubContextsForSingle(SearchPurpose searchPurpose = SearchPurpose::Install) : WorkflowTask("SearchSubContextsForSingle"), m_searchPurpose(searchPurpose) {} + SearchSubContextsForSingle(OperationType operation = OperationType::Install) : WorkflowTask("SearchSubContextsForSingle"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: - SearchPurpose m_searchPurpose; + OperationType m_operationType; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -224,4 +224,22 @@ namespace AppInstaller::CLI::Workflow table.OutputLine({ manifest.Version, manifest.Channel }); table.Complete(); } + + void GetManifest::operator()(Execution::Context& context) const + { + if (context.Args.Contains(Execution::Args::Type::Manifest)) + { + context << + GetManifestFromArg; + } + else + { + context << + OpenSource() << + SearchSourceForSingle << + HandleSearchResultFailures << + EnsureOneMatchFromSearchResult(OperationType::Show) << + GetManifestFromPackage(m_considerPins); + } + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.h b/src/AppInstallerCLICore/Workflows/ShowFlow.h @@ -28,4 +28,18 @@ namespace AppInstaller::CLI::Workflow // Inputs: Manifest // Outputs: None void ShowManifestVersion(Execution::Context& context); + + // Composite flow that produces a manifest; either from one given on the command line or by searching. + // Required Args: None + // Inputs: None + // Outputs: Manifest + struct GetManifest : public WorkflowTask + { + GetManifest(bool considerPins) : WorkflowTask("GetManifest"), m_considerPins(considerPins) {} + + void operator()(Execution::Context& context) const override; + + private: + bool m_considerPins; + }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -17,7 +17,7 @@ namespace AppInstaller::CLI::Workflow { namespace { - bool IsUpdateVersionApplicable(const Utility::Version& installedVersion, const Utility::Version& updateVersion) + bool IsUpdateVersionAvailable(const Utility::Version& installedVersion, const Utility::Version& updateVersion) { return installedVersion < updateVersion; } @@ -73,11 +73,18 @@ namespace AppInstaller::CLI::Workflow // The version keys should have already been sorted by version const auto& versionKeys = package->GetAvailableVersionKeys(); + // Assume that no update versions are applicable + bool upgradeVersionAvailable = false; for (const auto& key : versionKeys) { // Check Applicable Version - if (!isUpgrade || IsUpdateVersionApplicable(installedVersion, Utility::Version(key.Version))) + if (!isUpgrade || IsUpdateVersionAvailable(installedVersion, Utility::Version(key.Version))) { + // The only way to enter this portion of the statement with isUpgrade is if the version is available + if (isUpgrade) + { + upgradeVersionAvailable = true; + } // Check if the package is pinned if (key.PinnedState == Pinning::PinType::Blocking || key.PinnedState == Pinning::PinType::Gating || @@ -155,7 +162,18 @@ namespace AppInstaller::CLI::Workflow } else if (isUpgrade) { - context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl; + if (!upgradeVersionAvailable) + { + // This is the case when no newer versions are available in a configured source + context.Reporter.Info() << Resource::String::UpdateNoPackagesFound << std::endl + << Resource::String::UpdateNoPackagesFoundReason << std::endl; + } + else + { + // This is the case when newer versions are available in a configured source, but none are applicable due to OS Version, user requirement, etc. + context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl + << Resource::String::UpdateNotApplicableReason << std::endl; + } } else { @@ -173,9 +191,10 @@ namespace AppInstaller::CLI::Workflow Utility::Version installedVersion = Utility::Version(installedPackage->GetProperty(PackageVersionProperty::Version)); Utility::Version updateVersion(context.Get<Execution::Data::Manifest>().Version); - if (!IsUpdateVersionApplicable(installedVersion, updateVersion)) + if (!IsUpdateVersionAvailable(installedVersion, updateVersion)) { - context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl; + context.Reporter.Info() << Resource::String::UpdateNoPackagesFound << std::endl + << Resource::String::UpdateNoPackagesFoundReason << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } } @@ -269,12 +288,16 @@ namespace AppInstaller::CLI::Workflow void SelectSinglePackageVersionForInstallOrUpgrade::operator()(Execution::Context& context) const { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_operationType != OperationType::Install && m_operationType != OperationType::Upgrade); + context << HandleSearchResultFailures << - EnsureOneMatchFromSearchResult(m_isUpgrade) << + EnsureOneMatchFromSearchResult(m_operationType) << GetInstalledPackageVersion; - if (!m_isUpgrade && context.Contains(Execution::Data::InstalledPackageVersion) && context.Get<Execution::Data::InstalledPackageVersion>() != nullptr) + if ( m_operationType != OperationType::Upgrade && + context.Contains(Execution::Data::InstalledPackageVersion) && + context.Get<Execution::Data::InstalledPackageVersion>() != nullptr ) { if (context.Args.Contains(Execution::Args::Type::NoUpgrade)) { @@ -287,7 +310,7 @@ namespace AppInstaller::CLI::Workflow AICLI_LOG(CLI, Info, << "Found installed package, converting to upgrade flow"); context.Reporter.Info() << Execution::ConvertToUpgradeFlowEmphasis << Resource::String::ConvertInstallFlowToUpgrade << std::endl; context.SetFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); - m_isUpgrade = true; + m_operationType = OperationType::Upgrade; } } @@ -296,7 +319,7 @@ namespace AppInstaller::CLI::Workflow // If version specified, use the version and verify applicability context << GetManifestFromPackage(/* considerPins */ true); - if (m_isUpgrade) + if (m_operationType == OperationType::Upgrade) { context << EnsureUpdateVersionApplicable; } @@ -315,9 +338,11 @@ namespace AppInstaller::CLI::Workflow void InstallOrUpgradeSinglePackage::operator()(Execution::Context& context) const { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_operationType != OperationType::Install && m_operationType != OperationType::Upgrade); + context << SearchSourceForSingle << - SelectSinglePackageVersionForInstallOrUpgrade(m_isUpgrade) << + SelectSinglePackageVersionForInstallOrUpgrade(m_operationType) << InstallSinglePackage; } } diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.h b/src/AppInstallerCLICore/Workflows/UpdateFlow.h @@ -39,13 +39,13 @@ namespace AppInstaller::CLI::Workflow // Outputs: None struct SelectSinglePackageVersionForInstallOrUpgrade : public WorkflowTask { - SelectSinglePackageVersionForInstallOrUpgrade(bool isUpgrade) : - WorkflowTask("SelectSinglePackageVersionForInstallOrUpgrade"), m_isUpgrade(isUpgrade) {} + SelectSinglePackageVersionForInstallOrUpgrade(OperationType operation) : + WorkflowTask("SelectSinglePackageVersionForInstallOrUpgrade"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: - mutable bool m_isUpgrade; + mutable OperationType m_operationType; }; // Install or upgrade a single package @@ -54,12 +54,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: None struct InstallOrUpgradeSinglePackage : public WorkflowTask { - InstallOrUpgradeSinglePackage(bool isUpgrade) : - WorkflowTask("InstallOrUpgradeSinglePackage"), m_isUpgrade(isUpgrade) {} + InstallOrUpgradeSinglePackage(OperationType operation) : + WorkflowTask("InstallOrUpgradeSinglePackage"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: - mutable bool m_isUpgrade; + mutable OperationType m_operationType; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -855,14 +855,23 @@ namespace AppInstaller::CLI::Workflow if (searchResult.Matches.size() == 0) { Logging::Telemetry().LogNoAppMatch(); - - if (m_isFromInstalledSource) - { - context.Reporter.Info() << Resource::String::NoInstalledPackageFound << std::endl; - } - else + + switch (m_operationType) { - context.Reporter.Info() << Resource::String::NoPackageFound << std::endl; + // These search purposes require a package to be found in the Installed Packages + case OperationType::Export: + case OperationType::List: + case OperationType::Uninstall: + case OperationType::Pin: + case OperationType::Upgrade: + context.Reporter.Info() << Resource::String::NoInstalledPackageFound << std::endl; + case OperationType::Completion: + case OperationType::Install: + case OperationType::Search: + case OperationType::Show: + default: + context.Reporter.Info() << Resource::String::NoPackageFound << std::endl; + break; } AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); @@ -872,7 +881,7 @@ namespace AppInstaller::CLI::Workflow void EnsureOneMatchFromSearchResult::operator()(Execution::Context& context) const { context << - EnsureMatchesFromSearchResult(m_isFromInstalledSource); + EnsureMatchesFromSearchResult(m_operationType); if (!context.IsTerminated()) { @@ -882,7 +891,7 @@ namespace AppInstaller::CLI::Workflow { Logging::Telemetry().LogMultiAppMatch(); - if (m_isFromInstalledSource) + if (m_operationType == OperationType::Upgrade || m_operationType == OperationType::Uninstall ) { context.Reporter.Warn() << Resource::String::MultipleInstalledPackagesFound << std::endl; context << ReportMultiplePackageFoundResult; @@ -1083,24 +1092,6 @@ namespace AppInstaller::CLI::Workflow ReportIdentity(context, m_prefix, m_label, manifest.CurrentLocalization.Get<Manifest::Localization::PackageName>(), manifest.Id, manifest.Version, m_level); } - void GetManifest::operator()(Execution::Context& context) const - { - if (context.Args.Contains(Execution::Args::Type::Manifest)) - { - context << - GetManifestFromArg; - } - else - { - context << - OpenSource() << - SearchSourceForSingle << - HandleSearchResultFailures << - EnsureOneMatchFromSearchResult(false) << - GetManifestFromPackage(m_considerPins); - } - } - void SelectInstaller(Execution::Context& context) { bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -29,6 +29,19 @@ namespace AppInstaller::CLI::Workflow PostExecution = 5000, }; + enum class OperationType + { + Completion, + Export, + Install, + List, + Pin, + Search, + Show, + Uninstall, + Upgrade, + }; + // A task in the workflow. struct WorkflowTask { @@ -216,13 +229,13 @@ namespace AppInstaller::CLI::Workflow // Outputs: None struct EnsureMatchesFromSearchResult : public WorkflowTask { - EnsureMatchesFromSearchResult(bool isFromInstalledSource) : - WorkflowTask("EnsureMatchesFromSearchResult"), m_isFromInstalledSource(isFromInstalledSource) {} + EnsureMatchesFromSearchResult(OperationType operation) : + WorkflowTask("EnsureMatchesFromSearchResult"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: - bool m_isFromInstalledSource; + OperationType m_operationType; }; // Ensures that there is only one result in the search. @@ -231,13 +244,13 @@ namespace AppInstaller::CLI::Workflow // Outputs: Package struct EnsureOneMatchFromSearchResult : public WorkflowTask { - EnsureOneMatchFromSearchResult(bool isFromInstalledSource) : - WorkflowTask("EnsureOneMatchFromSearchResult"), m_isFromInstalledSource(isFromInstalledSource) {} + EnsureOneMatchFromSearchResult(OperationType operation) : + WorkflowTask("EnsureOneMatchFromSearchResult"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: - bool m_isFromInstalledSource; + OperationType m_operationType; }; // Gets the manifest from package. @@ -339,21 +352,6 @@ namespace AppInstaller::CLI::Workflow Execution::Reporter::Level m_level; }; - // Composite flow that produces a manifest; either from one given on the command line or by searching. - // Required Args: None - // Inputs: None - // Outputs: Manifest - struct GetManifest : public WorkflowTask - { - GetManifest(bool considerPins) : WorkflowTask("GetManifest"), m_considerPins(considerPins) {} - - void operator()(Execution::Context& context) const override; - - private: - bool m_considerPins; - }; - - // Selects the installer from the manifest, if one is applicable. // Required Args: None // Inputs: Manifest diff --git a/src/AppInstallerCLIE2ETests/InstallCommand.cs b/src/AppInstallerCLIE2ETests/InstallCommand.cs @@ -588,7 +588,7 @@ namespace AppInstallerCLIE2ETests var upgradeResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {upgradeDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_UPDATE_NOT_APPLICABLE, upgradeResult.ExitCode); Assert.True(upgradeResult.StdOut.Contains("Trying to upgrade the installed package...")); - Assert.True(upgradeResult.StdOut.Contains("No applicable upgrade")); + Assert.True(upgradeResult.StdOut.Contains("No available upgrade")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(baseDir)); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -701,6 +701,15 @@ They can be configured through the settings file 'winget settings'.</value> <data name="UpdateNotApplicable" xml:space="preserve"> <value>No applicable upgrade found.</value> </data> + <data name="UpdateNotApplicableReason" xml:space="preserve"> + <value>A newer package version is available in a configured source, but it does not apply to your system or requirements.</value> + </data> + <data name="UpdateNoPackagesFound" xml:space="preserve"> + <value>No available upgrade found.</value> + </data> + <data name="UpdateNoPackagesFoundReason" xml:space="preserve"> + <value>No newer package versions are available from the configured sources.</value> + </data> <data name="UpgradeCommandLongDescription" xml:space="preserve"> <value>Upgrades the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. When no arguments are given, shows the packages with upgrades available</value> <comment>id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case.</comment> diff --git a/src/AppInstallerCLITests/UpdateFlow.cpp b/src/AppInstallerCLITests/UpdateFlow.cpp @@ -99,7 +99,8 @@ TEST_CASE("UpdateFlow_UpdateWithManifestVersionAlreadyInstalled", "[UpdateFlow][ // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); - REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNotApplicable).get()) != std::string::npos); + REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos); + REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } @@ -403,7 +404,8 @@ TEST_CASE("UpdateFlow_UpdateExeLatestAlreadyInstalled", "[UpdateFlow][workflow]" // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); - REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNotApplicable).get()) != std::string::npos); + REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos); + REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } @@ -499,7 +501,8 @@ TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotApplicable", "[UpdateFlow][work auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_IncompatibleInstallerType })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_IncompatibleInstallerType.Query); - context.Args.AddArg(Execution::Args::Type::Version, "1.0.0.0"sv); + // This must be 2.0.0.0 since the version would not be an upgrade otherwise + context.Args.AddArg(Execution::Args::Type::Version, "2.0.0.0"sv); UpgradeCommand update({}); update.Execute(context); @@ -507,7 +510,7 @@ TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotApplicable", "[UpdateFlow][work // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); - REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNotApplicable).get()) != std::string::npos); + REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnology).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } @@ -906,7 +909,8 @@ TEST_CASE("InstallFlow_FoundInstalledAndUpgradeNotAvailable", "[UpdateFlow][work // Verify Installer is not called. REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNotApplicable).get()) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); }