winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 9dcbf99b2831af395fc996917da7dfe8fc085ffc
parent 8650938fa43524e009013f5d3255c52206732bad
Author: Chacón <lechacon@users.noreply.github.com>
Date:   Wed, 19 Jan 2022 18:32:45 -0800

Allow upgrades in packages that register a different installer type (#1796)

* Modified the applicability check in `InstalledTypeComparator` to also consider the installer types listed under `AppsAndFeaturesEntries`
* Extended string returned from `ExplainInapplicable` to also mention the installed type and other types accepted by the manifest.

Closes #1242. Validated that upgrading PowerToys (mentioned in that issue) worked correctly using a local manifest edited to include the `AppsAndFeaturesEntries` and installed type `msi`.

Diffstat:
Msrc/AppInstallerCLICore/Workflows/ManifestComparator.cpp | 26++++++++++++++++++++++++--
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 3+++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 3+++
Asrc/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_ARPInstallerType.yaml | 22++++++++++++++++++++++
Msrc/AppInstallerCLITests/WorkFlow.cpp | 36++++++++++++++++++++++++++++++++++++
5 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp @@ -210,18 +210,40 @@ namespace AppInstaller::CLI::Workflow InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override { + // The installer is applicable if it's type or any of its ARP entries' type matches the installed type if (Manifest::IsInstallerTypeCompatible(installer.InstallerType, m_installedType)) { return InapplicabilityFlags::None; } + auto itr = std::find_if( + installer.AppsAndFeaturesEntries.begin(), + installer.AppsAndFeaturesEntries.end(), + [=](AppsAndFeaturesEntry arpEntry) { return Manifest::IsInstallerTypeCompatible(arpEntry.InstallerType, m_installedType); }); + if (itr != installer.AppsAndFeaturesEntries.end()) + { + return InapplicabilityFlags::None; + } + return InapplicabilityFlags::InstalledType; } std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override { - std::string result = "Installed package type is not compatible with "; - result += Manifest::InstallerTypeToString(installer.InstallerType); + std::string result = "Installed package type '" + std::string{ Manifest::InstallerTypeToString(m_installedType) } + + "' is not compatible with installer type " + std::string{ Manifest::InstallerTypeToString(installer.InstallerType) }; + + std::string arpInstallerTypes; + for (const auto& entry : installer.AppsAndFeaturesEntries) + { + arpInstallerTypes += " " + std::string{ Manifest::InstallerTypeToString(entry.InstallerType) }; + } + + if (!arpInstallerTypes.empty()) + { + result += ", or with accepted type(s)" + arpInstallerTypes; + } + return result; } diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -515,6 +515,9 @@ <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_ARPInstallerType.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -435,6 +435,9 @@ <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_ARPInstallerType.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> diff --git a/src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_ARPInstallerType.yaml b/src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_ARPInstallerType.yaml @@ -0,0 +1,22 @@ +# Similar content to UpdateFlowTest_Exe.yaml, but with an AppsAndFeaturesEntry specifying installer type +PackageIdentifier: AppInstallerCliTest.TestExeInstaller +PackageVersion: 2.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +AppsAndFeaturesEntries: + - InstallerType: msix +InstallerSwitches: + Custom: /custom /ver2.0.0.0 + SilentWithProgress: /silentwithprogress + Silent: /silence + Update: /update +Installers: + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B +ManifestType: singleton +ManifestVersion: 1.1.0 diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -210,6 +210,21 @@ namespace PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } + if (input == "TestExeInstallerWithDifferentInstalledType") + { + auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); + auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_ARPInstallerType.yaml")); + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + manifest, + TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, + std::vector<Manifest>{ manifest2, manifest }, + shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); + } + if (input == "TestExeInstallerWithNothingInstalled") { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); @@ -1495,6 +1510,27 @@ TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicableSpecificVersion", "[Upd REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } +TEST_CASE("UpdateFlow_UpdateExeWithDifferentInstalledType", "[UpdateFlow][workflow]") +{ + // Tests installer applicability when installed type is different but listed in the manifest + TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); + + std::ostringstream updateOutput; + TestContext context{ updateOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForCompositeInstalledSource(context); + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::Query, "TestExeInstallerWithDifferentInstalledType"sv); + + UpgradeCommand update({}); + update.Execute(context); + INFO(updateOutput.str()); + + // Verify Installer is called. + REQUIRE(context.GetTerminationHR() == S_OK); + REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); +} + TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotFound", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt");