From 9c72b02d42544f23e51ac290634ab2a3eb0b2cc1 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 4 Jun 2021 18:16:41 -0300 Subject: [PATCH 01/82] first tests for show depenencies feature --- .../AppInstallerCLITests.vcxproj | 17 +++ .../AppInstallerCLITests.vcxproj.filters | 5 + .../ImportFile-Good-Dependencies.json | 25 ++++ .../Installer_Exe_AllDependencyTypes.yaml | 29 +++++ .../Installer_Exe_PackageDependency.yaml | 22 ++++ .../TestData/Installer_Msi_WFDependency.yaml | 19 +++ .../Manifest-Good-AllDependencyTypes.yaml | 28 +++++ src/AppInstallerCLITests/WorkFlow.cpp | 116 ++++++++++++++++++ 8 files changed, 261 insertions(+) create mode 100644 src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json create mode 100644 src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml create mode 100644 src/AppInstallerCLITests/TestData/Installer_Exe_PackageDependency.yaml create mode 100644 src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml create mode 100644 src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index e9b308f106..b91c95e1ca 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -511,6 +511,23 @@ true + + true + Document + + + true + Document + + + true + Document + + + + true + Document + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 4a61cf30ce..5a21e56e4c 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -169,6 +169,7 @@ + @@ -450,5 +451,9 @@ TestData + + + + \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json new file mode 100644 index 0000000000..30383e6b8a --- /dev/null +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://aka.ms/winget-packages.schema.1.0.json", + "CreationDate": "2021-01-01T12:00:00.000", + "Sources": [ + { + "Packages": [ + { + "Id": "AppInstallerCliTest.TestExeInstaller.PackageDep", + "Version": "2.0.0.0" + }, + { + "Id": "AppInstallerCliTest.TestMsixInstaller.WFDep", + "Version": "2.0.0.0" + } + ], + "SourceDetails": { + "Argument": "//arg", + "Identifier": "*TestSource", + "Name": "TestSource", + "Type": "Microsoft.TestSource" + } + } + ], + "WinGetVersion": "1.0.0" +} \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml b/src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml new file mode 100644 index 0000000000..b0128756d6 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml @@ -0,0 +1,29 @@ +PackageIdentifier: AppInstallerCliTest.TestExeInstaller.PackageDep +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Exe Installer With Package Dep +Publisher: Microsoft Corporation +Moniker: AICLITestExePackageDep +License: Test +ShortDescription: AppInstaller Test Exe Installer With Package Dep +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + InstallerSwitches: + SilentWithProgress: /silentwithprogress + Silent: /silence + Dependencies: + WindowsFeatures: + - WindowsFeaturesDep + WindowsLibraries: + - WindowsLibrariesDep + PackageDependencies: + - PackageIdentifier: Package.Dep1-x64 + MinimumVersion: 1.0 + - PackageIdentifier: Package.Dep2-x64 + ExternalDependencies: + - ExternalDep +ManifestType: singleton +ManifestVersion: 1.0.0 diff --git a/src/AppInstallerCLITests/TestData/Installer_Exe_PackageDependency.yaml b/src/AppInstallerCLITests/TestData/Installer_Exe_PackageDependency.yaml new file mode 100644 index 0000000000..4c294f33f2 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Installer_Exe_PackageDependency.yaml @@ -0,0 +1,22 @@ +PackageIdentifier: AppInstallerCliTest.TestExeInstaller.PackageDep +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Exe Installer With Package Dep +Publisher: Microsoft Corporation +Moniker: AICLITestExePackageDep +License: Test +ShortDescription: AppInstaller Test Exe Installer With Package Dep +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + InstallerSwitches: + SilentWithProgress: /silentwithprogress + Silent: /silence + Dependencies: + PackageDependencies: + - PackageIdentifier: Package.Dep1-x64 + MinimumVersion: 1.0 +ManifestType: singleton +ManifestVersion: 1.0.0 diff --git a/src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml b/src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml new file mode 100644 index 0000000000..38731764c7 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml @@ -0,0 +1,19 @@ +PackageIdentifier: AppInstallerCliTest.TestMsixInstaller.WFDep +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test MSIX Installer With Windows Feature Dep +Publisher: Microsoft Corporation +Moniker: AICLITestMsixWindowsFeatureDep +License: Test +ShortDescription: AppInstaller Test MSIX Installer With Windows Feature Dep +Installers: + - Architecture: x64 + InstallerUrl: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true + InstallerType: msix + InstallerSha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea + PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe + Dependencies: + WindowsFeatures: + - WindowsFeaturesDep +ManifestType: singleton +ManifestVersion: 1.0.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml new file mode 100644 index 0000000000..334441ac10 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml @@ -0,0 +1,28 @@ +# Installer with all types of dependencies +PackageIdentifier: AppInstallerCliTest.TestMsixInstaller +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test MSIX Installer +ShortDescription: AppInstaller Test MSIX Installer +Publisher: Microsoft Corporation +Moniker: AICLITestMsix +License: Test +Installers: + - Architecture: x64 + InstallerUrl: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true + InstallerType: msix + InstallerSha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea + PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe + Dependencies: + WindowsFeatures: + - WindowsFeaturesDep + WindowsLibraries: + - WindowsLibrariesDep + PackageDependencies: + - PackageIdentifier: Package.Dep1-x64 + MinimumVersion: 1.0 + - PackageIdentifier: Package.Dep2-x64 + ExternalDependencies: + - ExternalDep +ManifestType: singleton +ManifestVersion: 1.0.0 diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 2e99614cf3..77c8ba3ad4 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -129,6 +129,25 @@ namespace PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } + if (input.empty() || input == "AppInstallerCliTest.TestExeInstaller.PackageDep") + { + auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_PackageDependency.yaml")); + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + manifest, + TestPackage::MetadataMap + { + { PackageVersionMetadata::InstalledType, "Exe" }, + { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, + { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, + }, + std::vector{ manifest }, + this->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller.PackageDep"))); + } + if (input.empty() || input == "AppInstallerCliTest.TestMsixInstaller") { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Msix_StreamingFlow.yaml")); @@ -144,6 +163,20 @@ namespace PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller"))); } + if (input.empty() || input == "AppInstallerCliTest.TestMsixInstaller.WFDep") + { + auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Msi_WFDependency.yaml")); + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + manifest, + TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, + std::vector{ manifest }, + this->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestInstaller.WFDep"))); + } + if (input.empty() || input == "AppInstallerCliTest.TestMSStoreInstaller") { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_MSStore.yaml")); @@ -782,6 +815,27 @@ TEST_CASE("ShowFlow_SearchAndShowAppVersion", "[ShowFlow][workflow]") REQUIRE(showOutput.str().find(" Download Url: https://ThisIsNotUsed") == std::string::npos); } +TEST_CASE("ShowFlow_ShowDependencies", "[ShowFlow][workflow]") +{ + std::ostringstream showOutput; + TestContext context{ showOutput, std::cin }; + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); + + ShowCommand show({}); + show.Execute(context); + INFO(showOutput.str()); + + // Verify all types of dependencies are printed + REQUIRE(showOutput.str().find("Dependencies") != std::string::npos); + REQUIRE(showOutput.str().find("WindowsFeaturesDep") != std::string::npos); + REQUIRE(showOutput.str().find("WindowsLibrariesDep") != std::string::npos); + // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) + REQUIRE(showOutput.str().find("Package.Dep1-x64\x1b[0m [>= 1.0]") != std::string::npos); + REQUIRE(showOutput.str().find("Package.Dep2-x64") != std::string::npos); + REQUIRE(showOutput.str().find("Package.Dep2-x64\x1b[0m [") == std::string::npos); + REQUIRE(showOutput.str().find("ExternalDep") != std::string::npos); +} + TEST_CASE("UpdateFlow_UpdateWithManifest", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); @@ -1381,6 +1435,34 @@ TEST_CASE("ImportFlow_MachineScope", "[ImportFlow][workflow]") REQUIRE(installResultStr.find("/scope=machine") != std::string::npos); } +TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow]") +{ + TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); + TestCommon::TempFile msixInstallResultPath("TestMsixInstalled.txt"); + + std::ostringstream importOutput; + TestContext context{ importOutput, std::cin }; + OverrideForImportSource(context); + OverrideForMSIX(context); + OverrideForShellExecute(context); + + //context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good.json").GetPath().string()); + context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-Dependencies.json").GetPath().string()); + + ImportCommand importCommand({}); + importCommand.Execute(context); + INFO(importOutput.str()); + + // Verify all packages were installed + // Since we only show a dependency warning and then continue with the instalation + REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); + REQUIRE(std::filesystem::exists(msixInstallResultPath.GetPath())); + + // Verify dependencies for all packages are informed + REQUIRE(importOutput.str().find("WindowsFeaturesDep") != std::string::npos); + REQUIRE(importOutput.str().find("PackageDep\x1b[0m [>= 1.0]") != std::string::npos); +} + void VerifyMotw(const std::filesystem::path& testFile, DWORD zone) { std::filesystem::path motwFile(testFile); @@ -1520,4 +1602,38 @@ TEST_CASE("InstallFlowMultiLocale_PreferenceWithBetterLocale", "[InstallFlow][wo std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/en-GB") != std::string::npos); +} + +TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_AllDependencyTypes.yaml").GetPath().u8string()); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + // For now verify Installer is called and parameters are passed in. + // Since we only show a dependency warning and then continue with the instalation + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + std::ifstream installResultFile(installResultPath.GetPath()); + REQUIRE(installResultFile.is_open()); + std::string installResultStr; + std::getline(installResultFile, installResultStr); + REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); + + // Verify all types of dependencies are printed + REQUIRE(installOutput.str().find("[Warning] The installer has the following dependencies") != std::string::npos); + REQUIRE(installOutput.str().find("WindowsFeaturesDep") != std::string::npos); + REQUIRE(installOutput.str().find("WindowsLibrariesDep") != std::string::npos); + // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) + REQUIRE(installOutput.str().find("Package.Dep1-x64\x1b[0m [>= 1.0]") != std::string::npos); + REQUIRE(installOutput.str().find("Package.Dep2-x64") != std::string::npos); + REQUIRE(installOutput.str().find("Package.Dep2-x64\x1b[0m [") == std::string::npos); + REQUIRE(installOutput.str().find("ExternalDep") != std::string::npos); } \ No newline at end of file From 0a81ec5f5b519fe7ccece5e03e730572dd4a391e Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 4 Jun 2021 18:21:47 -0300 Subject: [PATCH 02/82] show dependencies feature for show, install, need to change message --- .../Workflows/InstallFlow.cpp | 43 +++++++++++++ .../Workflows/InstallFlow.h | 6 ++ .../Workflows/ShowFlow.cpp | 62 ++++++++++++++----- .../Public/winget/ManifestCommon.h | 5 ++ 4 files changed, 102 insertions(+), 14 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 53a6a4279d..771dddb8d8 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -392,6 +392,7 @@ namespace AppInstaller::CLI::Workflow context << Workflow::ReportManifestIdentity << Workflow::ShowInstallationDisclaimer << + Workflow::ReportDependencies << Workflow::ReportExecutionStage(ExecutionStage::Download) << Workflow::DownloadInstaller << Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << @@ -408,9 +409,51 @@ namespace AppInstaller::CLI::Workflow context << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << + Workflow::ReportDependencies << Workflow::InstallPackageInstaller; } + void ReportDependencies(Execution::Context& context) + { + const auto& installer = context.Get(); + if (installer) + { + auto dependencies = installer->Dependencies; + if (dependencies.HasAny()) + { + context.Reporter.Info() << " [Warning] The installer has the following dependencies: " << std::endl; + + auto windowsFeaturesDep = dependencies.WindowsFeatures; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << " WindowsFeatures: " << windowsFeaturesDep[i] << std::endl; + } + + auto windowsLibrariesDep = dependencies.WindowsLibraries; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << " WindowsLibraries: " << windowsLibrariesDep[i] << std::endl; + } + + auto packageDep = dependencies.PackageDependencies; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << " PackageDependency: " << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) { + context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + } + context.Reporter.Info() << std::endl; + } + + auto externalDependenciesDep = dependencies.ExternalDependencies; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << " ExternalDependencies: " << externalDependenciesDep[i] << std::endl; + } + } + } + } + void InstallMultiple(Execution::Context& context) { bool allSucceeded = true; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 30837e856c..5220bca46b 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -89,6 +89,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void InstallPackageVersion(Execution::Context& context); + // Shows information about dependencies. + // Required Args: None + // Inputs: Manifest + // Outputs: None + void ReportDependencies(Execution::Context& context); + // Installs multiple packages. // Required Args: None // Inputs: Manifests diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index c6da146fd0..a2e5ed6e1e 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -16,16 +16,16 @@ namespace AppInstaller::CLI::Workflow const auto& installer = context.Get(); // TODO: Come up with a prettier format - context.Reporter.Info() << "Version: " << manifest.Version << std::endl; - context.Reporter.Info() << "Publisher: " << manifest.CurrentLocalization.Get() << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "PackageVersion: " << manifest.Version << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "Publisher: " << manifest.CurrentLocalization.Get() << std::endl; auto author = manifest.CurrentLocalization.Get(); if (!author.empty()) { - context.Reporter.Info() << "Author: " << author << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "Author: " << author << std::endl; } if (!manifest.Moniker.empty()) { - context.Reporter.Info() << "Moniker: " << manifest.Moniker << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "Moniker: " << manifest.Moniker << std::endl; } auto description = manifest.CurrentLocalization.Get(); if (description.empty()) @@ -35,39 +35,73 @@ namespace AppInstaller::CLI::Workflow } if (!description.empty()) { - context.Reporter.Info() << "Description: " << description << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "Description: " << description << std::endl; } auto homepage = manifest.CurrentLocalization.Get(); if (!homepage.empty()) { - context.Reporter.Info() << "Homepage: " << homepage << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "PackageUrl: " << homepage << std::endl; } - context.Reporter.Info() << "License: " << manifest.CurrentLocalization.Get() << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "License: " << manifest.CurrentLocalization.Get() << std::endl; auto licenseUrl = manifest.CurrentLocalization.Get(); if (!licenseUrl.empty()) { - context.Reporter.Info() << "License Url: " << licenseUrl << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "LicenseUrl: " << licenseUrl << std::endl; } - context.Reporter.Info() << "Installer:" << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << "Installer:" << std::endl; if (installer) { - context.Reporter.Info() << " Type: " << Manifest::InstallerTypeToString(installer->InstallerType) << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << " InstallerType: " << Manifest::InstallerTypeToString(installer->InstallerType) << std::endl; if (!installer->Locale.empty()) { - context.Reporter.Info() << " Locale: " << installer->Locale << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << " InstallerLocale: " << installer->Locale << std::endl; } if (!installer->Url.empty()) { - context.Reporter.Info() << " Download Url: " << installer->Url << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << " InstallerUrl: " << installer->Url << std::endl; } if (!installer->Sha256.empty()) { - context.Reporter.Info() << " SHA256: " << Utility::SHA256::ConvertToString(installer->Sha256) << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << " InstallerSha256: " << Utility::SHA256::ConvertToString(installer->Sha256) << std::endl; } if (!installer->ProductId.empty()) { - context.Reporter.Info() << " Store Product Id: " << installer->ProductId << std::endl; + context.Reporter.Info() << Execution::ManifestInfoEmphasis << " ProductId: " << installer->ProductId << std::endl; + } + + auto dependencies = installer->Dependencies; + if (dependencies.HasAny()) + { + context.Reporter.Info() << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; + + auto windowsFeaturesDep = dependencies.WindowsFeatures; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << " WindowsFeatures: " << windowsFeaturesDep[i] << std::endl; + } + + auto windowsLibrariesDep = dependencies.WindowsLibraries; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << " WindowsLibraries: " << windowsLibrariesDep[i] << std::endl; + } + + auto packageDep = dependencies.PackageDependencies; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << " PackageDependency: " << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) { + context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + } + context.Reporter.Info() << std::endl; + } + + auto externalDependenciesDep = dependencies.ExternalDependencies; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << " ExternalDependencies: " << externalDependenciesDep[i] << std::endl; + } } } else diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 54d3993ac4..3701115c49 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -124,6 +124,11 @@ namespace AppInstaller::Manifest std::vector WindowsLibraries; std::vector PackageDependencies; std::vector ExternalDependencies; + + bool HasAny() const { + return !WindowsFeatures.empty() || !WindowsLibraries.empty() || !PackageDependencies.empty() || + !ExternalDependencies.empty(); + } }; From b416985f9ce0f0809943380f9b64bb7f558da4be Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 9 Jun 2021 16:22:11 -0300 Subject: [PATCH 03/82] showflow output changed --- src/AppInstallerCLICore/ExecutionReporter.cpp | 1 + src/AppInstallerCLICore/ExecutionReporter.h | 1 + .../Workflows/InstallFlow.cpp | 10 ++--- .../Workflows/ShowFlow.cpp | 41 ++++++++++++++----- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/AppInstallerCLICore/ExecutionReporter.cpp b/src/AppInstallerCLICore/ExecutionReporter.cpp index 92de8cb67a..7e0421a67c 100644 --- a/src/AppInstallerCLICore/ExecutionReporter.cpp +++ b/src/AppInstallerCLICore/ExecutionReporter.cpp @@ -11,6 +11,7 @@ namespace AppInstaller::CLI::Execution const Sequence& HelpCommandEmphasis = TextFormat::Foreground::Bright; const Sequence& HelpArgumentEmphasis = TextFormat::Foreground::Bright; + const Sequence& ManifestInfoEmphasis = TextFormat::Foreground::Bright; const Sequence& NameEmphasis = TextFormat::Foreground::BrightCyan; const Sequence& IdEmphasis = TextFormat::Foreground::BrightCyan; const Sequence& UrlEmphasis = TextFormat::Foreground::BrightBlue; diff --git a/src/AppInstallerCLICore/ExecutionReporter.h b/src/AppInstallerCLICore/ExecutionReporter.h index a6446b31a6..08fba5de25 100644 --- a/src/AppInstallerCLICore/ExecutionReporter.h +++ b/src/AppInstallerCLICore/ExecutionReporter.h @@ -142,6 +142,7 @@ namespace AppInstaller::CLI::Execution // Indirection to enable change without tracking down every place extern const VirtualTerminal::Sequence& HelpCommandEmphasis; extern const VirtualTerminal::Sequence& HelpArgumentEmphasis; + extern const VirtualTerminal::Sequence& ManifestInfoEmphasis; extern const VirtualTerminal::Sequence& NameEmphasis; extern const VirtualTerminal::Sequence& IdEmphasis; extern const VirtualTerminal::Sequence& UrlEmphasis; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 771dddb8d8..b77b60de8d 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -421,24 +421,24 @@ namespace AppInstaller::CLI::Workflow auto dependencies = installer->Dependencies; if (dependencies.HasAny()) { - context.Reporter.Info() << " [Warning] The installer has the following dependencies: " << std::endl; + context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; auto windowsFeaturesDep = dependencies.WindowsFeatures; for (size_t i = 0; i < windowsFeaturesDep.size(); i++) { - context.Reporter.Info() << " WindowsFeatures: " << windowsFeaturesDep[i] << std::endl; + context.Reporter.Info() << " - WindowsFeatures: " << windowsFeaturesDep[i] << std::endl; } auto windowsLibrariesDep = dependencies.WindowsLibraries; for (size_t i = 0; i < windowsLibrariesDep.size(); i++) { - context.Reporter.Info() << " WindowsLibraries: " << windowsLibrariesDep[i] << std::endl; + context.Reporter.Info() << " - WindowsLibraries: " << windowsLibrariesDep[i] << std::endl; } auto packageDep = dependencies.PackageDependencies; for (size_t i = 0; i < packageDep.size(); i++) { - context.Reporter.Info() << " PackageDependency: " << packageDep[i].Id; + context.Reporter.Info() << " - PackageDependency: " << packageDep[i].Id; if (!packageDep[i].MinVersion.empty()) { context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; } @@ -448,7 +448,7 @@ namespace AppInstaller::CLI::Workflow auto externalDependenciesDep = dependencies.ExternalDependencies; for (size_t i = 0; i < externalDependenciesDep.size(); i++) { - context.Reporter.Info() << " ExternalDependencies: " << externalDependenciesDep[i] << std::endl; + context.Reporter.Info() << " - ExternalDependencies: " << externalDependenciesDep[i] << std::endl; } } } diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index a2e5ed6e1e..91df20f3cf 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -76,31 +76,52 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; auto windowsFeaturesDep = dependencies.WindowsFeatures; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + if (!windowsFeaturesDep.empty()) { - context.Reporter.Info() << " WindowsFeatures: " << windowsFeaturesDep[i] << std::endl; + context.Reporter.Info() << " - Windows Features: "; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << windowsFeaturesDep[i]; + if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } auto windowsLibrariesDep = dependencies.WindowsLibraries; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + if (!windowsLibrariesDep.empty()) { - context.Reporter.Info() << " WindowsLibraries: " << windowsLibrariesDep[i] << std::endl; + context.Reporter.Info() << " - Windows Libraries: "; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << windowsLibrariesDep[i]; + if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } auto packageDep = dependencies.PackageDependencies; - for (size_t i = 0; i < packageDep.size(); i++) + if (!packageDep.empty()) { - context.Reporter.Info() << " PackageDependency: " << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) { - context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + context.Reporter.Info() << " - Packages: "; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; } context.Reporter.Info() << std::endl; } auto externalDependenciesDep = dependencies.ExternalDependencies; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) + if (!externalDependenciesDep.empty()) { - context.Reporter.Info() << " ExternalDependencies: " << externalDependenciesDep[i] << std::endl; + context.Reporter.Info() << " - Externals: "; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << externalDependenciesDep[i]; + if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } } } From c8488242d809ae2dd7afd3a8b068cc1c92330101 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 10 Jun 2021 16:36:49 -0300 Subject: [PATCH 04/82] install commands shows dependencies as in specs --- .../Workflows/InstallFlow.cpp | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index b77b60de8d..63a0e5f094 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -409,7 +409,6 @@ namespace AppInstaller::CLI::Workflow context << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << - Workflow::ReportDependencies << Workflow::InstallPackageInstaller; } @@ -424,31 +423,52 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; auto windowsFeaturesDep = dependencies.WindowsFeatures; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + if (!windowsFeaturesDep.empty()) { - context.Reporter.Info() << " - WindowsFeatures: " << windowsFeaturesDep[i] << std::endl; + context.Reporter.Info() << " - Windows Features: "; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << windowsFeaturesDep[i]; + if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } auto windowsLibrariesDep = dependencies.WindowsLibraries; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + if (!windowsLibrariesDep.empty()) { - context.Reporter.Info() << " - WindowsLibraries: " << windowsLibrariesDep[i] << std::endl; + context.Reporter.Info() << " - Windows Libraries: "; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << windowsLibrariesDep[i]; + if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } auto packageDep = dependencies.PackageDependencies; - for (size_t i = 0; i < packageDep.size(); i++) + if (!packageDep.empty()) { - context.Reporter.Info() << " - PackageDependency: " << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) { - context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + context.Reporter.Info() << " - Packages: "; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; } context.Reporter.Info() << std::endl; } auto externalDependenciesDep = dependencies.ExternalDependencies; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) + if (!externalDependenciesDep.empty()) { - context.Reporter.Info() << " - ExternalDependencies: " << externalDependenciesDep[i] << std::endl; + context.Reporter.Info() << " - Externals: "; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << externalDependenciesDep[i]; + if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } } } From 688cbe54e19f32c397aaa00c67efd9a58cb47e17 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 10 Jun 2021 16:53:22 -0300 Subject: [PATCH 05/82] tests for informing dependencies on commands: show, install, upgrade, import --- .../AppInstallerCLITests.vcxproj | 9 +- .../AppInstallerCLITests.vcxproj.filters | 6 +- .../ImportFile-Good-Dependencies.json | 4 +- .../Installer_Exe_AllDependencyTypes.yaml | 29 ---- ...y.yaml => Installer_Exe_Dependencies.yaml} | 7 +- .../TestData/Installer_Msi_WFDependency.yaml | 2 +- .../Manifest-Good-AllDependencyTypes.yaml | 3 +- .../UpdateFlowTest_ExeDependencies.yaml | 26 ++++ src/AppInstallerCLITests/WorkFlow.cpp | 127 +++++++++--------- 9 files changed, 107 insertions(+), 106 deletions(-) delete mode 100644 src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml rename src/AppInstallerCLITests/TestData/{Installer_Exe_PackageDependency.yaml => Installer_Exe_Dependencies.yaml} (75%) create mode 100644 src/AppInstallerCLITests/TestData/UpdateFlowTest_ExeDependencies.yaml diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index b91c95e1ca..cb35bdc9fd 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -519,12 +519,15 @@ true Document - + true Document - - + + true + Document + + true Document diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 5a21e56e4c..0eaf55980f 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -169,7 +169,6 @@ - @@ -453,7 +452,8 @@ - - + + + \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json index 30383e6b8a..caac43c1d3 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json @@ -5,12 +5,12 @@ { "Packages": [ { - "Id": "AppInstallerCliTest.TestExeInstaller.PackageDep", + "Id": "AppInstallerCliTest.TestExeInstaller.Dependencies", "Version": "2.0.0.0" }, { "Id": "AppInstallerCliTest.TestMsixInstaller.WFDep", - "Version": "2.0.0.0" + "Version": "1.0.0.0" } ], "SourceDetails": { diff --git a/src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml b/src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml deleted file mode 100644 index b0128756d6..0000000000 --- a/src/AppInstallerCLITests/TestData/Installer_Exe_AllDependencyTypes.yaml +++ /dev/null @@ -1,29 +0,0 @@ -PackageIdentifier: AppInstallerCliTest.TestExeInstaller.PackageDep -PackageVersion: 1.0.0.0 -PackageLocale: en-US -PackageName: AppInstaller Test Exe Installer With Package Dep -Publisher: Microsoft Corporation -Moniker: AICLITestExePackageDep -License: Test -ShortDescription: AppInstaller Test Exe Installer With Package Dep -Installers: - - Architecture: x64 - InstallerUrl: https://ThisIsNotUsed - InstallerType: exe - InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B - InstallerSwitches: - SilentWithProgress: /silentwithprogress - Silent: /silence - Dependencies: - WindowsFeatures: - - WindowsFeaturesDep - WindowsLibraries: - - WindowsLibrariesDep - PackageDependencies: - - PackageIdentifier: Package.Dep1-x64 - MinimumVersion: 1.0 - - PackageIdentifier: Package.Dep2-x64 - ExternalDependencies: - - ExternalDep -ManifestType: singleton -ManifestVersion: 1.0.0 diff --git a/src/AppInstallerCLITests/TestData/Installer_Exe_PackageDependency.yaml b/src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml similarity index 75% rename from src/AppInstallerCLITests/TestData/Installer_Exe_PackageDependency.yaml rename to src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml index 4c294f33f2..9d65242fa5 100644 --- a/src/AppInstallerCLITests/TestData/Installer_Exe_PackageDependency.yaml +++ b/src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml @@ -1,4 +1,4 @@ -PackageIdentifier: AppInstallerCliTest.TestExeInstaller.PackageDep +PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Exe Installer With Package Dep @@ -15,8 +15,7 @@ Installers: SilentWithProgress: /silentwithprogress Silent: /silence Dependencies: - PackageDependencies: - - PackageIdentifier: Package.Dep1-x64 - MinimumVersion: 1.0 + WindowsFeatures: + - PreviewIIS ManifestType: singleton ManifestVersion: 1.0.0 diff --git a/src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml b/src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml index 38731764c7..acd8ea69df 100644 --- a/src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml +++ b/src/AppInstallerCLITests/TestData/Installer_Msi_WFDependency.yaml @@ -14,6 +14,6 @@ Installers: PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe Dependencies: WindowsFeatures: - - WindowsFeaturesDep + - Hyper-V ManifestType: singleton ManifestVersion: 1.0.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml index 334441ac10..06cc453fb4 100644 --- a/src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml +++ b/src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml @@ -15,7 +15,8 @@ Installers: PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe Dependencies: WindowsFeatures: - - WindowsFeaturesDep + - WindowsFeaturesDep1 + - WindowsFeaturesDep2 WindowsLibraries: - WindowsLibrariesDep PackageDependencies: diff --git a/src/AppInstallerCLITests/TestData/UpdateFlowTest_ExeDependencies.yaml b/src/AppInstallerCLITests/TestData/UpdateFlowTest_ExeDependencies.yaml new file mode 100644 index 0000000000..25b55a6950 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/UpdateFlowTest_ExeDependencies.yaml @@ -0,0 +1,26 @@ +# Same content with Installer_Exe_Dependencies but with higher version +PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies +PackageVersion: 2.0.0.0 +PackageName: AppInstaller Test Installer +PackageLocale: en-US +Publisher: Microsoft Corporation +ShortDescription: Upgrade exe installer with dependencies +Moniker: AICLITestExe +License: Test +InstallerSwitches: + Custom: /custom + SilentWithProgress: /silentwithprogress + Silent: /silence + Upgrade: /upgrade +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + Dependencies: + WindowsFeatures: + - PreviewIIS + WindowsLibraries: + - Preview VC Runtime +ManifestType: singleton +ManifestVersion: 1.0.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 77c8ba3ad4..3b0f538b10 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -129,25 +129,6 @@ namespace PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } - if (input.empty() || input == "AppInstallerCliTest.TestExeInstaller.PackageDep") - { - auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_PackageDependency.yaml")); - result.Matches.emplace_back( - ResultMatch( - TestPackage::Make( - manifest, - TestPackage::MetadataMap - { - { PackageVersionMetadata::InstalledType, "Exe" }, - { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, - { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, - }, - std::vector{ manifest }, - this->shared_from_this() - ), - PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller.PackageDep"))); - } - if (input.empty() || input == "AppInstallerCliTest.TestMsixInstaller") { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Msix_StreamingFlow.yaml")); @@ -163,20 +144,6 @@ namespace PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller"))); } - if (input.empty() || input == "AppInstallerCliTest.TestMsixInstaller.WFDep") - { - auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Msi_WFDependency.yaml")); - result.Matches.emplace_back( - ResultMatch( - TestPackage::Make( - manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, - std::vector{ manifest }, - this->shared_from_this() - ), - PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestInstaller.WFDep"))); - } - if (input.empty() || input == "AppInstallerCliTest.TestMSStoreInstaller") { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_MSStore.yaml")); @@ -233,6 +200,38 @@ namespace PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } + if (input == "AppInstallerCliTest.TestExeInstaller.Dependencies") + { + auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_Dependencies.yaml")); + auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_ExeDependencies.yaml")); + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + manifest, + TestPackage::MetadataMap + { + { PackageVersionMetadata::InstalledType, "Exe" }, + { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, + { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, + }, + std::vector{ manifest2, manifest }, + this->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller.Dependencies"))); + } + + if (input == "AppInstallerCliTest.TestMsixInstaller.WFDep") + { + auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Msi_WFDependency.yaml")); + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + std::vector{ manifest }, + this->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller.WFDep"))); + } + return result; } }; @@ -815,7 +814,7 @@ TEST_CASE("ShowFlow_SearchAndShowAppVersion", "[ShowFlow][workflow]") REQUIRE(showOutput.str().find(" Download Url: https://ThisIsNotUsed") == std::string::npos); } -TEST_CASE("ShowFlow_ShowDependencies", "[ShowFlow][workflow]") +TEST_CASE("ShowFlow_ShowDependencies", "[ShowFlow][workflow][showDependencies]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; @@ -1090,6 +1089,28 @@ TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]") REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); } +TEST_CASE("UpdateFlow_ShowDependencies", "[UpdateFlow][workflow][showDependencies]") +{ + TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); + + std::ostringstream updateOutput; + TestContext context{ updateOutput, std::cin }; + OverrideForCompositeInstalledSource(context); + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller.Dependencies"sv); + + UpgradeCommand update({}); + update.Execute(context); + INFO(updateOutput.str()); + + std::string updateResultStr = updateOutput.str(); + + // Verufy dependencies are informed + REQUIRE(updateResultStr.find("This package requires the following dependencies:") != std::string::npos); + REQUIRE(updateResultStr.find("PreviewIIS") != std::string::npos); + REQUIRE(updateResultStr.find("Preview VC Runtime") != std::string::npos); +} + TEST_CASE("UninstallFlow_UninstallExe", "[UninstallFlow][workflow]") { TestCommon::TempFile uninstallResultPath("TestExeUninstalled.txt"); @@ -1435,7 +1456,7 @@ TEST_CASE("ImportFlow_MachineScope", "[ImportFlow][workflow]") REQUIRE(installResultStr.find("/scope=machine") != std::string::npos); } -TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow]") +TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow][ShowDependencies]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); TestCommon::TempFile msixInstallResultPath("TestMsixInstalled.txt"); @@ -1445,22 +1466,17 @@ TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow]") OverrideForImportSource(context); OverrideForMSIX(context); OverrideForShellExecute(context); - - //context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good.json").GetPath().string()); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-Dependencies.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); - - // Verify all packages were installed - // Since we only show a dependency warning and then continue with the instalation - REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(std::filesystem::exists(msixInstallResultPath.GetPath())); // Verify dependencies for all packages are informed - REQUIRE(importOutput.str().find("WindowsFeaturesDep") != std::string::npos); - REQUIRE(importOutput.str().find("PackageDep\x1b[0m [>= 1.0]") != std::string::npos); + REQUIRE(importOutput.str().find("The packages found in this import have the following dependencies:") != std::string::npos); + REQUIRE(importOutput.str().find("PreviewIIS") != std::string::npos); + REQUIRE(importOutput.str().find("Preview VC Runtime") != std::string::npos); + REQUIRE(importOutput.str().find("Hyper-V") != std::string::npos); } void VerifyMotw(const std::filesystem::path& testFile, DWORD zone) @@ -1604,7 +1620,7 @@ TEST_CASE("InstallFlowMultiLocale_PreferenceWithBetterLocale", "[InstallFlow][wo REQUIRE(installResultStr.find("/en-GB") != std::string::npos); } -TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow]") +TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow][showDependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); @@ -1612,28 +1628,13 @@ TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow]") TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_AllDependencyTypes.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - // For now verify Installer is called and parameters are passed in. - // Since we only show a dependency warning and then continue with the instalation - REQUIRE(std::filesystem::exists(installResultPath.GetPath())); - std::ifstream installResultFile(installResultPath.GetPath()); - REQUIRE(installResultFile.is_open()); - std::string installResultStr; - std::getline(installResultFile, installResultStr); - REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); - // Verify all types of dependencies are printed - REQUIRE(installOutput.str().find("[Warning] The installer has the following dependencies") != std::string::npos); - REQUIRE(installOutput.str().find("WindowsFeaturesDep") != std::string::npos); - REQUIRE(installOutput.str().find("WindowsLibrariesDep") != std::string::npos); - // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) - REQUIRE(installOutput.str().find("Package.Dep1-x64\x1b[0m [>= 1.0]") != std::string::npos); - REQUIRE(installOutput.str().find("Package.Dep2-x64") != std::string::npos); - REQUIRE(installOutput.str().find("Package.Dep2-x64\x1b[0m [") == std::string::npos); - REQUIRE(installOutput.str().find("ExternalDep") != std::string::npos); + REQUIRE(installOutput.str().find("This package requires the following dependencies:") != std::string::npos); + REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } \ No newline at end of file From 7b488f166524f198fd3a4d25545ae34824922ec7 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 10 Jun 2021 21:42:21 -0300 Subject: [PATCH 06/82] refactor install flow --- .../Workflows/InstallFlow.cpp | 129 +++++++++++------- .../Workflows/InstallFlow.h | 7 +- 2 files changed, 88 insertions(+), 48 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 63a0e5f094..e0ad660c28 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -392,7 +392,7 @@ namespace AppInstaller::CLI::Workflow context << Workflow::ReportManifestIdentity << Workflow::ShowInstallationDisclaimer << - Workflow::ReportDependencies << + Workflow::ManageDependencies << Workflow::ReportExecutionStage(ExecutionStage::Download) << Workflow::DownloadInstaller << Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << @@ -412,7 +412,7 @@ namespace AppInstaller::CLI::Workflow Workflow::InstallPackageInstaller; } - void ReportDependencies(Execution::Context& context) + void ManageDependencies(Execution::Context& context) { const auto& installer = context.Get(); if (installer) @@ -422,61 +422,96 @@ namespace AppInstaller::CLI::Workflow { context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; - auto windowsFeaturesDep = dependencies.WindowsFeatures; - if (!windowsFeaturesDep.empty()) - { - context.Reporter.Info() << " - Windows Features: "; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) - { - context.Reporter.Info() << windowsFeaturesDep[i]; - if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } + ManageWindowsFeatureDependencies(dependencies.WindowsFeatures, context); - auto windowsLibrariesDep = dependencies.WindowsLibraries; - if (!windowsLibrariesDep.empty()) - { - context.Reporter.Info() << " - Windows Libraries: "; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) - { - context.Reporter.Info() << windowsLibrariesDep[i]; - if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } + ManageWindowsLibrariesDependencies(dependencies.WindowsLibraries, context); - auto packageDep = dependencies.PackageDependencies; - if (!packageDep.empty()) - { - context.Reporter.Info() << " - Packages: "; - for (size_t i = 0; i < packageDep.size(); i++) - { - context.Reporter.Info() << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; - if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } + ManagePackageDependencies(dependencies.PackageDependencies, context); - auto externalDependenciesDep = dependencies.ExternalDependencies; - if (!externalDependenciesDep.empty()) - { - context.Reporter.Info() << " - Externals: "; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) - { - context.Reporter.Info() << externalDependenciesDep[i]; - if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } + ManageExternalDependencies(dependencies.ExternalDependencies, context); } } } + void ManageWindowsFeatureDependencies(std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context) + { + if (!windowsFeaturesDep.empty()) + { + context.Reporter.Info() << " - Windows Features: "; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << windowsFeaturesDep[i]; + if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + + void ManageWindowsLibrariesDependencies(std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context) + { + if (!windowsLibrariesDep.empty()) + { + context.Reporter.Info() << " - Windows Libraries: "; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << windowsLibrariesDep[i]; + if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + + void ManagePackageDependencies(std::vector& packageDep, AppInstaller::CLI::Execution::Context& context) + { + if (!packageDep.empty()) + { + context.Reporter.Info() << " - Packages: "; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + + void ManageExternalDependencies(std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context) + { + if (!externalDependenciesDep.empty()) + { + context.Reporter.Info() << " - Externals: "; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << externalDependenciesDep[i]; + if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + void InstallMultiple(Execution::Context& context) { bool allSucceeded = true; + + std::set windowsFeaturesDep; + std::set windowsLibrariesDep; + std::set packageDep; + std::set externalDependenciesDep; + + for (auto package : context.Get()) + { + auto manifest = package.PackageVersion->GetManifest(); + + /*const auto& installer = context.Get(); + if (installer) + { + auto dependencies = installer->Dependencies; + if (dependencies.HasAny()) + {*/ + + } + for (auto package : context.Get()) { Logging::SubExecutionTelemetryScope subExecution; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 5220bca46b..674faade7d 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -93,7 +93,12 @@ namespace AppInstaller::CLI::Workflow // Required Args: None // Inputs: Manifest // Outputs: None - void ReportDependencies(Execution::Context& context); + void ManageDependencies(Execution::Context& context); + + void ManageExternalDependencies(std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context); + void ManagePackageDependencies(std::vector& packageDep, AppInstaller::CLI::Execution::Context& context); + void ManageWindowsLibrariesDependencies(std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context); + void ManageWindowsFeatureDependencies(std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context); // Installs multiple packages. // Required Args: None From c8d02ea6bb15af40f51a491452cb3cab15a23d2d Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 11 Jun 2021 18:18:04 -0300 Subject: [PATCH 07/82] change import command from install flow (install multiple) --- .../Commands/UninstallCommand.cpp | 1 + .../Commands/UpgradeCommand.cpp | 5 +- .../Workflows/InstallFlow.cpp | 82 +++++++++++++++---- .../Workflows/UninstallFlow.cpp | 62 ++++++++++++++ .../Workflows/UninstallFlow.h | 6 ++ .../Workflows/UpdateFlow.cpp | 4 +- 6 files changed, 140 insertions(+), 20 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index df84be4b78..f0caac107b 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -128,6 +128,7 @@ namespace AppInstaller::CLI context << Workflow::GetInstalledPackageVersion << Workflow::GetUninstallInfo << + //Workflow::ReportDependencies << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << Workflow::ReportExecutionStage(ExecutionStage::PostExecution); diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 94f42929dc..14c5183dc9 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -149,6 +149,7 @@ namespace AppInstaller::CLI EnsureUpdateVersionApplicable << SelectInstaller << EnsureApplicableInstaller << + ManageDependencies << InstallPackageInstaller; } else @@ -175,7 +176,9 @@ namespace AppInstaller::CLI context << SelectLatestApplicableUpdate(true); } - context << InstallPackageInstaller; + context << + ManageDependencies << + InstallPackageInstaller; } } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index e0ad660c28..864418a648 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -392,7 +392,6 @@ namespace AppInstaller::CLI::Workflow context << Workflow::ReportManifestIdentity << Workflow::ShowInstallationDisclaimer << - Workflow::ManageDependencies << Workflow::ReportExecutionStage(ExecutionStage::Download) << Workflow::DownloadInstaller << Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << @@ -409,6 +408,7 @@ namespace AppInstaller::CLI::Workflow context << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << + Workflow::ManageDependencies << Workflow::InstallPackageInstaller; } @@ -490,27 +490,25 @@ namespace AppInstaller::CLI::Workflow } } - void InstallMultiple(Execution::Context& context) + const struct PackagesAndInstallers { - bool allSucceeded = true; + PackagesAndInstallers(std::optional inst, + AppInstaller::CLI::Execution::PackageToInstall pkg) : installer(inst), package(pkg) {} - std::set windowsFeaturesDep; - std::set windowsLibrariesDep; - std::set packageDep; - std::set externalDependenciesDep; + std::optional installer; + AppInstaller::CLI::Execution::PackageToInstall package; + }; - for (auto package : context.Get()) - { - auto manifest = package.PackageVersion->GetManifest(); + void InstallMultiple(Execution::Context& context) + { + bool allSucceeded = true; - /*const auto& installer = context.Get(); - if (installer) - { - auto dependencies = installer->Dependencies; - if (dependencies.HasAny()) - {*/ + std::vector windowsFeaturesDep; + std::vector windowsLibrariesDep; + std::vector packageDep; + std::vector externalDep; - } + std::vector installers; for (auto package : context.Get()) { @@ -527,7 +525,55 @@ namespace AppInstaller::CLI::Workflow // TODO: In the future, it would be better to not have to convert back and forth from a string installContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(package.PackageRequest.Scope)); - installContext << InstallPackageVersion; + //installContexts.push_back(installContext); + installContext << + Workflow::SelectInstaller; + + if (installContext.IsTerminated()) + { + allSucceeded = false; + } + + const auto& installer = context.Get(); + installers.push_back(PackagesAndInstallers(installer, package)); + + if (installer) { + auto dependencies = installer->Dependencies; + windowsFeaturesDep.insert(windowsFeaturesDep.begin(), + dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); + windowsLibrariesDep.insert(windowsLibrariesDep.begin(), + dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); + packageDep.insert(packageDep.begin(), + dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); + externalDep.insert(externalDep.begin(), + dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); + } + } + + context.Reporter.Info() << "The packages found in this import have the following dependencies:" << std::endl; + ManageWindowsFeatureDependencies(windowsFeaturesDep, context); + ManageWindowsLibrariesDependencies(windowsLibrariesDep, context); + ManagePackageDependencies(packageDep, context); + ManageExternalDependencies(externalDep, context); + + for (auto packageAndInstaller : installers) + { + auto package = packageAndInstaller.package; + auto installer = packageAndInstaller.installer; + + auto installContextPtr = context.Clone(); + Execution::Context& installContext = *installContextPtr; + + // set data needed for installing + installContext.Add(package.PackageVersion); + installContext.Add(package.PackageVersion->GetManifest()); + installContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(package.PackageRequest.Scope)); + installContext.Add(installer); + + installContext << + Workflow::EnsureApplicableInstaller << + Workflow::InstallPackageInstaller; + if (installContext.IsTerminated()) { allSucceeded = false; diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index b5fa852316..64b7b50235 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -123,4 +123,66 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; } + + void ReportDependencies(Execution::Context& context) + { + const auto& installer = context.Get(); + if (installer) + { + auto dependencies = installer->Dependencies; + if (dependencies.HasAny()) + { + context.Reporter.Info() << "This package had dependencies that may not be needed anymore:" << std::endl; + + auto windowsFeaturesDep = dependencies.WindowsFeatures; + if (!windowsFeaturesDep.empty()) + { + context.Reporter.Info() << " - Windows Features: "; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << windowsFeaturesDep[i]; + if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + + auto windowsLibrariesDep = dependencies.WindowsLibraries; + if (!windowsLibrariesDep.empty()) + { + context.Reporter.Info() << " - Windows Libraries: "; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << windowsLibrariesDep[i]; + if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + + auto packageDep = dependencies.PackageDependencies; + if (!packageDep.empty()) + { + context.Reporter.Info() << " - Packages: "; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + + auto externalDependenciesDep = dependencies.ExternalDependencies; + if (!externalDependenciesDep.empty()) + { + context.Reporter.Info() << " - Externals: "; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << externalDependenciesDep[i]; + if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + } + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h index 8a71a23040..54d639e663 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -22,4 +22,10 @@ namespace AppInstaller::CLI::Workflow // Inputs: PackageFamilyNames // Outputs: None void MsixUninstall(Execution::Context& context); + + // Shows information about dependencies. + // Required Args: None + // Inputs: Manifest + // Outputs: None + void ReportDependencies(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index f4c371b13a..8a5b251a9a 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -111,7 +111,9 @@ namespace AppInstaller::CLI::Workflow updateAllFoundUpdate = true; - updateContext << InstallPackageInstaller; + updateContext << + ManageDependencies << + InstallPackageInstaller; updateContext.Reporter.Info() << std::endl; From 72fd209edcb97b8a6ee59a09da7d59af0f4a020f Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 11 Jun 2021 19:32:15 -0300 Subject: [PATCH 08/82] import shows all dependencies together --- src/AppInstallerCLICore/Workflows/InstallFlow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 864418a648..f248e0a1d4 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -534,7 +534,7 @@ namespace AppInstaller::CLI::Workflow allSucceeded = false; } - const auto& installer = context.Get(); + const auto& installer = installContext.Get(); installers.push_back(PackagesAndInstallers(installer, package)); if (installer) { From d6d0c533cc190daffe6189a8226188fcfa361adb Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 11 Jun 2021 20:49:55 -0300 Subject: [PATCH 09/82] show dependencies for validate and uninstall --- .../Commands/UninstallCommand.cpp | 2 +- .../Commands/ValidateCommand.cpp | 83 ++++++++++++++++++- .../Workflows/UninstallFlow.cpp | 4 + 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index f0caac107b..458d0825ba 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -128,7 +128,7 @@ namespace AppInstaller::CLI context << Workflow::GetInstalledPackageVersion << Workflow::GetUninstallInfo << - //Workflow::ReportDependencies << + Workflow::ReportDependencies << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << Workflow::ReportExecutionStage(ExecutionStage::PostExecution); diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index 8955e13de2..c27eaf13a5 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -31,6 +31,63 @@ namespace AppInstaller::CLI return "https://aka.ms/winget-command-validate"; } + void ShowWindowsFeatureDependencies(std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context) + { + if (!windowsFeaturesDep.empty()) + { + context.Reporter.Info() << " - Windows Features: "; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << windowsFeaturesDep[i]; + if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + + void ShowWindowsLibrariesDependencies(std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context) + { + if (!windowsLibrariesDep.empty()) + { + context.Reporter.Info() << " - Windows Libraries: "; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << windowsLibrariesDep[i]; + if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + + void ShowPackageDependencies(std::vector& packageDep, AppInstaller::CLI::Execution::Context& context) + { + if (!packageDep.empty()) + { + context.Reporter.Info() << " - Packages: "; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + + void ShowExternalDependencies(std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context) + { + if (!externalDependenciesDep.empty()) + { + context.Reporter.Info() << " - Externals: "; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << externalDependenciesDep[i]; + if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; + } + } + void ValidateCommand::ExecuteInternal(Execution::Context& context) const { context << @@ -41,7 +98,31 @@ namespace AppInstaller::CLI try { - (void)Manifest::YamlParser::CreateFromPath(inputFile, true, true); + auto manifest = Manifest::YamlParser::CreateFromPath(inputFile, true, true); + + std::vector windowsFeaturesDep; + std::vector windowsLibrariesDep; + std::vector packageDep; + std::vector externalDep; + for (auto installer : manifest.Installers) + { + auto dependencies = installer.Dependencies; + windowsFeaturesDep.insert(windowsFeaturesDep.begin(), + dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); + windowsLibrariesDep.insert(windowsLibrariesDep.begin(), + dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); + packageDep.insert(packageDep.begin(), + dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); + externalDep.insert(externalDep.begin(), + dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); + } + + context.Reporter.Info() << "Manifest has the following dependencies:" << std::endl; + ShowWindowsFeatureDependencies(windowsFeaturesDep, context); + ShowWindowsLibrariesDependencies(windowsLibrariesDep, context); + ShowPackageDependencies(packageDep, context); + ShowExternalDependencies(externalDep, context); + context.Reporter.Info() << Resource::String::ManifestValidationSuccess << std::endl; } catch (const Manifest::ManifestException& e) diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 64b7b50235..b8d148f842 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -126,6 +126,10 @@ namespace AppInstaller::CLI::Workflow void ReportDependencies(Execution::Context& context) { + context << + Workflow::GetManifest << + Workflow::SelectInstaller; + const auto& installer = context.Get(); if (installer) { From d3063f239a9ac89c0f94619cb9ddd99f684edeaa Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 11 Jun 2021 20:50:28 -0300 Subject: [PATCH 10/82] tests for validate and uninstall --- src/AppInstallerCLITests/WorkFlow.cpp | 45 ++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 3b0f538b10..f223ffb6c9 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -27,6 +27,7 @@ #include #include #include +#include using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Management::Deployment; @@ -1105,7 +1106,7 @@ TEST_CASE("UpdateFlow_ShowDependencies", "[UpdateFlow][workflow][showDependencie std::string updateResultStr = updateOutput.str(); - // Verufy dependencies are informed + // Verify dependencies are informed REQUIRE(updateResultStr.find("This package requires the following dependencies:") != std::string::npos); REQUIRE(updateResultStr.find("PreviewIIS") != std::string::npos); REQUIRE(updateResultStr.find("Preview VC Runtime") != std::string::npos); @@ -1202,6 +1203,27 @@ TEST_CASE("UninstallFlow_UninstallExeNotFound", "[UninstallFlow][workflow]") REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); } +TEST_CASE("UninstallFlow_ShowDependencies", "[UninstallFlow][workflow][showDependencies]") +{ + TestCommon::TempFile uninstallResultPath("TestExeUninstalled.txt"); + + std::ostringstream uninstallOutput; + TestContext context{ uninstallOutput, std::cin }; + OverrideForCompositeInstalledSource(context); + OverrideForExeUninstall(context); + context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller.Dependencies"sv); + context.Args.AddArg(Execution::Args::Type::Silent); + + UninstallCommand uninstall({}); + uninstall.Execute(context); + INFO(uninstallOutput.str()); + + // Verify dependencies are informed + REQUIRE(uninstallOutput.str().find("This package had dependencies that may not be needed anymore:") != std::string::npos); + REQUIRE(uninstallOutput.str().find("PreviewIIS") != std::string::npos); + REQUIRE(uninstallOutput.str().find("Preview VC Runtime") != std::string::npos); +} + TEST_CASE("ExportFlow_ExportAll", "[ExportFlow][workflow]") { TestCommon::TempFile exportResultPath("TestExport.json"); @@ -1637,4 +1659,25 @@ TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow][showDependenc // Verify all types of dependencies are printed REQUIRE(installOutput.str().find("This package requires the following dependencies:") != std::string::npos); REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); +} + +TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") +{ + std::ostringstream validateOutput; + TestContext context{ validateOutput, std::cin }; + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); + + ValidateCommand validate({}); + validate.Execute(context); + INFO(validateOutput.str()); + + // Verify all types of dependencies are printed + REQUIRE(validateOutput.str().find("Manifest has the following dependencies:") != std::string::npos); + REQUIRE(validateOutput.str().find("WindowsFeaturesDep") != std::string::npos); + REQUIRE(validateOutput.str().find("WindowsLibrariesDep") != std::string::npos); + // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) + REQUIRE(validateOutput.str().find("Package.Dep1-x64\x1b[0m [>= 1.0]") != std::string::npos); + REQUIRE(validateOutput.str().find("Package.Dep2-x64") != std::string::npos); + REQUIRE(validateOutput.str().find("Package.Dep2-x64\x1b[0m [") == std::string::npos); + REQUIRE(validateOutput.str().find("ExternalDep") != std::string::npos); } \ No newline at end of file From 9d05f6f0212e17295273b89852ab0aaa1763ef4c Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 11 Jun 2021 21:32:17 -0300 Subject: [PATCH 11/82] create show dependencies exp feature --- doc/Settings.md | 10 ++++++++++ src/AppInstallerCommonCore/ExperimentalFeature.cpp | 4 ++++ .../Public/winget/ExperimentalFeature.h | 1 + .../Public/winget/UserSettings.h | 2 ++ src/AppInstallerCommonCore/UserSettings.cpp | 1 + 5 files changed, 18 insertions(+) diff --git a/doc/Settings.md b/doc/Settings.md index 2d4dcb23b1..d14ad6d656 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -133,3 +133,13 @@ Microsoft Store App support in WinGet is currently implemented as an experimenta "experimentalMSStore": true }, ``` + +### experimentalShowDependencies + +Experimental feature that shows package dependency information. It simply shows information, doesn't manage dependencies. You can enable the feature as shown below. + +```json + "experimentalFeatures": { + "experimentalShowDependencies": true + }, +``` \ No newline at end of file diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index 8f8f0ff6de..17ff527fd8 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -44,6 +44,8 @@ namespace AppInstaller::Settings return userSettings.Get(); case ExperimentalFeature::Feature::ExperimentalMSStore: return userSettings.Get(); + case ExperimentalFeature::Feature::EFExperimentalShowDependencies: + return userSettings.Get(); default: THROW_HR(E_UNEXPECTED); } @@ -72,6 +74,8 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "Argument Sample", "experimentalArg", "https://aka.ms/winget-settings", Feature::ExperimentalArg }; case Feature::ExperimentalMSStore: return ExperimentalFeature{ "Microsoft Store Support", "experimentalMSStore", "https://aka.ms/winget-settings", Feature::ExperimentalMSStore }; + case Feature::EFExperimentalShowDependencies: + return ExperimentalFeature{ "Show Dependencies Information", "experimentalShowDependencies", "https://aka.ms/winget-settings", Feature::EFExperimentalShowDependencies }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index b1bbf0e8eb..3e986cf2d5 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -21,6 +21,7 @@ namespace AppInstaller::Settings { None = 0x0, ExperimentalMSStore = 0x1, + EFExperimentalShowDependencies = 0x2, Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 69932b45a5..9665463681 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -68,6 +68,7 @@ namespace AppInstaller::Settings EFExperimentalCmd, EFExperimentalArg, EFExperimentalMSStore, + EFExperimentalShowDependencies, TelemetryDisable, InstallScopePreference, InstallScopeRequirement, @@ -113,6 +114,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalCmd, bool, bool, false, ".experimentalFeatures.experimentalCmd"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalArg, bool, bool, false, ".experimentalFeatures.experimentalArg"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalMSStore, bool, bool, false, ".experimentalFeatures.experimentalMSStore"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalShowDependencies, bool, bool, false, ".experimentalFeatures.experimentalShowDependencies"sv); SETTINGMAPPING_SPECIALIZATION(Setting::TelemetryDisable, bool, bool, false, ".telemetry.disable"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopePreference, std::string, ScopePreference, ScopePreference::User, ".installBehavior.preferences.scope"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopeRequirement, std::string, ScopePreference, ScopePreference::None, ".installBehavior.requirements.scope"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 8cac00105e..5037bbbe0e 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -225,6 +225,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFExperimentalCmd) WINGET_VALIDATE_PASS_THROUGH(EFExperimentalArg) WINGET_VALIDATE_PASS_THROUGH(EFExperimentalMSStore) + WINGET_VALIDATE_PASS_THROUGH(EFExperimentalShowDependencies) WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) WINGET_VALIDATE_SIGNATURE(InstallScopePreference) From e3ccc8e169e65ef6d55ee916a669a95748cb4c60 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 14 Jun 2021 10:59:13 -0300 Subject: [PATCH 12/82] test for validate command --- src/AppInstallerCLITests/WorkFlow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index f223ffb6c9..1ca82d7b9d 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1665,7 +1665,7 @@ TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") { std::ostringstream validateOutput; TestContext context{ validateOutput, std::cin }; - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::ValidateManifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); ValidateCommand validate({}); validate.Execute(context); From 44fe9e2bc9e9197dfbcba7e50e70acf2df4e313f Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 14 Jun 2021 12:30:25 -0300 Subject: [PATCH 13/82] put functionality under experimental feature check --- .../Commands/UninstallCommand.cpp | 10 ++- .../Commands/UpgradeCommand.cpp | 20 +++-- .../Commands/ValidateCommand.cpp | 43 +++++----- .../Workflows/InstallFlow.cpp | 65 +++++++++------ .../Workflows/ShowFlow.cpp | 82 ++++++++++--------- 5 files changed, 125 insertions(+), 95 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index 458d0825ba..a3067c9ef6 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -127,8 +127,14 @@ namespace AppInstaller::CLI context << Workflow::GetInstalledPackageVersion << - Workflow::GetUninstallInfo << - Workflow::ReportDependencies << + Workflow::GetUninstallInfo; + + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + { + context << Workflow::ReportDependencies; + } + + context << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << Workflow::ReportExecutionStage(ExecutionStage::PostExecution); diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 14c5183dc9..311618880c 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -148,9 +148,14 @@ namespace AppInstaller::CLI GetInstalledPackageVersion << EnsureUpdateVersionApplicable << SelectInstaller << - EnsureApplicableInstaller << - ManageDependencies << - InstallPackageInstaller; + EnsureApplicableInstaller; + + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + { + context << ManageDependencies; + } + + context << InstallPackageInstaller; } else { @@ -176,9 +181,12 @@ namespace AppInstaller::CLI context << SelectLatestApplicableUpdate(true); } - context << - ManageDependencies << - InstallPackageInstaller; + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + { + context << ManageDependencies; + } + + context << InstallPackageInstaller; } } } diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index c27eaf13a5..384a9f14db 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -100,28 +100,31 @@ namespace AppInstaller::CLI { auto manifest = Manifest::YamlParser::CreateFromPath(inputFile, true, true); - std::vector windowsFeaturesDep; - std::vector windowsLibrariesDep; - std::vector packageDep; - std::vector externalDep; - for (auto installer : manifest.Installers) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) { - auto dependencies = installer.Dependencies; - windowsFeaturesDep.insert(windowsFeaturesDep.begin(), - dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); - windowsLibrariesDep.insert(windowsLibrariesDep.begin(), - dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); - packageDep.insert(packageDep.begin(), - dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); - externalDep.insert(externalDep.begin(), - dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); + std::vector windowsFeaturesDep; + std::vector windowsLibrariesDep; + std::vector packageDep; + std::vector externalDep; + for (auto installer : manifest.Installers) + { + auto dependencies = installer.Dependencies; + windowsFeaturesDep.insert(windowsFeaturesDep.begin(), + dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); + windowsLibrariesDep.insert(windowsLibrariesDep.begin(), + dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); + packageDep.insert(packageDep.begin(), + dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); + externalDep.insert(externalDep.begin(), + dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); + } + + context.Reporter.Info() << "Manifest has the following dependencies:" << std::endl; + ShowWindowsFeatureDependencies(windowsFeaturesDep, context); + ShowWindowsLibrariesDependencies(windowsLibrariesDep, context); + ShowPackageDependencies(packageDep, context); + ShowExternalDependencies(externalDep, context); } - - context.Reporter.Info() << "Manifest has the following dependencies:" << std::endl; - ShowWindowsFeatureDependencies(windowsFeaturesDep, context); - ShowWindowsLibrariesDependencies(windowsLibrariesDep, context); - ShowPackageDependencies(packageDep, context); - ShowExternalDependencies(externalDep, context); context.Reporter.Info() << Resource::String::ManifestValidationSuccess << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index f248e0a1d4..11972a0292 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -407,29 +407,31 @@ namespace AppInstaller::CLI::Workflow { context << Workflow::SelectInstaller << - Workflow::EnsureApplicableInstaller << - Workflow::ManageDependencies << - Workflow::InstallPackageInstaller; + Workflow::EnsureApplicableInstaller; + + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + { + context << Workflow::ManageDependencies; + } + + context << Workflow::InstallPackageInstaller; } void ManageDependencies(Execution::Context& context) { const auto& installer = context.Get(); - if (installer) + if (installer && installer->Dependencies.HasAny()) { auto dependencies = installer->Dependencies; - if (dependencies.HasAny()) - { - context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; + context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; - ManageWindowsFeatureDependencies(dependencies.WindowsFeatures, context); + ManageWindowsFeatureDependencies(dependencies.WindowsFeatures, context); - ManageWindowsLibrariesDependencies(dependencies.WindowsLibraries, context); + ManageWindowsLibrariesDependencies(dependencies.WindowsLibraries, context); - ManagePackageDependencies(dependencies.PackageDependencies, context); + ManagePackageDependencies(dependencies.PackageDependencies, context); - ManageExternalDependencies(dependencies.ExternalDependencies, context); - } + ManageExternalDependencies(dependencies.ExternalDependencies, context); } } @@ -537,24 +539,33 @@ namespace AppInstaller::CLI::Workflow const auto& installer = installContext.Get(); installers.push_back(PackagesAndInstallers(installer, package)); - if (installer) { - auto dependencies = installer->Dependencies; - windowsFeaturesDep.insert(windowsFeaturesDep.begin(), - dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); - windowsLibrariesDep.insert(windowsLibrariesDep.begin(), - dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); - packageDep.insert(packageDep.begin(), - dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); - externalDep.insert(externalDep.begin(), - dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + { + if (installer) { + auto dependencies = installer->Dependencies; + windowsFeaturesDep.insert(windowsFeaturesDep.begin(), + dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); + windowsLibrariesDep.insert(windowsLibrariesDep.begin(), + dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); + packageDep.insert(packageDep.begin(), + dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); + externalDep.insert(externalDep.begin(), + dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); + } } } - context.Reporter.Info() << "The packages found in this import have the following dependencies:" << std::endl; - ManageWindowsFeatureDependencies(windowsFeaturesDep, context); - ManageWindowsLibrariesDependencies(windowsLibrariesDep, context); - ManagePackageDependencies(packageDep, context); - ManageExternalDependencies(externalDep, context); + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + { + if (!windowsFeaturesDep.empty() || !windowsLibrariesDep.empty() || !packageDep.empty() || !externalDep.empty()) + { + context.Reporter.Info() << "The packages found in this import have the following dependencies:" << std::endl; + ManageWindowsFeatureDependencies(windowsFeaturesDep, context); + ManageWindowsLibrariesDependencies(windowsLibrariesDep, context); + ManagePackageDependencies(packageDep, context); + ManageExternalDependencies(externalDep, context); + } + } for (auto packageAndInstaller : installers) { diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 91df20f3cf..f681719afd 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -70,58 +70,60 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Execution::ManifestInfoEmphasis << " ProductId: " << installer->ProductId << std::endl; } - auto dependencies = installer->Dependencies; - if (dependencies.HasAny()) - { - context.Reporter.Info() << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; - - auto windowsFeaturesDep = dependencies.WindowsFeatures; - if (!windowsFeaturesDep.empty()) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) { + auto dependencies = installer->Dependencies; + if (dependencies.HasAny()) { - context.Reporter.Info() << " - Windows Features: "; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + context.Reporter.Info() << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; + + auto windowsFeaturesDep = dependencies.WindowsFeatures; + if (!windowsFeaturesDep.empty()) { - context.Reporter.Info() << windowsFeaturesDep[i]; - if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + context.Reporter.Info() << " - Windows Features: "; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + context.Reporter.Info() << windowsFeaturesDep[i]; + if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } - context.Reporter.Info() << std::endl; - } - auto windowsLibrariesDep = dependencies.WindowsLibraries; - if (!windowsLibrariesDep.empty()) - { - context.Reporter.Info() << " - Windows Libraries: "; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + auto windowsLibrariesDep = dependencies.WindowsLibraries; + if (!windowsLibrariesDep.empty()) { - context.Reporter.Info() << windowsLibrariesDep[i]; - if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + context.Reporter.Info() << " - Windows Libraries: "; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + context.Reporter.Info() << windowsLibrariesDep[i]; + if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } - context.Reporter.Info() << std::endl; - } - auto packageDep = dependencies.PackageDependencies; - if (!packageDep.empty()) - { - context.Reporter.Info() << " - Packages: "; - for (size_t i = 0; i < packageDep.size(); i++) + auto packageDep = dependencies.PackageDependencies; + if (!packageDep.empty()) { - context.Reporter.Info() << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; - if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; + context.Reporter.Info() << " - Packages: "; + for (size_t i = 0; i < packageDep.size(); i++) + { + context.Reporter.Info() << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; + if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } - context.Reporter.Info() << std::endl; - } - auto externalDependenciesDep = dependencies.ExternalDependencies; - if (!externalDependenciesDep.empty()) - { - context.Reporter.Info() << " - Externals: "; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) + auto externalDependenciesDep = dependencies.ExternalDependencies; + if (!externalDependenciesDep.empty()) { - context.Reporter.Info() << externalDependenciesDep[i]; - if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + context.Reporter.Info() << " - Externals: "; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + context.Reporter.Info() << externalDependenciesDep[i]; + if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + } + context.Reporter.Info() << std::endl; } - context.Reporter.Info() << std::endl; } } } From a69b9303c66b0c0bc0f84a1ae662fb66a2bd39ce Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 14 Jun 2021 14:29:58 -0300 Subject: [PATCH 14/82] enable show dep experimental feature on unit test cases --- src/AppInstallerCLITests/WorkFlow.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 1ca82d7b9d..d5651a5f2c 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -821,6 +821,9 @@ TEST_CASE("ShowFlow_ShowDependencies", "[ShowFlow][workflow][showDependencies]") TestContext context{ showOutput, std::cin }; context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); + TestUserSettings settings; + settings.Set({true}); + ShowCommand show({}); show.Execute(context); INFO(showOutput.str()); @@ -1100,6 +1103,9 @@ TEST_CASE("UpdateFlow_ShowDependencies", "[UpdateFlow][workflow][showDependencie OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller.Dependencies"sv); + TestUserSettings settings; + settings.Set({ true }); + UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); @@ -1214,6 +1220,9 @@ TEST_CASE("UninstallFlow_ShowDependencies", "[UninstallFlow][workflow][showDepen context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller.Dependencies"sv); context.Args.AddArg(Execution::Args::Type::Silent); + TestUserSettings settings; + settings.Set({ true }); + UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); @@ -1490,6 +1499,9 @@ TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow][ShowDependencie OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-Dependencies.json").GetPath().string()); + TestUserSettings settings; + settings.Set({ true }); + ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); @@ -1652,6 +1664,9 @@ TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow][showDependenc context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); + TestUserSettings settings; + settings.Set({ true }); + InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); @@ -1667,6 +1682,9 @@ TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") TestContext context{ validateOutput, std::cin }; context.Args.AddArg(Execution::Args::Type::ValidateManifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); + TestUserSettings settings; + settings.Set({ true }); + ValidateCommand validate({}); validate.Execute(context); INFO(validateOutput.str()); From 8bb3aa15724f2546171c1dc5a6fb07e72bcec9cd Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 15 Jun 2021 11:15:57 -0300 Subject: [PATCH 15/82] change experimental feature name --- doc/Settings.md | 6 +++--- .../Commands/UninstallCommand.cpp | 2 +- src/AppInstallerCLICore/Commands/UpgradeCommand.cpp | 4 ++-- src/AppInstallerCLICore/Commands/ValidateCommand.cpp | 2 +- src/AppInstallerCLICore/Workflows/InstallFlow.cpp | 6 +++--- src/AppInstallerCLICore/Workflows/ShowFlow.cpp | 2 +- src/AppInstallerCLITests/WorkFlow.cpp | 12 ++++++------ src/AppInstallerCommonCore/ExperimentalFeature.cpp | 8 ++++---- .../Public/winget/ExperimentalFeature.h | 2 +- .../Public/winget/UserSettings.h | 4 ++-- src/AppInstallerCommonCore/UserSettings.cpp | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/doc/Settings.md b/doc/Settings.md index d14ad6d656..2e0f26e7be 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -134,12 +134,12 @@ Microsoft Store App support in WinGet is currently implemented as an experimenta }, ``` -### experimentalShowDependencies +### Dependencies -Experimental feature that shows package dependency information. It simply shows information, doesn't manage dependencies. You can enable the feature as shown below. +Experimental feature with the aim of managing dependencies, as of now it only shows package dependency information. You can enable the feature as shown below. ```json "experimentalFeatures": { - "experimentalShowDependencies": true + "dependencies": true }, ``` \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index a3067c9ef6..466d89a8b1 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -129,7 +129,7 @@ namespace AppInstaller::CLI Workflow::GetInstalledPackageVersion << Workflow::GetUninstallInfo; - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { context << Workflow::ReportDependencies; } diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 311618880c..98b5117f31 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -150,7 +150,7 @@ namespace AppInstaller::CLI SelectInstaller << EnsureApplicableInstaller; - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { context << ManageDependencies; } @@ -181,7 +181,7 @@ namespace AppInstaller::CLI context << SelectLatestApplicableUpdate(true); } - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { context << ManageDependencies; } diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index 384a9f14db..eeca9e20c2 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -100,7 +100,7 @@ namespace AppInstaller::CLI { auto manifest = Manifest::YamlParser::CreateFromPath(inputFile, true, true); - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { std::vector windowsFeaturesDep; std::vector windowsLibrariesDep; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 11972a0292..660430e668 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -409,7 +409,7 @@ namespace AppInstaller::CLI::Workflow Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller; - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { context << Workflow::ManageDependencies; } @@ -539,7 +539,7 @@ namespace AppInstaller::CLI::Workflow const auto& installer = installContext.Get(); installers.push_back(PackagesAndInstallers(installer, package)); - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { if (installer) { auto dependencies = installer->Dependencies; @@ -555,7 +555,7 @@ namespace AppInstaller::CLI::Workflow } } - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { if (!windowsFeaturesDep.empty() || !windowsLibrariesDep.empty() || !packageDep.empty() || !externalDep.empty()) { diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index f681719afd..c998e04f15 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -70,7 +70,7 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Execution::ManifestInfoEmphasis << " ProductId: " << installer->ProductId << std::endl; } - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::EFExperimentalShowDependencies)) { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { auto dependencies = installer->Dependencies; if (dependencies.HasAny()) { diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index d5651a5f2c..4297892c99 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -822,7 +822,7 @@ TEST_CASE("ShowFlow_ShowDependencies", "[ShowFlow][workflow][showDependencies]") context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); TestUserSettings settings; - settings.Set({true}); + settings.Set({true}); ShowCommand show({}); show.Execute(context); @@ -1104,7 +1104,7 @@ TEST_CASE("UpdateFlow_ShowDependencies", "[UpdateFlow][workflow][showDependencie context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller.Dependencies"sv); TestUserSettings settings; - settings.Set({ true }); + settings.Set({ true }); UpgradeCommand update({}); update.Execute(context); @@ -1221,7 +1221,7 @@ TEST_CASE("UninstallFlow_ShowDependencies", "[UninstallFlow][workflow][showDepen context.Args.AddArg(Execution::Args::Type::Silent); TestUserSettings settings; - settings.Set({ true }); + settings.Set({ true }); UninstallCommand uninstall({}); uninstall.Execute(context); @@ -1500,7 +1500,7 @@ TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow][ShowDependencie context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-Dependencies.json").GetPath().string()); TestUserSettings settings; - settings.Set({ true }); + settings.Set({ true }); ImportCommand importCommand({}); importCommand.Execute(context); @@ -1665,7 +1665,7 @@ TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow][showDependenc context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); TestUserSettings settings; - settings.Set({ true }); + settings.Set({ true }); InstallCommand install({}); install.Execute(context); @@ -1683,7 +1683,7 @@ TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") context.Args.AddArg(Execution::Args::Type::ValidateManifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); TestUserSettings settings; - settings.Set({ true }); + settings.Set({ true }); ValidateCommand validate({}); validate.Execute(context); diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index 17ff527fd8..5dcf9d3d26 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -44,8 +44,8 @@ namespace AppInstaller::Settings return userSettings.Get(); case ExperimentalFeature::Feature::ExperimentalMSStore: return userSettings.Get(); - case ExperimentalFeature::Feature::EFExperimentalShowDependencies: - return userSettings.Get(); + case ExperimentalFeature::Feature::Dependencies: + return userSettings.Get(); default: THROW_HR(E_UNEXPECTED); } @@ -74,8 +74,8 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "Argument Sample", "experimentalArg", "https://aka.ms/winget-settings", Feature::ExperimentalArg }; case Feature::ExperimentalMSStore: return ExperimentalFeature{ "Microsoft Store Support", "experimentalMSStore", "https://aka.ms/winget-settings", Feature::ExperimentalMSStore }; - case Feature::EFExperimentalShowDependencies: - return ExperimentalFeature{ "Show Dependencies Information", "experimentalShowDependencies", "https://aka.ms/winget-settings", Feature::EFExperimentalShowDependencies }; + case Feature::Dependencies: + return ExperimentalFeature{ "Show Dependencies Information", "dependencies", "https://aka.ms/winget-settings", Feature::Dependencies }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 3e986cf2d5..e76118fec1 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -21,7 +21,7 @@ namespace AppInstaller::Settings { None = 0x0, ExperimentalMSStore = 0x1, - EFExperimentalShowDependencies = 0x2, + Dependencies = 0x2, Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 9665463681..3a4467d545 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -68,7 +68,7 @@ namespace AppInstaller::Settings EFExperimentalCmd, EFExperimentalArg, EFExperimentalMSStore, - EFExperimentalShowDependencies, + EFDependencies, TelemetryDisable, InstallScopePreference, InstallScopeRequirement, @@ -114,7 +114,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalCmd, bool, bool, false, ".experimentalFeatures.experimentalCmd"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalArg, bool, bool, false, ".experimentalFeatures.experimentalArg"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalMSStore, bool, bool, false, ".experimentalFeatures.experimentalMSStore"sv); - SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalShowDependencies, bool, bool, false, ".experimentalFeatures.experimentalShowDependencies"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFDependencies, bool, bool, false, ".experimentalFeatures.dependencies"sv); SETTINGMAPPING_SPECIALIZATION(Setting::TelemetryDisable, bool, bool, false, ".telemetry.disable"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopePreference, std::string, ScopePreference, ScopePreference::User, ".installBehavior.preferences.scope"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopeRequirement, std::string, ScopePreference, ScopePreference::None, ".installBehavior.requirements.scope"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 5037bbbe0e..81e63813eb 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -225,7 +225,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFExperimentalCmd) WINGET_VALIDATE_PASS_THROUGH(EFExperimentalArg) WINGET_VALIDATE_PASS_THROUGH(EFExperimentalMSStore) - WINGET_VALIDATE_PASS_THROUGH(EFExperimentalShowDependencies) + WINGET_VALIDATE_PASS_THROUGH(EFDependencies) WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) WINGET_VALIDATE_SIGNATURE(InstallScopePreference) From 9737ae1f955515420a90b5d36a9201f23438aa35 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 15 Jun 2021 14:04:34 -0300 Subject: [PATCH 16/82] remove info stream characters on testcase --- src/AppInstallerCLITests/WorkFlow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 4297892c99..abd5ead8aa 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -833,9 +833,9 @@ TEST_CASE("ShowFlow_ShowDependencies", "[ShowFlow][workflow][showDependencies]") REQUIRE(showOutput.str().find("WindowsFeaturesDep") != std::string::npos); REQUIRE(showOutput.str().find("WindowsLibrariesDep") != std::string::npos); // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) - REQUIRE(showOutput.str().find("Package.Dep1-x64\x1b[0m [>= 1.0]") != std::string::npos); + REQUIRE(showOutput.str().find("Package.Dep1-x64 [>= 1.0]") != std::string::npos); REQUIRE(showOutput.str().find("Package.Dep2-x64") != std::string::npos); - REQUIRE(showOutput.str().find("Package.Dep2-x64\x1b[0m [") == std::string::npos); + REQUIRE(showOutput.str().find("Package.Dep2-x64 [") == std::string::npos); REQUIRE(showOutput.str().find("ExternalDep") != std::string::npos); } @@ -1694,8 +1694,8 @@ TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") REQUIRE(validateOutput.str().find("WindowsFeaturesDep") != std::string::npos); REQUIRE(validateOutput.str().find("WindowsLibrariesDep") != std::string::npos); // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) - REQUIRE(validateOutput.str().find("Package.Dep1-x64\x1b[0m [>= 1.0]") != std::string::npos); + REQUIRE(validateOutput.str().find("Package.Dep1-x64 [>= 1.0]") != std::string::npos); REQUIRE(validateOutput.str().find("Package.Dep2-x64") != std::string::npos); - REQUIRE(validateOutput.str().find("Package.Dep2-x64\x1b[0m [") == std::string::npos); + REQUIRE(validateOutput.str().find("Package.Dep2-x64 [") == std::string::npos); REQUIRE(validateOutput.str().find("ExternalDep") != std::string::npos); } \ No newline at end of file From 49c7feab2251364d8d09249b12780594211606c7 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 15 Jun 2021 14:09:03 -0300 Subject: [PATCH 17/82] move check for exp feature inside report function, create new DependenciesFlow --- .../AppInstallerCLICore.vcxproj | 2 + .../AppInstallerCLICore.vcxproj.filters | 6 ++ .../Commands/UninstallCommand.cpp | 11 +-- .../Commands/UpgradeCommand.cpp | 18 ++--- .../Workflows/DependenciesFlow.cpp | 67 ++++++++++++++++ .../Workflows/DependenciesFlow.h | 13 +++ .../Workflows/InstallFlow.cpp | 79 +++++++++---------- .../Workflows/ShowFlow.cpp | 49 ++++++------ .../Workflows/UninstallFlow.cpp | 66 ---------------- .../Workflows/UninstallFlow.h | 6 -- 10 files changed, 155 insertions(+), 162 deletions(-) create mode 100644 src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp create mode 100644 src/AppInstallerCLICore/Workflows/DependenciesFlow.h diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index 5e7333ed1f..cb22fcc8b1 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -248,6 +248,7 @@ + @@ -276,6 +277,7 @@ + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index a475b192b1..9e0a32f8c9 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -155,6 +155,9 @@ Public + + Workflows + @@ -277,6 +280,9 @@ Source Files + + Source Files + diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index 466d89a8b1..c6d3d0fd76 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -6,6 +6,7 @@ #include "Workflows/InstallFlow.h" #include "Workflows/CompletionFlow.h" #include "Workflows/WorkflowBase.h" +#include "Workflows/DependenciesFlow.h" #include "Resources.h" using AppInstaller::CLI::Execution::Args; @@ -127,14 +128,8 @@ namespace AppInstaller::CLI context << Workflow::GetInstalledPackageVersion << - Workflow::GetUninstallInfo; - - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - context << Workflow::ReportDependencies; - } - - context << + Workflow::GetUninstallInfo << + Workflow::ReportDependencies << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << Workflow::ReportExecutionStage(ExecutionStage::PostExecution); diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 98b5117f31..9a7e314a1f 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -149,13 +149,10 @@ namespace AppInstaller::CLI EnsureUpdateVersionApplicable << SelectInstaller << EnsureApplicableInstaller; - - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - context << ManageDependencies; - } - context << InstallPackageInstaller; + context << + ManageDependencies << + InstallPackageInstaller; } else { @@ -181,12 +178,9 @@ namespace AppInstaller::CLI context << SelectLatestApplicableUpdate(true); } - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - context << ManageDependencies; - } - - context << InstallPackageInstaller; + context << + ManageDependencies << + InstallPackageInstaller; } } } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp new file mode 100644 index 0000000000..124d560819 --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "DependenciesFlow.h" + +namespace AppInstaller::CLI::Workflow +{ + void ReportDependencies(Execution::Context& context) + { + context << + Workflow::GetManifest << + Workflow::SelectInstaller; + + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + const auto& installer = context.Get(); + if (installer && installer->Dependencies.HasAny()) + { + auto info = context.Reporter.Info(); + auto dependencies = installer->Dependencies; + + auto windowsFeaturesDep = dependencies.WindowsFeatures; + if (!windowsFeaturesDep.empty()) + { + info << " - Windows Features: "; + for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + { + info << " " << windowsFeaturesDep[i] << std::endl; + } + } + + auto windowsLibrariesDep = dependencies.WindowsLibraries; + if (!windowsLibrariesDep.empty()) + { + info << " - Windows Libraries: "; + for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + { + info << " " << windowsLibrariesDep[i] << std::endl; + } + } + + auto packageDep = dependencies.PackageDependencies; + if (!packageDep.empty()) + { + info << " - Packages: "; + for (size_t i = 0; i < packageDep.size(); i++) + { + info << " " << packageDep[i].Id; + if (!packageDep[i].MinVersion.empty()) info << " [>= " << packageDep[i].MinVersion << "]"; + info << std::endl; + } + } + + auto externalDependenciesDep = dependencies.ExternalDependencies; + if (!externalDependenciesDep.empty()) + { + info << " - Externals: "; + for (size_t i = 0; i < externalDependenciesDep.size(); i++) + { + info << " " << externalDependenciesDep[i] << std::endl; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h new file mode 100644 index 0000000000..6498ff3430 --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ExecutionContext.h" + +namespace AppInstaller::CLI::Workflow +{ + // Shows information about dependencies. + // Required Args: None + // Inputs: Manifest + // Outputs: None + void ReportDependencies(Execution::Context& context); +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 660430e668..4ff576ba58 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -407,98 +407,91 @@ namespace AppInstaller::CLI::Workflow { context << Workflow::SelectInstaller << - Workflow::EnsureApplicableInstaller; - - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - context << Workflow::ManageDependencies; - } - - context << Workflow::InstallPackageInstaller; + Workflow::EnsureApplicableInstaller << + Workflow::ManageDependencies << + Workflow::InstallPackageInstaller; } void ManageDependencies(Execution::Context& context) { - const auto& installer = context.Get(); - if (installer && installer->Dependencies.HasAny()) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - auto dependencies = installer->Dependencies; - context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; + const auto& installer = context.Get(); + if (installer && installer->Dependencies.HasAny()) + { + const auto& dependencies = installer->Dependencies; + context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; - ManageWindowsFeatureDependencies(dependencies.WindowsFeatures, context); + ManageWindowsFeatureDependencies(dependencies.WindowsFeatures, context); - ManageWindowsLibrariesDependencies(dependencies.WindowsLibraries, context); + ManageWindowsLibrariesDependencies(dependencies.WindowsLibraries, context); - ManagePackageDependencies(dependencies.PackageDependencies, context); + ManagePackageDependencies(dependencies.PackageDependencies, context); - ManageExternalDependencies(dependencies.ExternalDependencies, context); + ManageExternalDependencies(dependencies.ExternalDependencies, context); + } } } - void ManageWindowsFeatureDependencies(std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context) + void ManageWindowsFeatureDependencies(const std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context) { if (!windowsFeaturesDep.empty()) { context.Reporter.Info() << " - Windows Features: "; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + for (const auto& dep : windowsFeaturesDep) { - context.Reporter.Info() << windowsFeaturesDep[i]; - if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + context.Reporter.Info() << " " << dep << std::endl; } - context.Reporter.Info() << std::endl; } } - void ManageWindowsLibrariesDependencies(std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context) + void ManageWindowsLibrariesDependencies(const std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context) { if (!windowsLibrariesDep.empty()) { context.Reporter.Info() << " - Windows Libraries: "; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + for (const auto& dep : windowsLibrariesDep) { - context.Reporter.Info() << windowsLibrariesDep[i]; - if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + context.Reporter.Info() << " " << dep << std::endl; } - context.Reporter.Info() << std::endl; } } - void ManagePackageDependencies(std::vector& packageDep, AppInstaller::CLI::Execution::Context& context) + void ManagePackageDependencies(const std::vector& packageDep, AppInstaller::CLI::Execution::Context& context) { if (!packageDep.empty()) { - context.Reporter.Info() << " - Packages: "; - for (size_t i = 0; i < packageDep.size(); i++) + auto info = context.Reporter.Info(); + + info << " - Packages: "; + for (const auto& dep : packageDep) { - context.Reporter.Info() << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; - if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; + info << " " << dep.Id; + if (!dep.MinVersion.empty()) info << " [>= " << dep.MinVersion << "]"; + info << std::endl; } - context.Reporter.Info() << std::endl; } } - void ManageExternalDependencies(std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context) + void ManageExternalDependencies(const std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context) { if (!externalDependenciesDep.empty()) { context.Reporter.Info() << " - Externals: "; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) + for (const auto& dep : externalDependenciesDep) { - context.Reporter.Info() << externalDependenciesDep[i]; - if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + context.Reporter.Info() << " " << dep << std::endl; } - context.Reporter.Info() << std::endl; } } const struct PackagesAndInstallers { PackagesAndInstallers(std::optional inst, - AppInstaller::CLI::Execution::PackageToInstall pkg) : installer(inst), package(pkg) {} + AppInstaller::CLI::Execution::PackageToInstall pkg) : Installer(inst), Package(pkg) {} - std::optional installer; - AppInstaller::CLI::Execution::PackageToInstall package; + std::optional Installer; + AppInstaller::CLI::Execution::PackageToInstall Package; }; void InstallMultiple(Execution::Context& context) @@ -569,8 +562,8 @@ namespace AppInstaller::CLI::Workflow for (auto packageAndInstaller : installers) { - auto package = packageAndInstaller.package; - auto installer = packageAndInstaller.installer; + auto package = packageAndInstaller.Package; + auto installer = packageAndInstaller.Installer; auto installContextPtr = context.Clone(); Execution::Context& installContext = *installContextPtr; diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index c998e04f15..ea3f3932a9 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -71,58 +71,53 @@ namespace AppInstaller::CLI::Workflow } if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - auto dependencies = installer->Dependencies; + auto info = context.Reporter.Info(); + const auto& dependencies = installer->Dependencies; + if (dependencies.HasAny()) { - context.Reporter.Info() << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; + info << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; - auto windowsFeaturesDep = dependencies.WindowsFeatures; + const auto& windowsFeaturesDep = dependencies.WindowsFeatures; if (!windowsFeaturesDep.empty()) { - context.Reporter.Info() << " - Windows Features: "; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) + info << " - WindowsFeatures: "; + for (const auto& dep : windowsFeaturesDep) { - context.Reporter.Info() << windowsFeaturesDep[i]; - if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; + info << " " << dep << std::endl; } - context.Reporter.Info() << std::endl; } - auto windowsLibrariesDep = dependencies.WindowsLibraries; + const auto& windowsLibrariesDep = dependencies.WindowsLibraries; if (!windowsLibrariesDep.empty()) { - context.Reporter.Info() << " - Windows Libraries: "; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) + info << " - WindowsLibraries: "; + for (const auto& dep : windowsLibrariesDep) { - context.Reporter.Info() << windowsLibrariesDep[i]; - if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; + info << " " << dep << std::endl; } - context.Reporter.Info() << std::endl; } - auto packageDep = dependencies.PackageDependencies; + const auto& packageDep = dependencies.PackageDependencies; if (!packageDep.empty()) { - context.Reporter.Info() << " - Packages: "; - for (size_t i = 0; i < packageDep.size(); i++) + info << " - PackageDependencies: "; + for (const auto& dep : packageDep) { - context.Reporter.Info() << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; - if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; + info << " " << dep.Id; + if (!dep.MinVersion.empty()) info << " [>= " << dep.MinVersion << "]"; + info << std::endl; } - context.Reporter.Info() << std::endl; } - auto externalDependenciesDep = dependencies.ExternalDependencies; + const auto& externalDependenciesDep = dependencies.ExternalDependencies; if (!externalDependenciesDep.empty()) { - context.Reporter.Info() << " - Externals: "; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) + info << " - ExternalDependencies: "; + for (const auto& dep : externalDependenciesDep) { - context.Reporter.Info() << externalDependenciesDep[i]; - if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; + info << " " << dep << std::endl; } - context.Reporter.Info() << std::endl; } } } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index b8d148f842..b5fa852316 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -123,70 +123,4 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; } - - void ReportDependencies(Execution::Context& context) - { - context << - Workflow::GetManifest << - Workflow::SelectInstaller; - - const auto& installer = context.Get(); - if (installer) - { - auto dependencies = installer->Dependencies; - if (dependencies.HasAny()) - { - context.Reporter.Info() << "This package had dependencies that may not be needed anymore:" << std::endl; - - auto windowsFeaturesDep = dependencies.WindowsFeatures; - if (!windowsFeaturesDep.empty()) - { - context.Reporter.Info() << " - Windows Features: "; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) - { - context.Reporter.Info() << windowsFeaturesDep[i]; - if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - - auto windowsLibrariesDep = dependencies.WindowsLibraries; - if (!windowsLibrariesDep.empty()) - { - context.Reporter.Info() << " - Windows Libraries: "; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) - { - context.Reporter.Info() << windowsLibrariesDep[i]; - if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - - auto packageDep = dependencies.PackageDependencies; - if (!packageDep.empty()) - { - context.Reporter.Info() << " - Packages: "; - for (size_t i = 0; i < packageDep.size(); i++) - { - context.Reporter.Info() << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; - if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - - auto externalDependenciesDep = dependencies.ExternalDependencies; - if (!externalDependenciesDep.empty()) - { - context.Reporter.Info() << " - Externals: "; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) - { - context.Reporter.Info() << externalDependenciesDep[i]; - if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - } - } - } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h index 54d639e663..8a71a23040 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -22,10 +22,4 @@ namespace AppInstaller::CLI::Workflow // Inputs: PackageFamilyNames // Outputs: None void MsixUninstall(Execution::Context& context); - - // Shows information about dependencies. - // Required Args: None - // Inputs: Manifest - // Outputs: None - void ReportDependencies(Execution::Context& context); } \ No newline at end of file From 74d29ff8c171bc4d69e74d5d6df06a5554507bf0 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 15 Jun 2021 14:57:36 -0300 Subject: [PATCH 18/82] DependenciesFlow header and cpp inside Workflow --- src/AppInstallerCLICore/AppInstallerCLICore.vcxproj | 4 ++-- src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index cb22fcc8b1..52515b8426 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -248,7 +248,7 @@ - + @@ -277,7 +277,7 @@ - + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index 9e0a32f8c9..bb000f5408 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -155,7 +155,7 @@ Public - + Workflows @@ -280,7 +280,7 @@ Source Files - + Source Files From b7590c33595b5a676bd6be95bfd704229c676fe9 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 18 Jun 2021 13:07:54 -0300 Subject: [PATCH 19/82] =?UTF-8?q?=C2=96change=20representation=20of=20Depe?= =?UTF-8?q?ndency,=20create=20DependencyType=20and=20DependencyList;=20add?= =?UTF-8?q?=20Dependency=20to=20context=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/UninstallCommand.cpp | 1 + .../Commands/ValidateCommand.cpp | 83 ++--------------- .../ExecutionContextData.h | 9 +- .../Workflows/DependenciesFlow.cpp | 69 +++++--------- .../Workflows/InstallFlow.cpp | 90 ++----------------- .../Workflows/InstallFlow.h | 5 -- .../Workflows/ShowFlow.cpp | 43 +++------ .../Workflows/UninstallFlow.cpp | 10 +++ .../Workflows/UninstallFlow.h | 6 ++ .../AppInstallerCLITests.vcxproj | 1 + .../AppInstallerCLITests.vcxproj.filters | 3 + src/AppInstallerCLITests/YamlManifest.cpp | 39 +++++--- .../Manifest/ManifestYamlPopulator.cpp | 26 ++++-- .../Public/winget/ManifestCommon.h | 39 +++++--- .../Public/winget/ManifestInstaller.h | 2 +- .../Public/winget/ManifestYamlPopulator.h | 7 +- .../Schema/1_0/Json/ManifestDeserializer.cpp | 36 +++++--- .../Schema/1_0/Json/ManifestDeserializer.h | 2 +- 18 files changed, 182 insertions(+), 289 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index c6d3d0fd76..d84894613b 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -129,6 +129,7 @@ namespace AppInstaller::CLI context << Workflow::GetInstalledPackageVersion << Workflow::GetUninstallInfo << + Workflow::GetDependencies << Workflow::ReportDependencies << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index eeca9e20c2..6560df7ce5 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "ValidateCommand.h" #include "Workflows/WorkflowBase.h" +#include "Workflows/DependenciesFlow.h" #include "Resources.h" namespace AppInstaller::CLI @@ -31,63 +32,6 @@ namespace AppInstaller::CLI return "https://aka.ms/winget-command-validate"; } - void ShowWindowsFeatureDependencies(std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context) - { - if (!windowsFeaturesDep.empty()) - { - context.Reporter.Info() << " - Windows Features: "; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) - { - context.Reporter.Info() << windowsFeaturesDep[i]; - if (i < windowsFeaturesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - } - - void ShowWindowsLibrariesDependencies(std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context) - { - if (!windowsLibrariesDep.empty()) - { - context.Reporter.Info() << " - Windows Libraries: "; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) - { - context.Reporter.Info() << windowsLibrariesDep[i]; - if (i < windowsLibrariesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - } - - void ShowPackageDependencies(std::vector& packageDep, AppInstaller::CLI::Execution::Context& context) - { - if (!packageDep.empty()) - { - context.Reporter.Info() << " - Packages: "; - for (size_t i = 0; i < packageDep.size(); i++) - { - context.Reporter.Info() << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) context.Reporter.Info() << " [>= " << packageDep[i].MinVersion << "]"; - if (i < packageDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - } - - void ShowExternalDependencies(std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context) - { - if (!externalDependenciesDep.empty()) - { - context.Reporter.Info() << " - Externals: "; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) - { - context.Reporter.Info() << externalDependenciesDep[i]; - if (i < externalDependenciesDep.size() - 1) context.Reporter.Info() << ", "; - } - context.Reporter.Info() << std::endl; - } - } - void ValidateCommand::ExecuteInternal(Execution::Context& context) const { context << @@ -102,28 +46,13 @@ namespace AppInstaller::CLI if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - std::vector windowsFeaturesDep; - std::vector windowsLibrariesDep; - std::vector packageDep; - std::vector externalDep; - for (auto installer : manifest.Installers) - { - auto dependencies = installer.Dependencies; - windowsFeaturesDep.insert(windowsFeaturesDep.begin(), - dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); - windowsLibrariesDep.insert(windowsLibrariesDep.begin(), - dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); - packageDep.insert(packageDep.begin(), - dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); - externalDep.insert(externalDep.begin(), - dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); - } + Manifest::DependencyList allDependencies; + for (auto installer : manifest.Installers) { allDependencies.Add(installer.Dependencies); } + + context.Add(allDependencies); context.Reporter.Info() << "Manifest has the following dependencies:" << std::endl; - ShowWindowsFeatureDependencies(windowsFeaturesDep, context); - ShowWindowsLibrariesDependencies(windowsLibrariesDep, context); - ShowPackageDependencies(packageDep, context); - ShowExternalDependencies(externalDep, context); + context << Workflow::ReportDependencies; } context.Reporter.Info() << Resource::String::ManifestValidationSuccess << std::endl; diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index f6607dc470..5bce66227e 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -47,7 +47,8 @@ namespace AppInstaller::CLI::Execution // On import: Sources for the imported packages Sources, ARPSnapshot, - Max + Max, + Dependencies }; struct PackageToInstall @@ -184,5 +185,11 @@ namespace AppInstaller::CLI::Execution // Contains the { Id, Version, Channel } using value_t = std::vector>; }; + + template <> + struct DataMapping + { + using value_t = std::optional; + }; } } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 124d560819..194b5991b3 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -8,59 +8,36 @@ namespace AppInstaller::CLI::Workflow { void ReportDependencies(Execution::Context& context) { - context << - Workflow::GetManifest << - Workflow::SelectInstaller; - - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) return; + + const auto& dependencies = context.Get(); + if (dependencies && dependencies->HasAny()) { - const auto& installer = context.Get(); - if (installer && installer->Dependencies.HasAny()) - { - auto info = context.Reporter.Info(); - auto dependencies = installer->Dependencies; + auto info = context.Reporter.Info(); - auto windowsFeaturesDep = dependencies.WindowsFeatures; - if (!windowsFeaturesDep.empty()) - { - info << " - Windows Features: "; - for (size_t i = 0; i < windowsFeaturesDep.size(); i++) - { - info << " " << windowsFeaturesDep[i] << std::endl; - } - } + info << " - Windows Features: "; + for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep << std::endl; + } - auto windowsLibrariesDep = dependencies.WindowsLibraries; - if (!windowsLibrariesDep.empty()) - { - info << " - Windows Libraries: "; - for (size_t i = 0; i < windowsLibrariesDep.size(); i++) - { - info << " " << windowsLibrariesDep[i] << std::endl; - } - } + info << " - Windows Libraries: "; + for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep << std::endl; + } - auto packageDep = dependencies.PackageDependencies; - if (!packageDep.empty()) + info << " - Package: "; + for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::Package) { - info << " - Packages: "; - for (size_t i = 0; i < packageDep.size(); i++) - { - info << " " << packageDep[i].Id; - if (!packageDep[i].MinVersion.empty()) info << " [>= " << packageDep[i].MinVersion << "]"; - info << std::endl; - } + info << " " << dep.Id; + if (dep.MinVersion) info << " [>= " << dep.MinVersion << "]"; + info << std::endl; } + } - auto externalDependenciesDep = dependencies.ExternalDependencies; - if (!externalDependenciesDep.empty()) - { - info << " - Externals: "; - for (size_t i = 0; i < externalDependenciesDep.size(); i++) - { - info << " " << externalDependenciesDep[i] << std::endl; - } - } + info << " - External: "; + for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::External) info << " " << dep << std::endl; } } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 4ff576ba58..57d9347d15 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -7,6 +7,7 @@ #include "ShellExecuteInstallerHandler.h" #include "MSStoreInstallerHandler.h" #include "WorkflowBase.h" +#include "Workflows/DependenciesFlow.h" namespace AppInstaller::CLI::Workflow { @@ -419,68 +420,9 @@ namespace AppInstaller::CLI::Workflow const auto& installer = context.Get(); if (installer && installer->Dependencies.HasAny()) { - const auto& dependencies = installer->Dependencies; + context.Add(installer->Dependencies); context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; - - ManageWindowsFeatureDependencies(dependencies.WindowsFeatures, context); - - ManageWindowsLibrariesDependencies(dependencies.WindowsLibraries, context); - - ManagePackageDependencies(dependencies.PackageDependencies, context); - - ManageExternalDependencies(dependencies.ExternalDependencies, context); - } - } - } - - void ManageWindowsFeatureDependencies(const std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context) - { - if (!windowsFeaturesDep.empty()) - { - context.Reporter.Info() << " - Windows Features: "; - for (const auto& dep : windowsFeaturesDep) - { - context.Reporter.Info() << " " << dep << std::endl; - } - } - } - - void ManageWindowsLibrariesDependencies(const std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context) - { - if (!windowsLibrariesDep.empty()) - { - context.Reporter.Info() << " - Windows Libraries: "; - for (const auto& dep : windowsLibrariesDep) - { - context.Reporter.Info() << " " << dep << std::endl; - } - } - } - - void ManagePackageDependencies(const std::vector& packageDep, AppInstaller::CLI::Execution::Context& context) - { - if (!packageDep.empty()) - { - auto info = context.Reporter.Info(); - - info << " - Packages: "; - for (const auto& dep : packageDep) - { - info << " " << dep.Id; - if (!dep.MinVersion.empty()) info << " [>= " << dep.MinVersion << "]"; - info << std::endl; - } - } - } - - void ManageExternalDependencies(const std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context) - { - if (!externalDependenciesDep.empty()) - { - context.Reporter.Info() << " - Externals: "; - for (const auto& dep : externalDependenciesDep) - { - context.Reporter.Info() << " " << dep << std::endl; + context << Workflow::ReportDependencies; } } } @@ -498,10 +440,7 @@ namespace AppInstaller::CLI::Workflow { bool allSucceeded = true; - std::vector windowsFeaturesDep; - std::vector windowsLibrariesDep; - std::vector packageDep; - std::vector externalDep; + DependencyList allDependencies; std::vector installers; @@ -520,7 +459,6 @@ namespace AppInstaller::CLI::Workflow // TODO: In the future, it would be better to not have to convert back and forth from a string installContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(package.PackageRequest.Scope)); - //installContexts.push_back(installContext); installContext << Workflow::SelectInstaller; @@ -534,29 +472,17 @@ namespace AppInstaller::CLI::Workflow if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - if (installer) { - auto dependencies = installer->Dependencies; - windowsFeaturesDep.insert(windowsFeaturesDep.begin(), - dependencies.WindowsFeatures.begin(), dependencies.WindowsFeatures.end()); - windowsLibrariesDep.insert(windowsLibrariesDep.begin(), - dependencies.WindowsLibraries.begin(), dependencies.WindowsLibraries.end()); - packageDep.insert(packageDep.begin(), - dependencies.PackageDependencies.begin(), dependencies.PackageDependencies.end()); - externalDep.insert(externalDep.begin(), - dependencies.ExternalDependencies.begin(), dependencies.ExternalDependencies.end()); - } + if (installer) allDependencies.Add(installer->Dependencies); } } if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - if (!windowsFeaturesDep.empty() || !windowsLibrariesDep.empty() || !packageDep.empty() || !externalDep.empty()) + if (allDependencies.HasAny()) { + context.Add(allDependencies); context.Reporter.Info() << "The packages found in this import have the following dependencies:" << std::endl; - ManageWindowsFeatureDependencies(windowsFeaturesDep, context); - ManageWindowsLibrariesDependencies(windowsLibrariesDep, context); - ManagePackageDependencies(packageDep, context); - ManageExternalDependencies(externalDep, context); + context << Workflow::ReportDependencies; } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 674faade7d..65f4085445 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -95,11 +95,6 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void ManageDependencies(Execution::Context& context); - void ManageExternalDependencies(std::vector& externalDependenciesDep, AppInstaller::CLI::Execution::Context& context); - void ManagePackageDependencies(std::vector& packageDep, AppInstaller::CLI::Execution::Context& context); - void ManageWindowsLibrariesDependencies(std::vector& windowsLibrariesDep, AppInstaller::CLI::Execution::Context& context); - void ManageWindowsFeatureDependencies(std::vector& windowsFeaturesDep, AppInstaller::CLI::Execution::Context& context); - // Installs multiple packages. // Required Args: None // Inputs: Manifests diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index ea3f3932a9..327bb7b0d7 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -78,46 +78,29 @@ namespace AppInstaller::CLI::Workflow { info << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; - const auto& windowsFeaturesDep = dependencies.WindowsFeatures; - if (!windowsFeaturesDep.empty()) - { - info << " - WindowsFeatures: "; - for (const auto& dep : windowsFeaturesDep) - { - info << " " << dep << std::endl; - } + info << " - WindowsFeatures: "; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep << std::endl; } - const auto& windowsLibrariesDep = dependencies.WindowsLibraries; - if (!windowsLibrariesDep.empty()) - { - info << " - WindowsLibraries: "; - for (const auto& dep : windowsLibrariesDep) - { - info << " " << dep << std::endl; - } + info << " - WindowsLibraries: "; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep << std::endl; } - const auto& packageDep = dependencies.PackageDependencies; - if (!packageDep.empty()) - { - info << " - PackageDependencies: "; - for (const auto& dep : packageDep) + info << " - PackageDependencies: "; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::Package) { info << " " << dep.Id; - if (!dep.MinVersion.empty()) info << " [>= " << dep.MinVersion << "]"; + if (dep.MinVersion) info << " [>= " << dep.MinVersion << "]"; info << std::endl; } } - const auto& externalDependenciesDep = dependencies.ExternalDependencies; - if (!externalDependenciesDep.empty()) - { - info << " - ExternalDependencies: "; - for (const auto& dep : externalDependenciesDep) - { - info << " " << dep << std::endl; - } + info << " - ExternalDependencies: "; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::External) info << " " << dep << std::endl; } } } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index b5fa852316..898b256b77 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -122,5 +122,15 @@ namespace AppInstaller::CLI::Workflow } context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; + } + + void GetDependencies(Execution::Context& context) + { + const auto& installer = context.Get(); + if (installer && installer->Dependencies.HasAny()) + { + context.Add(installer->Dependencies); + context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; + } } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h index 8a71a23040..872156b7b1 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -22,4 +22,10 @@ namespace AppInstaller::CLI::Workflow // Inputs: PackageFamilyNames // Outputs: None void MsixUninstall(Execution::Context& context); + + // Gathers dependencies from installer. + // Required Args: None + // Inputs: Installer + // Outputs: None + void GetDependencies(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index cb35bdc9fd..695120d610 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -180,6 +180,7 @@ + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 0eaf55980f..560f05e82c 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -39,6 +39,9 @@ Header Files + + Header Files + diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index ed67a8b27e..1a3cc28451 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "YamlManifest.h" using namespace TestCommon; using namespace AppInstaller::Manifest; @@ -382,12 +383,11 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "msix", "appxbundle", "msixbundle" }); auto dependencies = manifest.DefaultInstallerInfo.Dependencies; - REQUIRE(dependencies.WindowsFeatures == MultiValue{ "IIS" }); - REQUIRE(dependencies.WindowsLibraries == MultiValue{ "VC Runtime" }); - REQUIRE(dependencies.PackageDependencies.size() == 1); - REQUIRE(dependencies.PackageDependencies[0].Id == "Microsoft.MsixSdkDep"); - REQUIRE(dependencies.PackageDependencies[0].MinVersion == "1.0.0"); - REQUIRE(dependencies.ExternalDependencies == MultiValue{ "Outside dependencies" }); + REQUIRE(HasDependency(dependencies, DependencyType::WindowsFeature, "IIS")); + REQUIRE(HasDependency(dependencies, DependencyType::WindowsLibraries, "VC Runtime")); + REQUIRE(HasDependency(dependencies, DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0")); + REQUIRE(HasDependency(dependencies, DependencyType::External, "Outside dependencies")); + REQUIRE(dependencies.dependencies.size() == 4); REQUIRE(manifest.DefaultInstallerInfo.Capabilities == MultiValue{ "internetClient" }); REQUIRE(manifest.DefaultInstallerInfo.RestrictedCapabilities == MultiValue{ "runFullTrust" }); @@ -430,11 +430,11 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) REQUIRE(installer1.FileExtensions == MultiValue{ "appxbundle", "msixbundle", "appx", "msix" }); auto installer1Dependencies = installer1.Dependencies; - REQUIRE(installer1Dependencies.WindowsFeatures == MultiValue{ "PreviewIIS" }); - REQUIRE(installer1Dependencies.WindowsLibraries == MultiValue{ "Preview VC Runtime" }); - REQUIRE(installer1Dependencies.PackageDependencies.size() == 1); - REQUIRE(installer1Dependencies.PackageDependencies[0].Id == "Microsoft.MsixSdkDepPreview"); - REQUIRE(installer1Dependencies.ExternalDependencies == MultiValue{ "Preview Outside dependencies" }); + REQUIRE(HasDependency(installer1Dependencies, DependencyType::WindowsFeature, "PreviewIIS")); + REQUIRE(HasDependency(installer1Dependencies, DependencyType::WindowsLibraries, "Preview VC Runtime")); + REQUIRE(HasDependency(installer1Dependencies, DependencyType::Package, "Microsoft.MsixSdkDepPreview")); + REQUIRE(HasDependency(installer1Dependencies, DependencyType::External, "Preview Outside dependencies")); + REQUIRE(installer1Dependencies.dependencies.size()==4); REQUIRE(installer1.Capabilities == MultiValue{ "internetClientPreview" }); REQUIRE(installer1.RestrictedCapabilities == MultiValue{ "runFullTrustPreview" }); @@ -470,6 +470,23 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) } } +bool HasDependency(DependencyList dependencies, DependencyType type, string_t id, string_t minVersion = "") +{ + for (const auto& dep : dependencies.dependencies) + { + if (dep.Type == type && dep.Id == id) + { + if (dep.MinVersion) { + if (dep.MinVersion == minVersion) return true; + } + else { + return true; + } + } + } + return false; +} + TEST_CASE("ValidateV1GoodManifestAndVerifyContents", "[ManifestValidation]") { TempDirectory singletonDirectory{ "SingletonManifest" }; diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 986368ad71..0f4bf67d92 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -228,7 +228,7 @@ namespace AppInstaller::Manifest { "Commands", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->Commands = ProcessStringSequenceNode(value); return {}; } }, { "Protocols", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->Protocols = ProcessStringSequenceNode(value); return {}; } }, { "FileExtensions", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->FileExtensions = ProcessStringSequenceNode(value); return {}; } }, - { "Dependencies", [this](const YAML::Node& value)->ValidationErrors { m_p_dependency = &(m_p_installer->Dependencies); return ValidateAndProcessFields(value, DependenciesFieldInfos); } }, + { "Dependencies", [this](const YAML::Node& value)->ValidationErrors { m_p_dependencyList = &(m_p_installer->Dependencies); return ValidateAndProcessFields(value, DependenciesFieldInfos); } }, { "Capabilities", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->Capabilities = ProcessStringSequenceNode(value); return {}; } }, { "RestrictedCapabilities", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->RestrictedCapabilities = ProcessStringSequenceNode(value); return {}; } }, }; @@ -355,16 +355,25 @@ namespace AppInstaller::Manifest { result = { - { "WindowsFeatures", [this](const YAML::Node& value)->ValidationErrors { m_p_dependency->WindowsFeatures = ProcessStringSequenceNode(value); return {}; } }, - { "WindowsLibraries", [this](const YAML::Node& value)->ValidationErrors { m_p_dependency->WindowsLibraries = ProcessStringSequenceNode(value); return {}; } }, - { "PackageDependencies", [this](const YAML::Node& value)->ValidationErrors { return ProcessPackageDependenciesNode(value, m_p_dependency->PackageDependencies); } }, - { "ExternalDependencies", [this](const YAML::Node& value)->ValidationErrors { m_p_dependency->ExternalDependencies = ProcessStringSequenceNode(value); return {}; } }, + { "WindowsFeatures", [this](const YAML::Node& value)->ValidationErrors { ProcessDependenciesNode(DependencyType::WindowsFeature, value); return {}; } }, + { "WindowsLibraries", [this](const YAML::Node& value)->ValidationErrors { ProcessDependenciesNode(DependencyType::WindowsLibraries, value); return {}; } }, + { "PackageDependencies", [this](const YAML::Node& value)->ValidationErrors { ProcessPackageDependenciesNode(value); return {}; } }, + { "ExternalDependencies", [this](const YAML::Node& value)->ValidationErrors { ProcessDependenciesNode(DependencyType::External, value); return {}; } }, }; } return result; } + void ManifestYamlPopulator::ProcessDependenciesNode(DependencyType type, const YAML::Node& node) + { + const auto& ids = ProcessStringSequenceNode(node); + for (auto id : ids) + { + m_p_dependencyList->Add(Dependency(type, id)); + } + } + std::vector ManifestYamlPopulator::GetPackageDependenciesFieldProcessInfo(const ManifestVer& manifestVersion) { std::vector result = {}; @@ -450,18 +459,17 @@ namespace AppInstaller::Manifest return resultErrors; } - ValidationErrors ManifestYamlPopulator::ProcessPackageDependenciesNode(const YAML::Node& rootNode, std::vector& packageDependencies) + ValidationErrors ManifestYamlPopulator::ProcessPackageDependenciesNode(const YAML::Node& rootNode) { ValidationErrors resultErrors; - packageDependencies.clear(); for (auto const& entry : rootNode.Sequence()) { - PackageDependency packageDependency; + Dependency packageDependency = Dependency(DependencyType::Package); m_p_packageDependency = &packageDependency; auto errors = ValidateAndProcessFields(entry, PackageDependenciesFieldInfos); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); - packageDependencies.emplace_back(std::move(std::move(packageDependency))); + m_p_dependencyList->Add(std::move(std::move(packageDependency))); } return resultErrors; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 3701115c49..437a81b2aa 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -112,25 +112,40 @@ namespace AppInstaller::Manifest Preview, }; - struct PackageDependency + enum class DependencyType { - string_t Id; - string_t MinVersion; + WindowsFeature, + WindowsLibraries, + Package, + External }; struct Dependency { - std::vector WindowsFeatures; - std::vector WindowsLibraries; - std::vector PackageDependencies; - std::vector ExternalDependencies; - - bool HasAny() const { - return !WindowsFeatures.empty() || !WindowsLibraries.empty() || !PackageDependencies.empty() || - !ExternalDependencies.empty(); - } + DependencyType Type; + string_t Id; + std::optional MinVersion; + + Dependency(DependencyType type, string_t id, std::optional minVersion) : Type(type), Id(id), MinVersion(minVersion) {} + Dependency(DependencyType type, string_t id) : Type(type), Id(id) {} + Dependency(DependencyType type) : Type(type) {} }; + struct DependencyList + { + std::vector dependencies; + + DependencyList() {} + + void Add(Dependency dep) { dependencies.push_back(dep); } + void Add(DependencyList dependencyList) + { + const auto& deps = dependencyList.dependencies; + dependencies.insert(deps.end(), deps.begin(), deps.end()); + } + + bool HasAny() const { return !dependencies.empty(); } + }; InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h index 7696fd1d35..8579296c37 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h @@ -71,6 +71,6 @@ namespace AppInstaller::Manifest // For msix only std::vector RestrictedCapabilities; - Dependency Dependencies; + DependencyList Dependencies; }; } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h b/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h index 886cf2184c..59c3a4872d 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h @@ -37,8 +37,8 @@ namespace AppInstaller::Manifest AppInstaller::Manifest::Manifest* m_p_manifest = nullptr; AppInstaller::Manifest::ManifestInstaller* m_p_installer = nullptr; std::map* m_p_switches = nullptr; - AppInstaller::Manifest::Dependency* m_p_dependency = nullptr; - AppInstaller::Manifest::PackageDependency* m_p_packageDependency = nullptr; + AppInstaller::Manifest::DependencyList* m_p_dependencyList = nullptr; + AppInstaller::Manifest::Dependency* m_p_packageDependency = nullptr; AppInstaller::Manifest::ManifestLocalization* m_p_localization = nullptr; // Cache of Installers node and Localization node @@ -60,7 +60,8 @@ namespace AppInstaller::Manifest const YAML::Node& rootNode, const std::vector& fieldInfos); - std::vector ProcessPackageDependenciesNode(const YAML::Node& rootNode, std::vector& packageDependencies); + void ProcessDependenciesNode(DependencyType type, const YAML::Node& rootNode); + std::vector ProcessPackageDependenciesNode(const YAML::Node& rootNode); std::vector PopulateManifestInternal(const YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, bool fullValidation); }; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp index 6a7120c734..0e6d1670d5 100644 --- a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp +++ b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp @@ -413,10 +413,10 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0::Json JsonHelper::GetJsonValueFromNode(installerJsonObject, JsonHelper::GetUtilityString(Dependencies)); if (dependenciesObject) { - std::optional dependency = DeserializeDependency(dependenciesObject.value().get()); - if (dependency) + std::optional dependencyList = DeserializeDependency(dependenciesObject.value().get()); + if (dependencyList) { - installer.Dependencies = std::move(dependency.value()); + installer.Dependencies = std::move(dependencyList.value()); } } @@ -428,18 +428,32 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0::Json return installer; } - std::optional ManifestDeserializer::DeserializeDependency(const web::json::value& dependenciesObject) const + std::optional ManifestDeserializer::DeserializeDependency(const web::json::value& dependenciesObject) const { if (dependenciesObject.is_null()) { return {}; } - Manifest::Dependency dependency; + Manifest::DependencyList dependencyList; - dependency.WindowsFeatures = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsFeatures))); - dependency.WindowsLibraries = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsLibraries))); - dependency.ExternalDependencies = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(ExternalDependencies))); + const auto& wfIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsFeatures))); + for (auto id : wfIds) + { + dependencyList.Add(Dependency(DependencyType::WindowsFeature, id)); + }; + + const auto& wlIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsLibraries))); + for (auto id : wlIds) + { + dependencyList.Add(Dependency(DependencyType::WindowsLibraries, id)); + }; + + const auto& extIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(ExternalDependencies))); + for (auto id : extIds) + { + dependencyList.Add(Dependency(DependencyType::External, id)); + }; // Package Dependencies std::optional> packageDependencies = JsonHelper::GetRawJsonArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(PackageDependencies)); @@ -450,12 +464,12 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0::Json std::optional id = JsonHelper::GetRawStringValueFromJsonNode(packageDependency, JsonHelper::GetUtilityString(PackageIdentifier)); if (id) { - PackageDependency pkg{ std::move(id.value()) , JsonHelper::GetRawStringValueFromJsonNode(packageDependency, JsonHelper::GetUtilityString(MinimumVersion)).value_or("") }; - dependency.PackageDependencies.emplace_back(std::move(pkg)); + Dependency pkg{ DependencyType::Package, std::move(id.value()) , JsonHelper::GetRawStringValueFromJsonNode(packageDependency, JsonHelper::GetUtilityString(MinimumVersion)).value_or("") }; + dependencyList.Add(std::move(pkg)); } } } - return dependency; + return dependencyList; } } diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.h index f3d6de7cc0..a61b6b6f65 100644 --- a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.h +++ b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.h @@ -19,6 +19,6 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0::Json std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const; - std::optional DeserializeDependency(const web::json::value& dependenciesJsonObject) const; + std::optional DeserializeDependency(const web::json::value& dependenciesJsonObject) const; }; } From b04225a46eaadd962d6cd6644121b5cc280ea55a Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 18 Jun 2021 15:45:06 -0300 Subject: [PATCH 20/82] dependencies context data is of type DependencyList (not optional) --- .../Commands/ValidateCommand.cpp | 2 +- .../ExecutionContextData.h | 6 ++--- .../Workflows/DependenciesFlow.cpp | 26 +++++++++---------- .../Workflows/ShowFlow.cpp | 16 ++++++------ src/AppInstallerCLITests/YamlManifest.cpp | 2 +- .../Public/winget/ManifestCommon.h | 8 ++++++ 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index 6560df7ce5..828ad5e9cd 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -51,7 +51,7 @@ namespace AppInstaller::CLI context.Add(allDependencies); - context.Reporter.Info() << "Manifest has the following dependencies:" << std::endl; + context.Reporter.Info() << "Manifest has the following dependencies that were not validated, ensure that they are good :" << std::endl; context << Workflow::ReportDependencies; } diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index 5bce66227e..e0d8b63d57 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -47,8 +47,8 @@ namespace AppInstaller::CLI::Execution // On import: Sources for the imported packages Sources, ARPSnapshot, - Max, - Dependencies + Dependencies, + Max }; struct PackageToInstall @@ -189,7 +189,7 @@ namespace AppInstaller::CLI::Execution template <> struct DataMapping { - using value_t = std::optional; + using value_t = Manifest::DependencyList; }; } } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 194b5991b3..bfa7c1ebdf 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -11,33 +11,33 @@ namespace AppInstaller::CLI::Workflow if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) return; const auto& dependencies = context.Get(); - if (dependencies && dependencies->HasAny()) + if (dependencies.HasAny()) { auto info = context.Reporter.Info(); - info << " - Windows Features: "; - for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep << std::endl; + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - Windows Features: " << std::endl; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep.Id << std::endl; } - info << " - Windows Libraries: "; - for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep << std::endl; + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - Windows Libraries: " << std::endl; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep.Id << std::endl; } - info << " - Package: "; - for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList + if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - Package: " << std::endl; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList if (dep.Type == Manifest::DependencyType::Package) { info << " " << dep.Id; - if (dep.MinVersion) info << " [>= " << dep.MinVersion << "]"; + if (dep.MinVersion) info << " [>= " << dep.MinVersion.value() << "]"; info << std::endl; } } - info << " - External: "; - for (const auto& dep : dependencies->dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::External) info << " " << dep << std::endl; + if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - External: " << std::endl; + for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dep.Type == Manifest::DependencyType::External) info << " " << dep.Id << std::endl; } } } diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 327bb7b0d7..8332adf55d 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -78,29 +78,29 @@ namespace AppInstaller::CLI::Workflow { info << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; - info << " - WindowsFeatures: "; + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - WindowsFeatures: " << std::endl; for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep << std::endl; + if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep.Id << std::endl; } - info << " - WindowsLibraries: "; + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - WindowsLibraries: " << std::endl; for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep << std::endl; + if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep.Id << std::endl; } - info << " - PackageDependencies: "; + if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - PackageDependencies: " << std::endl; for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList if (dep.Type == Manifest::DependencyType::Package) { info << " " << dep.Id; - if (dep.MinVersion) info << " [>= " << dep.MinVersion << "]"; + if (dep.MinVersion) info << " [>= " << dep.MinVersion.value() << "]"; info << std::endl; } } - info << " - ExternalDependencies: "; + if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - ExternalDependencies: " << std::endl; for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::External) info << " " << dep << std::endl; + if (dep.Type == Manifest::DependencyType::External) info << " " << dep.Id << std::endl; } } } diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 1a3cc28451..93d8a99d7f 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -477,7 +477,7 @@ bool HasDependency(DependencyList dependencies, DependencyType type, string_t id if (dep.Type == type && dep.Id == id) { if (dep.MinVersion) { - if (dep.MinVersion == minVersion) return true; + if (dep.MinVersion.value() == minVersion) return true; } else { return true; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 437a81b2aa..9afc90959d 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -145,6 +145,14 @@ namespace AppInstaller::Manifest } bool HasAny() const { return !dependencies.empty(); } + bool HasAnyOf(DependencyType type) const + { + for (const auto& dep : dependencies) + { + if (dep.Type == type) return true; + }; + return false; + } }; InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); From 2a620d87ba1cff57fc5a46301a5e6a53b2e516fe Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 18 Jun 2021 18:54:16 -0300 Subject: [PATCH 21/82] fix spelling errors --- .../Workflows/DependenciesFlow.cpp | 20 +++++++++---------- .../Workflows/ShowFlow.cpp | 20 +++++++++---------- src/AppInstallerCLITests/YamlManifest.cpp | 8 ++++---- .../Public/winget/ManifestCommon.h | 10 +++++----- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index bfa7c1ebdf..04df49f7d2 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -16,28 +16,28 @@ namespace AppInstaller::CLI::Workflow auto info = context.Reporter.Info(); if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - Windows Features: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep.Id << std::endl; + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::WindowsFeature) info << " " << dependency.Id << std::endl; } if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - Windows Libraries: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep.Id << std::endl; + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dependency.Id << std::endl; } if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - Package: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::Package) + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::Package) { - info << " " << dep.Id; - if (dep.MinVersion) info << " [>= " << dep.MinVersion.value() << "]"; + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; info << std::endl; } } if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - External: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::External) info << " " << dep.Id << std::endl; + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::External) info << " " << dependency.Id << std::endl; } } } diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 8332adf55d..5a2c5bb691 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -79,28 +79,28 @@ namespace AppInstaller::CLI::Workflow info << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - WindowsFeatures: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsFeature) info << " " << dep.Id << std::endl; + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::WindowsFeature) info << " " << dependency.Id << std::endl; } if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - WindowsLibraries: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dep.Id << std::endl; + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dependency.Id << std::endl; } if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - PackageDependencies: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::Package) + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::Package) { - info << " " << dep.Id; - if (dep.MinVersion) info << " [>= " << dep.MinVersion.value() << "]"; + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; info << std::endl; } } if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - ExternalDependencies: " << std::endl; - for (const auto& dep : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dep.Type == Manifest::DependencyType::External) info << " " << dep.Id << std::endl; + for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList + if (dependency.Type == Manifest::DependencyType::External) info << " " << dependency.Id << std::endl; } } } diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 93d8a99d7f..648c422c78 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -472,12 +472,12 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) bool HasDependency(DependencyList dependencies, DependencyType type, string_t id, string_t minVersion = "") { - for (const auto& dep : dependencies.dependencies) + for (const auto& dependency : dependencies.dependencies) { - if (dep.Type == type && dep.Id == id) + if (dependency.Type == type && dependency.Id == id) { - if (dep.MinVersion) { - if (dep.MinVersion.value() == minVersion) return true; + if (dependency.MinVersion) { + if (dependency.MinVersion.value() == minVersion) return true; } else { return true; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 9afc90959d..6cec90e33f 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -137,19 +137,19 @@ namespace AppInstaller::Manifest DependencyList() {} - void Add(Dependency dep) { dependencies.push_back(dep); } + void Add(Dependency dependency) { dependencies.push_back(dependency); } void Add(DependencyList dependencyList) { - const auto& deps = dependencyList.dependencies; - dependencies.insert(deps.end(), deps.begin(), deps.end()); + const auto& dependenciesToAdd = dependencyList.dependencies; + dependencies.insert(dependenciesToAdd.end(), dependenciesToAdd.begin(), dependenciesToAdd.end()); } bool HasAny() const { return !dependencies.empty(); } bool HasAnyOf(DependencyType type) const { - for (const auto& dep : dependencies) + for (const auto& dependency : dependencies) { - if (dep.Type == type) return true; + if (dependency.Type == type) return true; }; return false; } From 40e05b44b5d465a9f09d98037d117a1afa0be1e2 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 18 Jun 2021 20:22:43 -0300 Subject: [PATCH 22/82] ApplyTo function, can receibe a lambda function to apply on specific DpeendencyType --- .../Workflows/DependenciesFlow.cpp | 33 +++++++------------ .../Workflows/ShowFlow.cpp | 25 +++++--------- .../Public/winget/ManifestCommon.h | 16 +++++++-- 3 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 04df49f7d2..fa57a10a12 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -15,30 +15,21 @@ namespace AppInstaller::CLI::Workflow { auto info = context.Reporter.Info(); - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - Windows Features: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::WindowsFeature) info << " " << dependency.Id << std::endl; - } + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - Windows Features: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - Windows Libraries: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dependency.Id << std::endl; - } + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - Windows Libraries: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); - if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - Package: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::Package) - { - info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; - info << std::endl; - } - } + if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - Package: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + info << std::endl; + }); - if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - External: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::External) info << " " << dependency.Id << std::endl; - } + if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - External: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); } } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 5a2c5bb691..d8df6c7d74 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -79,29 +79,20 @@ namespace AppInstaller::CLI::Workflow info << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - WindowsFeatures: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::WindowsFeature) info << " " << dependency.Id << std::endl; - } + dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - WindowsLibraries: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::WindowsLibraries) info << " " << dependency.Id << std::endl; - } + dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - PackageDependencies: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::Package) - { - info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; - info << std::endl; - } - } + dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + info << std::endl; + }); if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - ExternalDependencies: " << std::endl; - for (const auto& dependency : dependencies.dependencies) { //TODO change for lambda function called inside DepList - if (dependency.Type == Manifest::DependencyType::External) info << " " << dependency.Id << std::endl; - } + dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); } } } diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 6cec90e33f..0557864766 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -138,10 +138,12 @@ namespace AppInstaller::Manifest DependencyList() {} void Add(Dependency dependency) { dependencies.push_back(dependency); } - void Add(DependencyList dependencyList) + void Add(DependencyList otherDependencyList) { - const auto& dependenciesToAdd = dependencyList.dependencies; - dependencies.insert(dependenciesToAdd.end(), dependenciesToAdd.begin(), dependenciesToAdd.end()); + for (const auto& dependency : otherDependencyList.dependencies) + { + dependencies.push_back(dependency); + } } bool HasAny() const { return !dependencies.empty(); } @@ -153,6 +155,14 @@ namespace AppInstaller::Manifest }; return false; } + + void ApplyToType(DependencyType type, std::function func) const + { + for (const auto& dependency : dependencies) + { + if (dependency.Type == type) func(dependency); + } + } }; InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); From d8c411f18cd6ecf8b3d0857ad940dd6b9a3a3910 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 22 Jun 2021 14:04:45 -0300 Subject: [PATCH 23/82] localize user strings, code style changes, validate report dependencies task divided, creates ValidateFlow --- .../AppInstallerCLICore.vcxproj | 2 + .../AppInstallerCLICore.vcxproj.filters | 6 +++ .../Commands/UninstallCommand.cpp | 2 +- .../Commands/UpgradeCommand.cpp | 8 ++-- .../Commands/ValidateCommand.cpp | 15 +++---- src/AppInstallerCLICore/Resources.h | 10 ++++- .../Workflows/DependenciesFlow.cpp | 42 +++++++++++++------ .../Workflows/InstallFlow.cpp | 12 +++--- .../Workflows/InstallFlow.h | 4 +- .../Workflows/ShowFlow.cpp | 36 ++++++++++------ .../Workflows/UninstallFlow.cpp | 4 +- .../Workflows/UninstallFlow.h | 2 +- .../Workflows/UpdateFlow.cpp | 2 +- .../Workflows/ValidateFlow.cpp | 26 ++++++++++++ .../Workflows/ValidateFlow.h | 13 ++++++ .../Shared/Strings/en-us/winget.resw | 28 +++++++++++++ .../AppInstallerCLITests.vcxproj | 16 +++---- .../AppInstallerCLITests.vcxproj.filters | 23 ++++++---- .../RestInterface_1_0.cpp | 10 ++--- src/AppInstallerCLITests/WorkFlow.cpp | 10 ++--- src/AppInstallerCLITests/YamlManifest.cpp | 35 ++++++++-------- 21 files changed, 204 insertions(+), 102 deletions(-) create mode 100644 src/AppInstallerCLICore/Workflows/ValidateFlow.cpp create mode 100644 src/AppInstallerCLICore/Workflows/ValidateFlow.h diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index 52515b8426..c6d264088c 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -263,6 +263,7 @@ + @@ -317,6 +318,7 @@ + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index bb000f5408..ee8da9ee29 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -158,6 +158,9 @@ Workflows + + Header Files + @@ -283,6 +286,9 @@ Source Files + + Source Files + diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index d84894613b..b2780d5bfc 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -129,7 +129,7 @@ namespace AppInstaller::CLI context << Workflow::GetInstalledPackageVersion << Workflow::GetUninstallInfo << - Workflow::GetDependencies << + Workflow::GetDependenciesInfo << Workflow::ReportDependencies << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 9a7e314a1f..4d3fcc353c 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -148,10 +148,8 @@ namespace AppInstaller::CLI GetInstalledPackageVersion << EnsureUpdateVersionApplicable << SelectInstaller << - EnsureApplicableInstaller; - - context << - ManageDependencies << + EnsureApplicableInstaller << + GetInstallerDependencies << InstallPackageInstaller; } else @@ -179,7 +177,7 @@ namespace AppInstaller::CLI } context << - ManageDependencies << + GetInstallerDependencies << InstallPackageInstaller; } } diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index 828ad5e9cd..9bf5b00d97 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -4,6 +4,7 @@ #include "ValidateCommand.h" #include "Workflows/WorkflowBase.h" #include "Workflows/DependenciesFlow.h" +#include "Workflows/ValidateFlow.h" #include "Resources.h" namespace AppInstaller::CLI @@ -44,16 +45,10 @@ namespace AppInstaller::CLI { auto manifest = Manifest::YamlParser::CreateFromPath(inputFile, true, true); - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - Manifest::DependencyList allDependencies; - for (auto installer : manifest.Installers) { allDependencies.Add(installer.Dependencies); } - - context.Add(allDependencies); - - context.Reporter.Info() << "Manifest has the following dependencies that were not validated, ensure that they are good :" << std::endl; - context << Workflow::ReportDependencies; - } + context.Add(manifest); + context << + Workflow::GetDependenciesFromManifest << + Workflow::ReportDependencies; context.Reporter.Info() << Resource::String::ManifestValidationSuccess << std::endl; } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index ffca60870b..fc992bc902 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -46,6 +46,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ExportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExportIncludeVersionsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExportSourceArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ExternalDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ExtraPositionalError); WINGET_DEFINE_RESOURCE_STRINGID(FeatureDisabledMessage); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesCommandLongDescription); @@ -68,6 +69,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(HelpLinkPreamble); WINGET_DEFINE_RESOURCE_STRINGID(IdArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandLongDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportFileArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportFileHasInvalidSchema); @@ -82,6 +84,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimerMSStore); WINGET_DEFINE_RESOURCE_STRINGID(InstallationRequiresHigherWindows); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandLongDescription); + WINGET_DEFINE_RESOURCE_STRINGID(InstallAndUpgradeCommandsReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageNotAvailable); WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageVersionNotAvailable); @@ -147,6 +150,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(OutputFileArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OverrideArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(Package); + WINGET_DEFINE_RESOURCE_STRINGID(PackageDependencies); WINGET_DEFINE_RESOURCE_STRINGID(PendingWorkError); WINGET_DEFINE_RESOURCE_STRINGID(PoliciesDisabled); WINGET_DEFINE_RESOURCE_STRINGID(PoliciesEnabled); @@ -170,8 +174,8 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(SettingLoadFailure); WINGET_DEFINE_RESOURCE_STRINGID(SettingsCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsCommandShortDescription); - WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarnings); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningField); + WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarnings); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningValue); WINGET_DEFINE_RESOURCE_STRINGID(ShowChannel); WINGET_DEFINE_RESOURCE_STRINGID(ShowCommandLongDescription); @@ -235,6 +239,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UnexpectedErrorExecutingCommand); WINGET_DEFINE_RESOURCE_STRINGID(UninstallAbandoned); WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandLongDescription); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFailedWithCode); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFlowStartingPackageUninstall); @@ -246,6 +251,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(Usage); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandLongDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ValidateManifestArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(VerboseLogsArgumentDescription); @@ -255,6 +261,8 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(VerifyPathFailedNotExist); WINGET_DEFINE_RESOURCE_STRINGID(VersionArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(VersionsArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(WindowsFeaturesDependencies); + WINGET_DEFINE_RESOURCE_STRINGID(WindowsLibrariesDependencies); WINGET_DEFINE_RESOURCE_STRINGID(WordArgumentDescription); }; diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index fa57a10a12..44eebed7e7 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -8,28 +8,44 @@ namespace AppInstaller::CLI::Workflow { void ReportDependencies(Execution::Context& context) { - if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) return; + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + return; + } const auto& dependencies = context.Get(); if (dependencies.HasAny()) { auto info = context.Reporter.Info(); - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - Windows Features: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) + { + info << " - " << Resource::String::WindowsFeaturesDependencies << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - Windows Libraries: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) + { + info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } - if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - Package: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { - info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; - info << std::endl; - }); + if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) + { + info << " - " << Resource::String::PackageDependencies << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) + { + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + info << std::endl; + }); + } - if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - External: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + if (dependencies.HasAnyOf(Manifest::DependencyType::External)) + { + info << " - " << Resource::String::ExternalDependencies << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } } } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 57d9347d15..1b4f576255 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -409,11 +409,12 @@ namespace AppInstaller::CLI::Workflow context << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << - Workflow::ManageDependencies << + Workflow::GetInstallerDependencies << + Workflow::ReportDependencies << Workflow::InstallPackageInstaller; } - void ManageDependencies(Execution::Context& context) + void GetInstallerDependencies(Execution::Context& context) { if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { @@ -421,8 +422,7 @@ namespace AppInstaller::CLI::Workflow if (installer && installer->Dependencies.HasAny()) { context.Add(installer->Dependencies); - context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; - context << Workflow::ReportDependencies; + context.Reporter.Info() << Resource::String::InstallAndUpgradeCommandsReportDependencies << std::endl; } } } @@ -439,9 +439,7 @@ namespace AppInstaller::CLI::Workflow void InstallMultiple(Execution::Context& context) { bool allSucceeded = true; - DependencyList allDependencies; - std::vector installers; for (auto package : context.Get()) @@ -481,7 +479,7 @@ namespace AppInstaller::CLI::Workflow if (allDependencies.HasAny()) { context.Add(allDependencies); - context.Reporter.Info() << "The packages found in this import have the following dependencies:" << std::endl; + context.Reporter.Info() << Resource::String::ImportCommandReportDependencies << std::endl; context << Workflow::ReportDependencies; } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 65f4085445..d7156fa025 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -89,11 +89,11 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void InstallPackageVersion(Execution::Context& context); - // Shows information about dependencies. + // Gathers package dependencies information. // Required Args: None // Inputs: Manifest // Outputs: None - void ManageDependencies(Execution::Context& context); + void GetInstallerDependencies(Execution::Context& context); // Installs multiple packages. // Required Args: None diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index d8df6c7d74..b15bcb27b0 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -78,21 +78,33 @@ namespace AppInstaller::CLI::Workflow { info << Execution::ManifestInfoEmphasis << " Dependencies: " << std::endl; - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) info << " - WindowsFeatures: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) + { + info << " - WindowsFeatures: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) info << " - WindowsLibraries: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) + { + info << " - WindowsLibraries: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } - if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) info << " - PackageDependencies: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { - info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; - info << std::endl; - }); + if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) + { + info << " - PackageDependencies: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + info << std::endl; + }); + } - if (dependencies.HasAnyOf(Manifest::DependencyType::External)) info << " - ExternalDependencies: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + if (dependencies.HasAnyOf(Manifest::DependencyType::External)) + { + info << " - ExternalDependencies: " << std::endl; + dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } } } } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 898b256b77..2ac88d92f1 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -124,13 +124,13 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; } - void GetDependencies(Execution::Context& context) + void GetDependenciesInfo(Execution::Context& context) { const auto& installer = context.Get(); if (installer && installer->Dependencies.HasAny()) { context.Add(installer->Dependencies); - context.Reporter.Info() << "This package requires the following dependencies:" << std::endl; + context.Reporter.Info() << Resource::String::UninstallCommandReportDependencies << std::endl; } } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h index 872156b7b1..1760d1fa1d 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -27,5 +27,5 @@ namespace AppInstaller::CLI::Workflow // Required Args: None // Inputs: Installer // Outputs: None - void GetDependencies(Execution::Context& context); + void GetDependenciesInfo(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index 8a5b251a9a..9a803de42e 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -112,7 +112,7 @@ namespace AppInstaller::CLI::Workflow updateAllFoundUpdate = true; updateContext << - ManageDependencies << + GetInstallerDependencies << InstallPackageInstaller; updateContext.Reporter.Info() << std::endl; diff --git a/src/AppInstallerCLICore/Workflows/ValidateFlow.cpp b/src/AppInstallerCLICore/Workflows/ValidateFlow.cpp new file mode 100644 index 0000000000..b02c0846fa --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/ValidateFlow.cpp @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "ValidateFlow.h" + +namespace AppInstaller::CLI::Workflow +{ + void GetDependenciesFromManifest(Execution::Context& context) { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + const auto& manifest = context.Get(); + Manifest::DependencyList allDependencies; + + for (const auto& installer : manifest.Installers) + { + allDependencies.Add(installer.Dependencies); + } + + context.Add(allDependencies); + context.Reporter.Info() << Resource::String::ValidateCommandReportDependencies << std::endl; + } + } +} + + diff --git a/src/AppInstallerCLICore/Workflows/ValidateFlow.h b/src/AppInstallerCLICore/Workflows/ValidateFlow.h new file mode 100644 index 0000000000..5d08ee4e65 --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/ValidateFlow.h @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ExecutionContext.h" + +namespace AppInstaller::CLI::Workflow +{ + // Gathers dependencies from manifest. + // Required Args: None + // Inputs: Manifest + // Outputs: None + void GetDependenciesFromManifest(Execution::Context& context); +} \ No newline at end of file diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 459c90288b..cd912f5fba 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -921,4 +921,32 @@ Configuration is disabled due to Group Policy. Cancelled + + Externals + + + The packages found in this import have the following dependencies: + Import command sentence showed before reporting dependencies + + + This package requires the following dependencies: + Install and Upgrade commands sentence showed before reporting dependencies + + + Packages + + + This package had dependencies that may not be needed anymore: + Uninstall command sentence showed before reporting dependencies + + + Manifest has the following dependencies that were not validated, ensure that they are valid: + Validate command sentence showed before reporting dependencies + + + Windows Features + + + Windows Libraries + \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 695120d610..1a1d81b889 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -180,7 +180,6 @@ - @@ -513,24 +512,19 @@ true - true - Document + true - true - Document + true - true - Document + true - true - Document + true - true - Document + true diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 560f05e82c..9eba633432 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -39,9 +39,6 @@ Header Files - - Header Files - @@ -453,10 +450,20 @@ TestData - - - - - + + TestData + + + TestData + + + TestData + + + TestData + + + TestData + \ No newline at end of file diff --git a/src/AppInstallerCLITests/RestInterface_1_0.cpp b/src/AppInstallerCLITests/RestInterface_1_0.cpp index e05b10f696..71bc923916 100644 --- a/src/AppInstallerCLITests/RestInterface_1_0.cpp +++ b/src/AppInstallerCLITests/RestInterface_1_0.cpp @@ -259,11 +259,11 @@ namespace REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); - REQUIRE(actualInstaller.Dependencies.WindowsFeatures.at(0) == "feature1"); - REQUIRE(actualInstaller.Dependencies.WindowsLibraries.at(0) == "library1"); - REQUIRE(actualInstaller.Dependencies.PackageDependencies.at(0).Id == "Foo.Baz"); - REQUIRE(actualInstaller.Dependencies.PackageDependencies.at(0).MinVersion == "2.0.0"); - REQUIRE(actualInstaller.Dependencies.ExternalDependencies.at(0) == "FooBarBaz"); + REQUIRE(actualInstaller.Dependencies.dependencies.at(0).Id == "feature1"); + REQUIRE(actualInstaller.Dependencies.dependencies.at(1).Id == "library1"); + REQUIRE(actualInstaller.Dependencies.dependencies.at(2).Id == "Foo.Baz"); + REQUIRE(actualInstaller.Dependencies.dependencies.at(2).MinVersion == "2.0.0"); + REQUIRE(actualInstaller.Dependencies.dependencies.at(3).Id == "FooBarBaz"); REQUIRE(actualInstaller.PackageFamilyName == "FooBar.PackageFamilyName"); REQUIRE(actualInstaller.ProductCode == ""); REQUIRE(actualInstaller.Capabilities.at(0) == "Bluetooth"); diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index abd5ead8aa..cd2b3e1923 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1113,7 +1113,7 @@ TEST_CASE("UpdateFlow_ShowDependencies", "[UpdateFlow][workflow][showDependencie std::string updateResultStr = updateOutput.str(); // Verify dependencies are informed - REQUIRE(updateResultStr.find("This package requires the following dependencies:") != std::string::npos); + REQUIRE(updateResultStr.find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); REQUIRE(updateResultStr.find("PreviewIIS") != std::string::npos); REQUIRE(updateResultStr.find("Preview VC Runtime") != std::string::npos); } @@ -1228,7 +1228,7 @@ TEST_CASE("UninstallFlow_ShowDependencies", "[UninstallFlow][workflow][showDepen INFO(uninstallOutput.str()); // Verify dependencies are informed - REQUIRE(uninstallOutput.str().find("This package had dependencies that may not be needed anymore:") != std::string::npos); + REQUIRE(uninstallOutput.str().find(Resource::LocString(Resource::String::UninstallCommandReportDependencies).get()) != std::string::npos); REQUIRE(uninstallOutput.str().find("PreviewIIS") != std::string::npos); REQUIRE(uninstallOutput.str().find("Preview VC Runtime") != std::string::npos); } @@ -1507,7 +1507,7 @@ TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow][ShowDependencie INFO(importOutput.str()); // Verify dependencies for all packages are informed - REQUIRE(importOutput.str().find("The packages found in this import have the following dependencies:") != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportCommandReportDependencies).get()) != std::string::npos); REQUIRE(importOutput.str().find("PreviewIIS") != std::string::npos); REQUIRE(importOutput.str().find("Preview VC Runtime") != std::string::npos); REQUIRE(importOutput.str().find("Hyper-V") != std::string::npos); @@ -1672,7 +1672,7 @@ TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow][showDependenc INFO(installOutput.str()); // Verify all types of dependencies are printed - REQUIRE(installOutput.str().find("This package requires the following dependencies:") != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } @@ -1690,7 +1690,7 @@ TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") INFO(validateOutput.str()); // Verify all types of dependencies are printed - REQUIRE(validateOutput.str().find("Manifest has the following dependencies:") != std::string::npos); + REQUIRE(validateOutput.str().find(Resource::LocString(Resource::String::ValidateCommandReportDependencies).get()) != std::string::npos); REQUIRE(validateOutput.str().find("WindowsFeaturesDep") != std::string::npos); REQUIRE(validateOutput.str().find("WindowsLibrariesDep") != std::string::npos); // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 648c422c78..7fdef0bd4d 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -5,7 +5,6 @@ #include #include #include -#include "YamlManifest.h" using namespace TestCommon; using namespace AppInstaller::Manifest; @@ -32,6 +31,23 @@ bool operator==(const MultiValue& a, const MultiValue& b) return true; } +bool HasDependency(DependencyList dependencies, DependencyType type, string_t id, string_t minVersion = "") +{ + for (const auto& dependency : dependencies.dependencies) + { + if (dependency.Type == type && dependency.Id == id) + { + if (dependency.MinVersion) { + if (dependency.MinVersion.value() == minVersion) return true; + } + else { + return true; + } + } + } + return false; +} + TEST_CASE("ReadPreviewGoodManifestAndVerifyContents", "[ManifestValidation]") { auto manifestFile = TestDataFile("Manifest-Good.yaml"); @@ -470,23 +486,6 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) } } -bool HasDependency(DependencyList dependencies, DependencyType type, string_t id, string_t minVersion = "") -{ - for (const auto& dependency : dependencies.dependencies) - { - if (dependency.Type == type && dependency.Id == id) - { - if (dependency.MinVersion) { - if (dependency.MinVersion.value() == minVersion) return true; - } - else { - return true; - } - } - } - return false; -} - TEST_CASE("ValidateV1GoodManifestAndVerifyContents", "[ManifestValidation]") { TempDirectory singletonDirectory{ "SingletonManifest" }; From 9f3961a1d8475015d192341e30a8874d22cafab2 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 22 Jun 2021 16:31:17 -0300 Subject: [PATCH 24/82] change uninstall flow (gets dependencies from package version), make DependencyList.dependencies private, code style --- .../Workflows/DependenciesFlow.cpp | 4 +- .../Workflows/InstallFlow.cpp | 5 ++- .../Workflows/ShowFlow.cpp | 4 +- .../Workflows/UninstallFlow.cpp | 9 +++-- .../Workflows/UninstallFlow.h | 4 +- .../RestInterface_1_0.cpp | 9 ++--- src/AppInstallerCLITests/YamlManifest.cpp | 37 +++++------------- .../Manifest/ManifestYamlPopulator.cpp | 2 +- .../Public/winget/ManifestCommon.h | 38 ++++++++++++++++--- .../Schema/1_0/Json/ManifestDeserializer.cpp | 2 +- 10 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 44eebed7e7..c137582cbc 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -24,10 +24,10 @@ namespace AppInstaller::CLI::Workflow dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); } - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibrary)) { info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + dependencies.ApplyToType(Manifest::DependencyType::WindowsLibrary, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); } if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 1b4f576255..b403b589a6 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -458,11 +458,13 @@ namespace AppInstaller::CLI::Workflow installContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(package.PackageRequest.Scope)); installContext << - Workflow::SelectInstaller; + Workflow::SelectInstaller << + Workflow::EnsureApplicableInstaller; if (installContext.IsTerminated()) { allSucceeded = false; + continue; } const auto& installer = installContext.Get(); @@ -499,7 +501,6 @@ namespace AppInstaller::CLI::Workflow installContext.Add(installer); installContext << - Workflow::EnsureApplicableInstaller << Workflow::InstallPackageInstaller; if (installContext.IsTerminated()) diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index b15bcb27b0..e57da40893 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -84,10 +84,10 @@ namespace AppInstaller::CLI::Workflow dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); } - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibraries)) + if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibrary)) { info << " - WindowsLibraries: " << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsLibraries, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + dependencies.ApplyToType(Manifest::DependencyType::WindowsLibrary, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); } if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 2ac88d92f1..568746f0f4 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -126,10 +126,13 @@ namespace AppInstaller::CLI::Workflow void GetDependenciesInfo(Execution::Context& context) { - const auto& installer = context.Get(); - if (installer && installer->Dependencies.HasAny()) + const auto& installedPackageVersion = context.Get(); + const auto& installerOptions = installedPackageVersion->GetManifest().Installers; + + // TODO making best effort to get the correct installer information, on the future it may be better to have a record of installations and save them + if (!installerOptions.empty()) { - context.Add(installer->Dependencies); + context.Add(installerOptions.at(0).Dependencies); context.Reporter.Info() << Resource::String::UninstallCommandReportDependencies << std::endl; } } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h index 1760d1fa1d..f771ea1d2a 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -25,7 +25,7 @@ namespace AppInstaller::CLI::Workflow // Gathers dependencies from installer. // Required Args: None - // Inputs: Installer - // Outputs: None + // Inputs: InstalledPackageVersion + // Outputs: Dependencies void GetDependenciesInfo(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLITests/RestInterface_1_0.cpp b/src/AppInstallerCLITests/RestInterface_1_0.cpp index 71bc923916..d1b13e4ad8 100644 --- a/src/AppInstallerCLITests/RestInterface_1_0.cpp +++ b/src/AppInstallerCLITests/RestInterface_1_0.cpp @@ -259,11 +259,10 @@ namespace REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); - REQUIRE(actualInstaller.Dependencies.dependencies.at(0).Id == "feature1"); - REQUIRE(actualInstaller.Dependencies.dependencies.at(1).Id == "library1"); - REQUIRE(actualInstaller.Dependencies.dependencies.at(2).Id == "Foo.Baz"); - REQUIRE(actualInstaller.Dependencies.dependencies.at(2).MinVersion == "2.0.0"); - REQUIRE(actualInstaller.Dependencies.dependencies.at(3).Id == "FooBarBaz"); + REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::WindowsFeature, "feature1")); + REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::WindowsLibrary, "library1")); + REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); + REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == "FooBar.PackageFamilyName"); REQUIRE(actualInstaller.ProductCode == ""); REQUIRE(actualInstaller.Capabilities.at(0) == "Bluetooth"); diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 7fdef0bd4d..a8544b6563 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -31,23 +31,6 @@ bool operator==(const MultiValue& a, const MultiValue& b) return true; } -bool HasDependency(DependencyList dependencies, DependencyType type, string_t id, string_t minVersion = "") -{ - for (const auto& dependency : dependencies.dependencies) - { - if (dependency.Type == type && dependency.Id == id) - { - if (dependency.MinVersion) { - if (dependency.MinVersion.value() == minVersion) return true; - } - else { - return true; - } - } - } - return false; -} - TEST_CASE("ReadPreviewGoodManifestAndVerifyContents", "[ManifestValidation]") { auto manifestFile = TestDataFile("Manifest-Good.yaml"); @@ -399,11 +382,11 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "msix", "appxbundle", "msixbundle" }); auto dependencies = manifest.DefaultInstallerInfo.Dependencies; - REQUIRE(HasDependency(dependencies, DependencyType::WindowsFeature, "IIS")); - REQUIRE(HasDependency(dependencies, DependencyType::WindowsLibraries, "VC Runtime")); - REQUIRE(HasDependency(dependencies, DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0")); - REQUIRE(HasDependency(dependencies, DependencyType::External, "Outside dependencies")); - REQUIRE(dependencies.dependencies.size() == 4); + REQUIRE(dependencies.HasDependency(DependencyType::WindowsFeature, "IIS")); + REQUIRE(dependencies.HasDependency(DependencyType::WindowsLibrary, "VC Runtime")); + REQUIRE(dependencies.HasDependency(DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0")); + REQUIRE(dependencies.HasDependency(DependencyType::External, "Outside dependencies")); + REQUIRE(dependencies.Size() == 4); REQUIRE(manifest.DefaultInstallerInfo.Capabilities == MultiValue{ "internetClient" }); REQUIRE(manifest.DefaultInstallerInfo.RestrictedCapabilities == MultiValue{ "runFullTrust" }); @@ -446,11 +429,11 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) REQUIRE(installer1.FileExtensions == MultiValue{ "appxbundle", "msixbundle", "appx", "msix" }); auto installer1Dependencies = installer1.Dependencies; - REQUIRE(HasDependency(installer1Dependencies, DependencyType::WindowsFeature, "PreviewIIS")); - REQUIRE(HasDependency(installer1Dependencies, DependencyType::WindowsLibraries, "Preview VC Runtime")); - REQUIRE(HasDependency(installer1Dependencies, DependencyType::Package, "Microsoft.MsixSdkDepPreview")); - REQUIRE(HasDependency(installer1Dependencies, DependencyType::External, "Preview Outside dependencies")); - REQUIRE(installer1Dependencies.dependencies.size()==4); + REQUIRE(installer1Dependencies.HasDependency(DependencyType::WindowsFeature, "PreviewIIS")); + REQUIRE(installer1Dependencies.HasDependency(DependencyType::WindowsLibrary, "Preview VC Runtime")); + REQUIRE(installer1Dependencies.HasDependency(DependencyType::Package, "Microsoft.MsixSdkDepPreview")); + REQUIRE(installer1Dependencies.HasDependency(DependencyType::External, "Preview Outside dependencies")); + REQUIRE(installer1Dependencies.Size() == 4); REQUIRE(installer1.Capabilities == MultiValue{ "internetClientPreview" }); REQUIRE(installer1.RestrictedCapabilities == MultiValue{ "runFullTrustPreview" }); diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 0f4bf67d92..efd31a72a8 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -356,7 +356,7 @@ namespace AppInstaller::Manifest result = { { "WindowsFeatures", [this](const YAML::Node& value)->ValidationErrors { ProcessDependenciesNode(DependencyType::WindowsFeature, value); return {}; } }, - { "WindowsLibraries", [this](const YAML::Node& value)->ValidationErrors { ProcessDependenciesNode(DependencyType::WindowsLibraries, value); return {}; } }, + { "WindowsLibraries", [this](const YAML::Node& value)->ValidationErrors { ProcessDependenciesNode(DependencyType::WindowsLibrary, value); return {}; } }, { "PackageDependencies", [this](const YAML::Node& value)->ValidationErrors { ProcessPackageDependenciesNode(value); return {}; } }, { "ExternalDependencies", [this](const YAML::Node& value)->ValidationErrors { ProcessDependenciesNode(DependencyType::External, value); return {}; } }, }; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 0557864766..4d8b2867f6 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -115,7 +115,7 @@ namespace AppInstaller::Manifest enum class DependencyType { WindowsFeature, - WindowsLibraries, + WindowsLibrary, Package, External }; @@ -126,16 +126,14 @@ namespace AppInstaller::Manifest string_t Id; std::optional MinVersion; - Dependency(DependencyType type, string_t id, std::optional minVersion) : Type(type), Id(id), MinVersion(minVersion) {} - Dependency(DependencyType type, string_t id) : Type(type), Id(id) {} + Dependency(DependencyType type, string_t id, string_t minVersion) : Type(std::move(type)), Id(std::move(id)), MinVersion(std::move(minVersion)) {} + Dependency(DependencyType type, string_t id) : Type(std::move(type)), Id(std::move(id)) {} Dependency(DependencyType type) : Type(type) {} }; struct DependencyList { - std::vector dependencies; - - DependencyList() {} + DependencyList() = default; void Add(Dependency dependency) { dependencies.push_back(dependency); } void Add(DependencyList otherDependencyList) @@ -156,6 +154,31 @@ namespace AppInstaller::Manifest return false; } + bool HasDependency(DependencyType type, string_t id, string_t minVersion = "") + { + for (const auto& dependency : dependencies) + { + if (dependency.Type == type && dependency.Id == id) + { + if (dependency.MinVersion) { + if (dependency.MinVersion.value() == minVersion) + { + return true; + } + } + else { + return true; + } + } + } + return false; + } + + size_t Size() + { + return dependencies.size(); + } + void ApplyToType(DependencyType type, std::function func) const { for (const auto& dependency : dependencies) @@ -163,6 +186,9 @@ namespace AppInstaller::Manifest if (dependency.Type == type) func(dependency); } } + + private: + std::vector dependencies; }; InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp index 0e6d1670d5..e6d45fc10e 100644 --- a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp +++ b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp @@ -446,7 +446,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0::Json const auto& wlIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsLibraries))); for (auto id : wlIds) { - dependencyList.Add(Dependency(DependencyType::WindowsLibraries, id)); + dependencyList.Add(Dependency(DependencyType::WindowsLibrary, id)); }; const auto& extIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(ExternalDependencies))); From 1c6347f89a0e735ca7b043a41a09027b91213702 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 22 Jun 2021 16:39:22 -0300 Subject: [PATCH 25/82] add missing report dep on upgrade --- src/AppInstallerCLICore/Commands/UpgradeCommand.cpp | 3 +++ src/AppInstallerCLITests/WorkFlow.cpp | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 4d3fcc353c..e4ce3bfcb9 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -6,6 +6,7 @@ #include "Workflows/InstallFlow.h" #include "Workflows/UpdateFlow.h" #include "Workflows/WorkflowBase.h" +#include "Workflows/DependenciesFlow.h" #include "Resources.h" using namespace AppInstaller::CLI::Execution; @@ -150,6 +151,7 @@ namespace AppInstaller::CLI SelectInstaller << EnsureApplicableInstaller << GetInstallerDependencies << + ReportDependencies << InstallPackageInstaller; } else @@ -178,6 +180,7 @@ namespace AppInstaller::CLI context << GetInstallerDependencies << + ReportDependencies << InstallPackageInstaller; } } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index cd2b3e1923..6ea5b70ecf 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1230,7 +1230,6 @@ TEST_CASE("UninstallFlow_ShowDependencies", "[UninstallFlow][workflow][showDepen // Verify dependencies are informed REQUIRE(uninstallOutput.str().find(Resource::LocString(Resource::String::UninstallCommandReportDependencies).get()) != std::string::npos); REQUIRE(uninstallOutput.str().find("PreviewIIS") != std::string::npos); - REQUIRE(uninstallOutput.str().find("Preview VC Runtime") != std::string::npos); } TEST_CASE("ExportFlow_ExportAll", "[ExportFlow][workflow]") From c785b149b764fd498dc38533c1db4a0a2d1ebb0a Mon Sep 17 00:00:00 2001 From: fzanollo Date: Wed, 23 Jun 2021 13:04:47 -0300 Subject: [PATCH 26/82] Update src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw Co-authored-by: JohnMcPMS --- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index cd912f5fba..d4b016a34e 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -940,7 +940,8 @@ Configuration is disabled due to Group Policy. Uninstall command sentence showed before reporting dependencies - Manifest has the following dependencies that were not validated, ensure that they are valid: + Manifest has the following dependencies that were not validated; ensure that they are valid: + Validate command sentence showed before reporting dependencies @@ -949,4 +950,4 @@ Configuration is disabled due to Group Policy. Windows Libraries - \ No newline at end of file + From 0a31f7905094018ef89d47e6b6ebead7e59e969c Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 23 Jun 2021 13:16:33 -0300 Subject: [PATCH 27/82] compare id with ICU case insensitive --- .../Public/winget/ManifestCommon.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 4d8b2867f6..3cdc4bbcb8 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -126,7 +126,7 @@ namespace AppInstaller::Manifest string_t Id; std::optional MinVersion; - Dependency(DependencyType type, string_t id, string_t minVersion) : Type(std::move(type)), Id(std::move(id)), MinVersion(std::move(minVersion)) {} + Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), Id(std::move(id)), MinVersion(std::move(minVersion)) {} Dependency(DependencyType type, string_t id) : Type(std::move(type)), Id(std::move(id)) {} Dependency(DependencyType type) : Type(type) {} }; @@ -135,8 +135,12 @@ namespace AppInstaller::Manifest { DependencyList() = default; - void Add(Dependency dependency) { dependencies.push_back(dependency); } - void Add(DependencyList otherDependencyList) + void Add(const Dependency& dependency) + { + dependencies.push_back(dependency); + } + + void Add(const DependencyList& otherDependencyList) { for (const auto& dependency : otherDependencyList.dependencies) { @@ -158,7 +162,7 @@ namespace AppInstaller::Manifest { for (const auto& dependency : dependencies) { - if (dependency.Type == type && dependency.Id == id) + if (dependency.Type == type && Utility::ICUCaseInsensitiveEquals(dependency.Id, id)) { if (dependency.MinVersion) { if (dependency.MinVersion.value() == minVersion) @@ -179,7 +183,7 @@ namespace AppInstaller::Manifest return dependencies.size(); } - void ApplyToType(DependencyType type, std::function func) const + void ApplyToType(DependencyType type, std::function func) const { for (const auto& dependency : dependencies) { From af7b09ceeb3e0a321b1f04b603605d7c3252bbb4 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 23 Jun 2021 14:41:28 -0300 Subject: [PATCH 28/82] move dependencies related functions to DependenciesFlow, ReportDependencies is now a WorkflowTask receiving resource string id --- .../AppInstallerCLICore.vcxproj | 2 - .../AppInstallerCLICore.vcxproj.filters | 6 --- .../Commands/UninstallCommand.cpp | 2 +- .../Commands/UpgradeCommand.cpp | 8 +-- .../Commands/ValidateCommand.cpp | 5 +- .../Workflows/DependenciesFlow.cpp | 52 ++++++++++++++----- .../Workflows/DependenciesFlow.h | 23 +++++++- .../Workflows/InstallFlow.cpp | 25 ++------- .../Workflows/InstallFlow.h | 6 --- .../Workflows/UninstallFlow.cpp | 10 +--- .../Workflows/UpdateFlow.cpp | 2 +- .../Workflows/ValidateFlow.cpp | 26 ---------- .../Workflows/ValidateFlow.h | 13 ----- .../Shared/Strings/en-us/winget.resw | 5 +- 14 files changed, 77 insertions(+), 108 deletions(-) delete mode 100644 src/AppInstallerCLICore/Workflows/ValidateFlow.cpp delete mode 100644 src/AppInstallerCLICore/Workflows/ValidateFlow.h diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index c6d264088c..52515b8426 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -263,7 +263,6 @@ - @@ -318,7 +317,6 @@ - diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index ee8da9ee29..bb000f5408 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -158,9 +158,6 @@ Workflows - - Header Files - @@ -286,9 +283,6 @@ Source Files - - Source Files - diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index b2780d5bfc..91e1b5b3b5 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -130,7 +130,7 @@ namespace AppInstaller::CLI Workflow::GetInstalledPackageVersion << Workflow::GetUninstallInfo << Workflow::GetDependenciesInfo << - Workflow::ReportDependencies << + Workflow::ReportDependencies(Resource::String::UninstallCommandReportDependencies) << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << Workflow::ReportExecutionStage(ExecutionStage::PostExecution); diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index e4ce3bfcb9..9f85569538 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -150,8 +150,8 @@ namespace AppInstaller::CLI EnsureUpdateVersionApplicable << SelectInstaller << EnsureApplicableInstaller << - GetInstallerDependencies << - ReportDependencies << + GetDependenciesFromInstaller << + ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << InstallPackageInstaller; } else @@ -179,8 +179,8 @@ namespace AppInstaller::CLI } context << - GetInstallerDependencies << - ReportDependencies << + GetDependenciesFromInstaller << + ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << InstallPackageInstaller; } } diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index 9bf5b00d97..b8b470185e 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -4,7 +4,6 @@ #include "ValidateCommand.h" #include "Workflows/WorkflowBase.h" #include "Workflows/DependenciesFlow.h" -#include "Workflows/ValidateFlow.h" #include "Resources.h" namespace AppInstaller::CLI @@ -47,8 +46,8 @@ namespace AppInstaller::CLI context.Add(manifest); context << - Workflow::GetDependenciesFromManifest << - Workflow::ReportDependencies; + Workflow::GetInstallersDependenciesFromManifest << + Workflow::ReportDependencies(Resource::String::ValidateCommandReportDependencies); context.Reporter.Info() << Resource::String::ManifestValidationSuccess << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index c137582cbc..18f6796943 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -5,18 +5,19 @@ #include "DependenciesFlow.h" namespace AppInstaller::CLI::Workflow -{ - void ReportDependencies(Execution::Context& context) - { +{ + void ReportDependencies::operator()(Execution::Context& context) const + { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { return; } + auto info = context.Reporter.Info(); const auto& dependencies = context.Get(); if (dependencies.HasAny()) { - auto info = context.Reporter.Info(); + info << Resource::StringId(m_messageId) << std::endl; if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) { @@ -33,12 +34,12 @@ namespace AppInstaller::CLI::Workflow if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) { info << " - " << Resource::String::PackageDependencies << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) - { - info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; - info << std::endl; - }); + dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) + { + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + info << std::endl; + }); } if (dependencies.HasAnyOf(Manifest::DependencyType::External)) @@ -46,6 +47,33 @@ namespace AppInstaller::CLI::Workflow info << " - " << Resource::String::ExternalDependencies << std::endl; dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); } - } - } + } + } + + void GetInstallersDependenciesFromManifest(Execution::Context& context) { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + const auto& manifest = context.Get(); + Manifest::DependencyList allDependencies; + + for (const auto& installer : manifest.Installers) + { + allDependencies.Add(installer.Dependencies); + } + + context.Add(allDependencies); + } + } + + void GetDependenciesFromInstaller(Execution::Context& context) + { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + const auto& installer = context.Get(); + if (installer) + { + context.Add(installer->Dependencies); + } + } + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 6498ff3430..0b1852115b 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -6,8 +6,29 @@ namespace AppInstaller::CLI::Workflow { // Shows information about dependencies. + // Required Args: message to use at the begining, before outputting dependencies + // Inputs: Manifest + // Outputs: None + struct ReportDependencies : public WorkflowTask + { + ReportDependencies(AppInstaller::StringResource::StringId messageId) : + WorkflowTask("ReportDependencies"), m_messageId(messageId) {} + + void operator()(Execution::Context& context) const override; + + private: + AppInstaller::StringResource::StringId m_messageId; + }; + + // Gathers all installers dependencies from manifest. // Required Args: None // Inputs: Manifest // Outputs: None - void ReportDependencies(Execution::Context& context); + void GetInstallersDependenciesFromManifest(Execution::Context& context); + + // Gathers package dependencies information from installer. + // Required Args: None + // Inputs: Installer + // Outputs: None + void GetDependenciesFromInstaller(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index b403b589a6..4d2aeb3dab 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -409,24 +409,11 @@ namespace AppInstaller::CLI::Workflow context << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << - Workflow::GetInstallerDependencies << - Workflow::ReportDependencies << + Workflow::GetDependenciesFromInstaller << + Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; } - void GetInstallerDependencies(Execution::Context& context) - { - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - const auto& installer = context.Get(); - if (installer && installer->Dependencies.HasAny()) - { - context.Add(installer->Dependencies); - context.Reporter.Info() << Resource::String::InstallAndUpgradeCommandsReportDependencies << std::endl; - } - } - } - const struct PackagesAndInstallers { PackagesAndInstallers(std::optional inst, @@ -478,12 +465,8 @@ namespace AppInstaller::CLI::Workflow if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - if (allDependencies.HasAny()) - { - context.Add(allDependencies); - context.Reporter.Info() << Resource::String::ImportCommandReportDependencies << std::endl; - context << Workflow::ReportDependencies; - } + context.Add(allDependencies); + context << Workflow::ReportDependencies(Resource::String::ImportCommandReportDependencies); } for (auto packageAndInstaller : installers) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index d7156fa025..30837e856c 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -89,12 +89,6 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void InstallPackageVersion(Execution::Context& context); - // Gathers package dependencies information. - // Required Args: None - // Inputs: Manifest - // Outputs: None - void GetInstallerDependencies(Execution::Context& context); - // Installs multiple packages. // Required Args: None // Inputs: Manifests diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 568746f0f4..5f0a48c369 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -126,14 +126,6 @@ namespace AppInstaller::CLI::Workflow void GetDependenciesInfo(Execution::Context& context) { - const auto& installedPackageVersion = context.Get(); - const auto& installerOptions = installedPackageVersion->GetManifest().Installers; - - // TODO making best effort to get the correct installer information, on the future it may be better to have a record of installations and save them - if (!installerOptions.empty()) - { - context.Add(installerOptions.at(0).Dependencies); - context.Reporter.Info() << Resource::String::UninstallCommandReportDependencies << std::endl; - } + // TODO make best effort to get the correct installer information, on the future it may be better to have a record of installations and save them } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index 9a803de42e..a0689d946f 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -112,7 +112,7 @@ namespace AppInstaller::CLI::Workflow updateAllFoundUpdate = true; updateContext << - GetInstallerDependencies << + GetDependenciesFromInstaller << InstallPackageInstaller; updateContext.Reporter.Info() << std::endl; diff --git a/src/AppInstallerCLICore/Workflows/ValidateFlow.cpp b/src/AppInstallerCLICore/Workflows/ValidateFlow.cpp deleted file mode 100644 index b02c0846fa..0000000000 --- a/src/AppInstallerCLICore/Workflows/ValidateFlow.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "ValidateFlow.h" - -namespace AppInstaller::CLI::Workflow -{ - void GetDependenciesFromManifest(Execution::Context& context) { - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - const auto& manifest = context.Get(); - Manifest::DependencyList allDependencies; - - for (const auto& installer : manifest.Installers) - { - allDependencies.Add(installer.Dependencies); - } - - context.Add(allDependencies); - context.Reporter.Info() << Resource::String::ValidateCommandReportDependencies << std::endl; - } - } -} - - diff --git a/src/AppInstallerCLICore/Workflows/ValidateFlow.h b/src/AppInstallerCLICore/Workflows/ValidateFlow.h deleted file mode 100644 index 5d08ee4e65..0000000000 --- a/src/AppInstallerCLICore/Workflows/ValidateFlow.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "ExecutionContext.h" - -namespace AppInstaller::CLI::Workflow -{ - // Gathers dependencies from manifest. - // Required Args: None - // Inputs: Manifest - // Outputs: None - void GetDependenciesFromManifest(Execution::Context& context); -} \ No newline at end of file diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index d4b016a34e..9e00e20f10 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -922,7 +922,7 @@ Configuration is disabled due to Group Policy. Cancelled - Externals + External The packages found in this import have the following dependencies: @@ -941,7 +941,6 @@ Configuration is disabled due to Group Policy. Manifest has the following dependencies that were not validated; ensure that they are valid: - Validate command sentence showed before reporting dependencies @@ -950,4 +949,4 @@ Configuration is disabled due to Group Policy. Windows Libraries - + \ No newline at end of file From 8fce81d7208be39aad151da57a85b8d41b94234d Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 23 Jun 2021 15:16:50 -0300 Subject: [PATCH 29/82] TODO to get dependencies on uninstall context, remove testcase for now, move dependency related function to DependenciesFlow --- .../Commands/UninstallCommand.cpp | 2 +- .../Workflows/DependenciesFlow.cpp | 9 ++++++++ .../Workflows/DependenciesFlow.h | 13 ++++++++--- .../Workflows/UninstallFlow.cpp | 5 ---- .../Workflows/UninstallFlow.h | 6 ----- .../Workflows/UpdateFlow.cpp | 2 ++ src/AppInstallerCLITests/WorkFlow.cpp | 23 ------------------- 7 files changed, 22 insertions(+), 38 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index 91e1b5b3b5..2f9fd4f925 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -129,7 +129,7 @@ namespace AppInstaller::CLI context << Workflow::GetInstalledPackageVersion << Workflow::GetUninstallInfo << - Workflow::GetDependenciesInfo << + Workflow::GetDependenciesInfoForUninstall << Workflow::ReportDependencies(Resource::String::UninstallCommandReportDependencies) << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 18f6796943..41bb4e9663 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -76,4 +76,13 @@ namespace AppInstaller::CLI::Workflow } } } + + void GetDependenciesInfoForUninstall(Execution::Context& context) + { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + // TODO make best effort to get the correct installer information, it may be better to have a record of installations and save the correct installers + context.Add(Manifest::DependencyList()); // sending empty list of dependencies for now + } + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 0b1852115b..34b55bc30c 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -7,7 +7,7 @@ namespace AppInstaller::CLI::Workflow { // Shows information about dependencies. // Required Args: message to use at the begining, before outputting dependencies - // Inputs: Manifest + // Inputs: Dependencies // Outputs: None struct ReportDependencies : public WorkflowTask { @@ -23,12 +23,19 @@ namespace AppInstaller::CLI::Workflow // Gathers all installers dependencies from manifest. // Required Args: None // Inputs: Manifest - // Outputs: None + // Outputs: Dependencies void GetInstallersDependenciesFromManifest(Execution::Context& context); // Gathers package dependencies information from installer. // Required Args: None // Inputs: Installer - // Outputs: None + // Outputs: Dependencies void GetDependenciesFromInstaller(Execution::Context& context); + + // TODO: + // Gathers dependencies information for the uninstall command. + // Required Args: None + // Inputs: None + // Outputs: Dependencies + void GetDependenciesInfoForUninstall(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 5f0a48c369..b5fa852316 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -122,10 +122,5 @@ namespace AppInstaller::CLI::Workflow } context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; - } - - void GetDependenciesInfo(Execution::Context& context) - { - // TODO make best effort to get the correct installer information, on the future it may be better to have a record of installations and save them } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h index f771ea1d2a..8a71a23040 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -22,10 +22,4 @@ namespace AppInstaller::CLI::Workflow // Inputs: PackageFamilyNames // Outputs: None void MsixUninstall(Execution::Context& context); - - // Gathers dependencies from installer. - // Required Args: None - // Inputs: InstalledPackageVersion - // Outputs: Dependencies - void GetDependenciesInfo(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index a0689d946f..e86c5a084c 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "WorkflowBase.h" +#include "DependenciesFlow.h" #include "InstallFlow.h" #include "UpdateFlow.h" #include "ManifestComparator.h" @@ -113,6 +114,7 @@ namespace AppInstaller::CLI::Workflow updateContext << GetDependenciesFromInstaller << + ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << InstallPackageInstaller; updateContext.Reporter.Info() << std::endl; diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 6ea5b70ecf..bfadb5b607 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1209,29 +1209,6 @@ TEST_CASE("UninstallFlow_UninstallExeNotFound", "[UninstallFlow][workflow]") REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); } -TEST_CASE("UninstallFlow_ShowDependencies", "[UninstallFlow][workflow][showDependencies]") -{ - TestCommon::TempFile uninstallResultPath("TestExeUninstalled.txt"); - - std::ostringstream uninstallOutput; - TestContext context{ uninstallOutput, std::cin }; - OverrideForCompositeInstalledSource(context); - OverrideForExeUninstall(context); - context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller.Dependencies"sv); - context.Args.AddArg(Execution::Args::Type::Silent); - - TestUserSettings settings; - settings.Set({ true }); - - UninstallCommand uninstall({}); - uninstall.Execute(context); - INFO(uninstallOutput.str()); - - // Verify dependencies are informed - REQUIRE(uninstallOutput.str().find(Resource::LocString(Resource::String::UninstallCommandReportDependencies).get()) != std::string::npos); - REQUIRE(uninstallOutput.str().find("PreviewIIS") != std::string::npos); -} - TEST_CASE("ExportFlow_ExportAll", "[ExportFlow][workflow]") { TestCommon::TempFile exportResultPath("TestExport.json"); From af036e44e5cde0334d94b09887d962075e744a1f Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 23 Jun 2021 16:17:10 -0300 Subject: [PATCH 30/82] wfIds not temporary --- .../Rest/Schema/1_0/Json/ManifestDeserializer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp index e6d45fc10e..5e9fd361c4 100644 --- a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp +++ b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.cpp @@ -437,10 +437,10 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0::Json Manifest::DependencyList dependencyList; - const auto& wfIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsFeatures))); - for (auto id : wfIds) + auto wfIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsFeatures))); + for (auto&& id : wfIds) { - dependencyList.Add(Dependency(DependencyType::WindowsFeature, id)); + dependencyList.Add(Dependency(DependencyType::WindowsFeature, std::move(id))); }; const auto& wlIds = ConvertToManifestStringArray(JsonHelper::GetRawStringArrayFromJsonNode(dependenciesObject, JsonHelper::GetUtilityString(WindowsLibraries))); From 3388ea7195d88ec30ef84890d97fa37d45095fa0 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 23 Jun 2021 18:17:22 -0300 Subject: [PATCH 31/82] DependencyList Add function checks for existence and updates min version if needed --- .../Workflows/DependenciesFlow.h | 2 +- .../RestInterface_1_0.cpp | 8 ++-- src/AppInstallerCLITests/YamlManifest.cpp | 16 ++++---- .../Public/winget/ManifestCommon.h | 41 +++++++++++++++++-- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 34b55bc30c..e781411d47 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -6,7 +6,7 @@ namespace AppInstaller::CLI::Workflow { // Shows information about dependencies. - // Required Args: message to use at the begining, before outputting dependencies + // Required Args: message to use at the beginning, before outputting dependencies // Inputs: Dependencies // Outputs: None struct ReportDependencies : public WorkflowTask diff --git a/src/AppInstallerCLITests/RestInterface_1_0.cpp b/src/AppInstallerCLITests/RestInterface_1_0.cpp index d1b13e4ad8..29fb5e5757 100644 --- a/src/AppInstallerCLITests/RestInterface_1_0.cpp +++ b/src/AppInstallerCLITests/RestInterface_1_0.cpp @@ -259,10 +259,10 @@ namespace REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); - REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::WindowsFeature, "feature1")); - REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::WindowsLibrary, "library1")); - REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); - REQUIRE(actualInstaller.Dependencies.HasDependency(DependencyType::External, "FooBarBaz")); + REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); + REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); + REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); + REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == "FooBar.PackageFamilyName"); REQUIRE(actualInstaller.ProductCode == ""); REQUIRE(actualInstaller.Capabilities.at(0) == "Bluetooth"); diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index a8544b6563..d5cbab39fa 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -382,10 +382,10 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "msix", "appxbundle", "msixbundle" }); auto dependencies = manifest.DefaultInstallerInfo.Dependencies; - REQUIRE(dependencies.HasDependency(DependencyType::WindowsFeature, "IIS")); - REQUIRE(dependencies.HasDependency(DependencyType::WindowsLibrary, "VC Runtime")); - REQUIRE(dependencies.HasDependency(DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0")); - REQUIRE(dependencies.HasDependency(DependencyType::External, "Outside dependencies")); + REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsFeature, "IIS")); + REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsLibrary, "VC Runtime")); + REQUIRE(dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0")); + REQUIRE(dependencies.HasExactDependency(DependencyType::External, "Outside dependencies")); REQUIRE(dependencies.Size() == 4); REQUIRE(manifest.DefaultInstallerInfo.Capabilities == MultiValue{ "internetClient" }); @@ -429,10 +429,10 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton) REQUIRE(installer1.FileExtensions == MultiValue{ "appxbundle", "msixbundle", "appx", "msix" }); auto installer1Dependencies = installer1.Dependencies; - REQUIRE(installer1Dependencies.HasDependency(DependencyType::WindowsFeature, "PreviewIIS")); - REQUIRE(installer1Dependencies.HasDependency(DependencyType::WindowsLibrary, "Preview VC Runtime")); - REQUIRE(installer1Dependencies.HasDependency(DependencyType::Package, "Microsoft.MsixSdkDepPreview")); - REQUIRE(installer1Dependencies.HasDependency(DependencyType::External, "Preview Outside dependencies")); + REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsFeature, "PreviewIIS")); + REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "Preview VC Runtime")); + REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDepPreview")); + REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::External, "Preview Outside dependencies")); REQUIRE(installer1Dependencies.Size() == 4); REQUIRE(installer1.Capabilities == MultiValue{ "internetClientPreview" }); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 3cdc4bbcb8..fb6a4d1576 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -135,16 +135,36 @@ namespace AppInstaller::Manifest { DependencyList() = default; - void Add(const Dependency& dependency) + void Add(const Dependency& newDependency) { - dependencies.push_back(dependency); + Dependency* existingDependency; + + if (this->HasDependency(newDependency, existingDependency)) { + if (newDependency.MinVersion) + { + if (existingDependency->MinVersion) + { + const auto& newDependencyVersion = Version(newDependency.MinVersion.value()); + const auto& existingDependencyVersion = Version(existingDependency->MinVersion.value()); + existingDependency->MinVersion.value() = newDependencyVersion <= existingDependencyVersion ? existingDependencyVersion.ToString() : newDependencyVersion.ToString(); + } + else + { + existingDependency->MinVersion.value() = newDependency.MinVersion.value(); + } + } + } + else + { + dependencies.push_back(newDependency); + } } void Add(const DependencyList& otherDependencyList) { for (const auto& dependency : otherDependencyList.dependencies) { - dependencies.push_back(dependency); + this->Add(dependency); } } @@ -158,7 +178,20 @@ namespace AppInstaller::Manifest return false; } - bool HasDependency(DependencyType type, string_t id, string_t minVersion = "") + bool HasDependency(const Dependency& dependencyToSearch, Dependency* result) + { + for (auto& dependency : dependencies) { + if (dependency.Type == dependencyToSearch.Type && ICUCaseInsensitiveEquals(dependency.Id, dependencyToSearch.Id)) + { + result = &dependency; + return true; + } + } + return false; + } + + // for testing purposes + bool HasExactDependency(DependencyType type, string_t id, string_t minVersion = "") { for (const auto& dependency : dependencies) { From f07fdc611b0b2d809d057630d83eb09383eb4203 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 24 Jun 2021 14:11:54 -0300 Subject: [PATCH 32/82] root dependencies are used when installer are not present, otherwise installer are preferred --- .../AppInstallerCLITests.vcxproj | 6 ++ .../AppInstallerCLITests.vcxproj.filters | 6 ++ ...ller_Exe_DependenciesMultideclaration.yaml | 24 ++++++++ .../Installer_Exe_DependenciesOnRoot.yaml | 21 +++++++ src/AppInstallerCLITests/WorkFlow.cpp | 56 +++++++++++++++++-- .../Manifest/ManifestYamlPopulator.cpp | 8 +++ .../Public/winget/ManifestCommon.h | 17 +++--- 7 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesMultideclaration.yaml create mode 100644 src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesOnRoot.yaml diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 1a1d81b889..38ddc73844 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -526,6 +526,12 @@ true + + true + + + true + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 9eba633432..8ecdf7e10f 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -465,5 +465,11 @@ TestData + + TestData + + + TestData + \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesMultideclaration.yaml b/src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesMultideclaration.yaml new file mode 100644 index 0000000000..dddc675865 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesMultideclaration.yaml @@ -0,0 +1,24 @@ +PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Exe Installer With Package Dep +Publisher: Microsoft Corporation +Moniker: AICLITestExePackageDep +License: Test +ShortDescription: AppInstaller Test Exe Installer With Package Dep +Dependencies: + WindowsFeatures: + - PreviewIISOnRoot +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + InstallerSwitches: + SilentWithProgress: /silentwithprogress + Silent: /silence + Dependencies: + WindowsFeatures: + - PreviewIIS +ManifestType: singleton +ManifestVersion: 1.0.0 diff --git a/src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesOnRoot.yaml b/src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesOnRoot.yaml new file mode 100644 index 0000000000..f4146721d4 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesOnRoot.yaml @@ -0,0 +1,21 @@ +PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Exe Installer With Package Dep +Publisher: Microsoft Corporation +Moniker: AICLITestExePackageDep +License: Test +ShortDescription: AppInstaller Test Exe Installer With Package Dep +Dependencies: + WindowsFeatures: + - PreviewIISOnRoot +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + InstallerSwitches: + SilentWithProgress: /silentwithprogress + Silent: /silence +ManifestType: singleton +ManifestVersion: 1.0.0 diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index bfadb5b607..e767e8725b 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -815,7 +815,7 @@ TEST_CASE("ShowFlow_SearchAndShowAppVersion", "[ShowFlow][workflow]") REQUIRE(showOutput.str().find(" Download Url: https://ThisIsNotUsed") == std::string::npos); } -TEST_CASE("ShowFlow_ShowDependencies", "[ShowFlow][workflow][showDependencies]") +TEST_CASE("ShowFlow_Dependencies", "[ShowFlow][workflow][dependencies]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; @@ -1093,7 +1093,7 @@ TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]") REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); } -TEST_CASE("UpdateFlow_ShowDependencies", "[UpdateFlow][workflow][showDependencies]") +TEST_CASE("UpdateFlow_Dependencies", "[UpdateFlow][workflow][dependencies]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); @@ -1463,7 +1463,7 @@ TEST_CASE("ImportFlow_MachineScope", "[ImportFlow][workflow]") REQUIRE(installResultStr.find("/scope=machine") != std::string::npos); } -TEST_CASE("ImportFlow_ShowDependencies", "[ImportFlow][workflow][ShowDependencies]") +TEST_CASE("ImportFlow_Dependencies", "[ImportFlow][workflow][dependencies]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); TestCommon::TempFile msixInstallResultPath("TestMsixInstalled.txt"); @@ -1630,7 +1630,7 @@ TEST_CASE("InstallFlowMultiLocale_PreferenceWithBetterLocale", "[InstallFlow][wo REQUIRE(installResultStr.find("/en-GB") != std::string::npos); } -TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow][showDependencies]") +TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); @@ -1652,7 +1652,7 @@ TEST_CASE("InstallFlow_ShowDependencies", "[InstallFlow][workflow][showDependenc REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } -TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") +TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") { std::ostringstream validateOutput; TestContext context{ validateOutput, std::cin }; @@ -1674,4 +1674,50 @@ TEST_CASE("ValidateCommand_ShowDependencies", "[showDependencies]") REQUIRE(validateOutput.str().find("Package.Dep2-x64") != std::string::npos); REQUIRE(validateOutput.str().find("Package.Dep2-x64 [") == std::string::npos); REQUIRE(validateOutput.str().find("ExternalDep") != std::string::npos); +} + +TEST_CASE("DependenciesMultideclaration_InstallerDependenciesPreference", "[dependencies]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesMultideclaration.yaml").GetPath().u8string()); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + // Verify installer dependencies are shown + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); + REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); + // and root dependencies are not + REQUIRE(installOutput.str().find("PreviewIISOnRoot") == std::string::npos); +} + +TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesOnRoot.yaml").GetPath().u8string()); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + // Verify root dependencies are shown + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); + REQUIRE(installOutput.str().find("PreviewIISOnRoot") != std::string::npos); } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index efd31a72a8..58b6c56cdb 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -510,6 +510,8 @@ namespace AppInstaller::Manifest // Clear these defaults as PackageFamilyName and ProductCode needs to be copied based on InstallerType installer.PackageFamilyName.clear(); installer.ProductCode.clear(); + // Clear dependencies as installer overrides root dependencies + installer.Dependencies.Clear(); m_p_installer = &installer; auto errors = ValidateAndProcessFields(entry, InstallerFieldInfos); @@ -526,6 +528,12 @@ namespace AppInstaller::Manifest installer.ProductCode = manifest.DefaultInstallerInfo.ProductCode; } + // If there are no dependencies on installer use default ones + if (!installer.Dependencies.HasAny()) + { + installer.Dependencies = manifest.DefaultInstallerInfo.Dependencies; + } + // Populate installer default switches if not exists auto defaultSwitches = GetDefaultKnownSwitches(installer.InstallerType); for (auto const& defaultSwitch : defaultSwitches) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index fb6a4d1576..53a0a5bf96 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -137,15 +137,15 @@ namespace AppInstaller::Manifest void Add(const Dependency& newDependency) { - Dependency* existingDependency; + Dependency* existingDependency = this->HasDependency(newDependency); - if (this->HasDependency(newDependency, existingDependency)) { + if (existingDependency != NULL) { if (newDependency.MinVersion) { if (existingDependency->MinVersion) { - const auto& newDependencyVersion = Version(newDependency.MinVersion.value()); - const auto& existingDependencyVersion = Version(existingDependency->MinVersion.value()); + const auto& newDependencyVersion = AppInstaller::Utility::Version(newDependency.MinVersion.value()); + const auto& existingDependencyVersion = AppInstaller::Utility::Version(existingDependency->MinVersion.value()); existingDependency->MinVersion.value() = newDependencyVersion <= existingDependencyVersion ? existingDependencyVersion.ToString() : newDependencyVersion.ToString(); } else @@ -178,16 +178,15 @@ namespace AppInstaller::Manifest return false; } - bool HasDependency(const Dependency& dependencyToSearch, Dependency* result) + Dependency* HasDependency(const Dependency& dependencyToSearch) { for (auto& dependency : dependencies) { if (dependency.Type == dependencyToSearch.Type && ICUCaseInsensitiveEquals(dependency.Id, dependencyToSearch.Id)) { - result = &dependency; - return true; + return &dependency; } } - return false; + return NULL; } // for testing purposes @@ -224,6 +223,8 @@ namespace AppInstaller::Manifest } } + void Clear() { dependencies.clear(); } + private: std::vector dependencies; }; From c8e9e27bbe52bffa48d4165123b93173c3b29a41 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 24 Jun 2021 15:34:48 -0300 Subject: [PATCH 33/82] merge commits from master --- README.md | 250 ++++---- azure-pipelines.yml | 2 +- doc/Settings.md | 16 +- .../#1155 - Enhancements to list command.md | 70 +++ doc/troubleshooting/README.md | 75 +++ .../package-manager/package/repository.md | 4 +- doc/windows/package-manager/winget/export.md | 4 +- .../package-manager/winget/features.md | 2 +- doc/windows/package-manager/winget/import.md | 4 +- doc/windows/package-manager/winget/index.md | 4 +- doc/windows/package-manager/winget/install.md | 2 +- doc/windows/package-manager/winget/list.md | 6 +- doc/windows/package-manager/winget/search.md | 2 +- .../package-manager/winget/settings.md | 2 +- doc/windows/package-manager/winget/show.md | 2 +- doc/windows/package-manager/winget/source.md | 2 +- .../package-manager/winget/uninstall.md | 8 +- doc/windows/package-manager/winget/upgrade.md | 4 +- .../JSON/settings/settings.schema.0.2.json | 2 +- src/AppInstallerCLI.sln | 66 +++ src/AppInstallerCLI/AppInstallerCLI.vcxproj | 8 +- src/AppInstallerCLI/packages.config | 2 +- .../AppInstallerCLICore.vcxproj | 12 +- src/AppInstallerCLICore/COMContext.cpp | 25 +- src/AppInstallerCLICore/COMContext.h | 10 +- src/AppInstallerCLICore/Core.cpp | 1 + src/AppInstallerCLICore/ExecutionContext.cpp | 140 +++-- src/AppInstallerCLICore/ExecutionContext.h | 7 + .../Workflows/ImportExportFlow.cpp | 8 +- .../Workflows/InstallFlow.cpp | 7 + .../ShellExecuteInstallerHandler.cpp | 20 +- .../Workflows/UpdateFlow.cpp | 6 + src/AppInstallerCLICore/packages.config | 4 +- src/AppInstallerCLIE2ETests/BaseCommand.cs | 1 + .../FeaturesCommand.cs | 1 + .../AppInstallerCLIPackage.wapproj | 18 + .../Package.appxmanifest | 103 ++-- .../AppInstallerCLITests.vcxproj | 12 +- src/AppInstallerCLITests/main.cpp | 2 +- src/AppInstallerCLITests/packages.config | 4 +- .../AppInstallerCommonCore.vcxproj | 14 +- .../AppInstallerCommonCore.vcxproj.filters | 6 + .../AppInstallerLogging.cpp | 16 + .../AppInstallerTelemetry.cpp | 159 +++--- src/AppInstallerCommonCore/DODownloader.cpp | 19 +- src/AppInstallerCommonCore/DODownloader.h | 4 + src/AppInstallerCommonCore/Downloader.cpp | 41 +- src/AppInstallerCommonCore/Errors.cpp | 2 + .../ExperimentalFeature.cpp | 4 +- src/AppInstallerCommonCore/FileLogger.cpp | 29 +- .../Public/AppInstallerErrors.h | 1 + .../Public/AppInstallerFileLogger.h | 8 +- .../Public/AppInstallerLogging.h | 7 +- .../Public/AppInstallerSHA256.h | 4 +- .../Public/AppInstallerTelemetry.h | 18 +- .../Public/winget/ExperimentalFeature.h | 3 +- .../Public/winget/TraceLogger.h | 24 + .../Public/winget/UserSettings.h | 2 + src/AppInstallerCommonCore/Runtime.cpp | 34 +- src/AppInstallerCommonCore/SHA256.cpp | 40 +- .../Telemetry/TraceLogging.cpp | 24 +- .../Telemetry/TraceLogging.h | 3 +- src/AppInstallerCommonCore/TraceLogger.cpp | 30 + src/AppInstallerCommonCore/UserSettings.cpp | 1 + src/AppInstallerCommonCore/packages.config | 4 +- .../AppInstallerRepositoryCore.vcxproj | 12 +- .../CompositeSource.cpp | 129 +++-- .../Public/AppInstallerRepositorySource.h | 22 + .../RepositorySource.cpp | 95 +++- .../packages.config | 4 +- .../CatalogPackage.cpp | 100 ++++ .../CatalogPackage.h | 32 ++ .../ConnectResult.cpp | 22 + .../ConnectResult.h | 19 + .../Converters.cpp | 273 +++++++++ .../Converters.h | 17 + .../CreateCompositePackageCatalogOptions.cpp | 33 ++ .../CreateCompositePackageCatalogOptions.h | 26 + .../FindPackagesOptions.cpp | 37 ++ .../FindPackagesOptions.h | 29 + .../FindPackagesResult.cpp | 31 + .../FindPackagesResult.h | 26 + src/Microsoft.Management.Deployment/Helpers.h | 6 + .../InstallOptions.cpp | 93 +++ .../InstallOptions.h | 47 ++ .../InstallResult.cpp | 37 ++ .../InstallResult.h | 27 + .../MatchResult.cpp | 27 + .../MatchResult.h | 19 + .../Microsoft.Management.Deployment.vcxproj | 193 +++++++ .../Microsoft_Management_Deployment.def | 3 + .../PackageCatalog.cpp | 176 ++++++ .../PackageCatalog.h | 26 + .../PackageCatalogInfo.cpp | 63 ++ .../PackageCatalogInfo.h | 24 + .../PackageCatalogReference.cpp | 109 ++++ .../PackageCatalogReference.h | 23 + .../PackageManager.cpp | 354 ++++++++++++ .../PackageManager.h | 29 + .../PackageManager.idl | 536 ++++++++++++++++++ .../PackageMatchFilter.cpp | 55 ++ .../PackageMatchFilter.h | 31 + .../PackageVersionId.cpp | 27 + .../PackageVersionId.h | 19 + .../PackageVersionInfo.cpp | 90 +++ .../PackageVersionInfo.h | 27 + .../PropertySheet.props | 16 + .../packages.config | 5 + src/Microsoft.Management.Deployment/pch.cpp | 3 + src/Microsoft.Management.Deployment/pch.h | 6 + src/WinGetServer/PropertySheet.props | 16 + src/WinGetServer/WinGetServer.exe.manifest | 3 + src/WinGetServer/WinGetServer.rc | Bin 0 -> 2664 bytes src/WinGetServer/WinGetServer.vcxproj | 176 ++++++ src/WinGetServer/WinGetServer.vcxproj.filters | 39 ++ src/WinGetServer/WinMain.cpp | 79 +++ src/WinGetServer/packages.config | 5 + src/WinGetServer/resource.h | 15 + src/WinGetUtil/Exports.cpp | 1 + src/WinGetUtil/WinGetUtil.vcxproj | 12 +- src/WinGetUtil/packages.config | 4 +- 121 files changed, 4243 insertions(+), 500 deletions(-) create mode 100644 doc/specs/#1155 - Enhancements to list command.md create mode 100644 doc/troubleshooting/README.md create mode 100644 src/AppInstallerCommonCore/Public/winget/TraceLogger.h create mode 100644 src/AppInstallerCommonCore/TraceLogger.cpp create mode 100644 src/Microsoft.Management.Deployment/CatalogPackage.cpp create mode 100644 src/Microsoft.Management.Deployment/CatalogPackage.h create mode 100644 src/Microsoft.Management.Deployment/ConnectResult.cpp create mode 100644 src/Microsoft.Management.Deployment/ConnectResult.h create mode 100644 src/Microsoft.Management.Deployment/Converters.cpp create mode 100644 src/Microsoft.Management.Deployment/Converters.h create mode 100644 src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.cpp create mode 100644 src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.h create mode 100644 src/Microsoft.Management.Deployment/FindPackagesOptions.cpp create mode 100644 src/Microsoft.Management.Deployment/FindPackagesOptions.h create mode 100644 src/Microsoft.Management.Deployment/FindPackagesResult.cpp create mode 100644 src/Microsoft.Management.Deployment/FindPackagesResult.h create mode 100644 src/Microsoft.Management.Deployment/Helpers.h create mode 100644 src/Microsoft.Management.Deployment/InstallOptions.cpp create mode 100644 src/Microsoft.Management.Deployment/InstallOptions.h create mode 100644 src/Microsoft.Management.Deployment/InstallResult.cpp create mode 100644 src/Microsoft.Management.Deployment/InstallResult.h create mode 100644 src/Microsoft.Management.Deployment/MatchResult.cpp create mode 100644 src/Microsoft.Management.Deployment/MatchResult.h create mode 100644 src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj create mode 100644 src/Microsoft.Management.Deployment/Microsoft_Management_Deployment.def create mode 100644 src/Microsoft.Management.Deployment/PackageCatalog.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageCatalog.h create mode 100644 src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageCatalogInfo.h create mode 100644 src/Microsoft.Management.Deployment/PackageCatalogReference.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageCatalogReference.h create mode 100644 src/Microsoft.Management.Deployment/PackageManager.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageManager.h create mode 100644 src/Microsoft.Management.Deployment/PackageManager.idl create mode 100644 src/Microsoft.Management.Deployment/PackageMatchFilter.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageMatchFilter.h create mode 100644 src/Microsoft.Management.Deployment/PackageVersionId.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageVersionId.h create mode 100644 src/Microsoft.Management.Deployment/PackageVersionInfo.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageVersionInfo.h create mode 100644 src/Microsoft.Management.Deployment/PropertySheet.props create mode 100644 src/Microsoft.Management.Deployment/packages.config create mode 100644 src/Microsoft.Management.Deployment/pch.cpp create mode 100644 src/Microsoft.Management.Deployment/pch.h create mode 100644 src/WinGetServer/PropertySheet.props create mode 100644 src/WinGetServer/WinGetServer.exe.manifest create mode 100644 src/WinGetServer/WinGetServer.rc create mode 100644 src/WinGetServer/WinGetServer.vcxproj create mode 100644 src/WinGetServer/WinGetServer.vcxproj.filters create mode 100644 src/WinGetServer/WinMain.cpp create mode 100644 src/WinGetServer/packages.config create mode 100644 src/WinGetServer/resource.h diff --git a/README.md b/README.md index c7173fb96a..cb32a9c96b 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,127 @@ -# Welcome to the Windows Package Manager Client (aka winget.exe) repository - -This repository contains the source code for the Windows Package Manager Client (aka winget.exe). - -![winget install Microsoft.WindowsTerminal](.github/images/WingetInstallTerminal.gif) - -The packages available to the client are in the [Community repo](https://github.com/microsoft/winget-pkgs). - -> The Windows Package Manager project is in Preview. We welcome all feedback, and that feedback might lead to big (maybe even breaking) changes. - -## Installing the client - -> The client requires Windows 10 1809 (build 17763) or later at this time. - -### Microsoft Store [Recommended] - -The client is distributed within the [App Installer](https://www.microsoft.com/en-us/p/app-installer/9nblggh4nns1) package. While this package is pre-installed on Windows, the client will not be made generally available during the Preview period. In order to get automatic updates from the Microsoft Store that contain the client, one must do one of the following: - -* Install a [Windows 10 Insider](https://insider.windows.com/) build -* Join the Preview flight ring by [signing up](http://aka.ms/winget-InsiderProgram) - -Note: it may take a few days to get the updated App Installer after you receive e-mail confirmation from joining the Windows Package Manager Insider program. If you decide to install the latest release from GitHub, and you have successfully joined the insider program, you will receive updates when the next stable release has been added to the Microsoft Store. - -Once you have received the updated App Installer you should be able to execute `winget`. Some users have reported [issues](https://github.com/microsoft/winget-cli/issues/210) with the client not being on their PATH. - -### Manually Update - -The same Microsoft Store package will be made available via our [Releases](https://github.com/microsoft/winget-cli/releases). Note that installing this package will give you the WinGet client, but it will not enable automatic updates from the Microsoft Store. - -> You may need to install the [VC++ v14 Desktop Framework Package](https://docs.microsoft.com/en-us/troubleshoot/cpp/c-runtime-packages-desktop-bridge#how-to-install-and-update-desktop-framework-packages). -> This should only be necessary on older builds of Windows 10 and only if you get an error about missing framework packages. - -## Administrator considerations - -Installer behavior can be different depending on whether you are running **winget** with administrator privileges. - -* When running **winget** without administrator privileges, some applications may [require elevation](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/) to install. When the installer runs, Windows will prompt you to [elevate](https://docs.microsoft.com/windows/security/identity-protection/user-account-control). If you choose not to elevate, the application will fail to install. - -* When running **winget** in an Administrator Command Prompt, you will not see [elevation prompts](/windows/security/identity-protection/user-account-control/how-user-account-control-works) if the application requires it. Always use caution when running your command prompt as an administrator, and only install applications you trust. - -### Build your own - -You can also [build the client yourself](#building-the-client). While the client should be perfectly functional, we are not ready to provide full support for clients running outside of the official distribution mechanisms yet. Feel free to file an Issue, but know that it may get lower prioritization. - -## Build Status - -[![Build Status](https://dev.azure.com/ms/winget-cli/_apis/build/status/microsoft.winget-cli?branchName=master)](https://dev.azure.com/ms/winget-cli/_build/latest?definitionId=344&branchName=master) - -## Windows Package Manager 1.0 Roadmap -The plan for delivering Windows Package Manager v1.0 [is described here](doc/windows-package-manager-v1-roadmap.md), and will be updated as the project proceeds. - -## Overview of the Windows Package Manager -The **Windows Package Manager** is a tool designed to help you quickly and easily discover and install those tools that make your PC environment special. By using the **Windows Package Manager**, from one command, you can install your favorite tool: -```winget install ``` - -For Preview, the goal is to get something usable in your hands as soon as possible. At preview you can **search**, **show**, and **install** packages. Soon we will have **uninstall**, **list** and **update**. These items are available on our [backlog](https://github.com/microsoft/winget-cli/issues), so feel free to upvote the features you want. - -## Overview - -### Client Repository -This winget-cli repository includes the source code designed to build the client. You are encouraged to participate in the development of this client. We have plenty of backlog features in our [Issues](https://github.com/microsoft/winget-cli/issues). You can upvote the ones you want, add more, or even [get started on one.](https://github.com/microsoft/winget-cli/projects/1) - -### Sources -The client is built around the concept of sources; a set of packages effectively. Sources provide the ability to discover and retrieve the metadata about the packages, so that the client can act on it. - -The default source reflects that data available from the [Community repo](https://github.com/microsoft/winget-pkgs). - -We plan to better support additional sources, and additional types of sources, in the future. For now, additional sources can be configured, but only one used at a time. - -### Package Manager Service -The **Package Manager Service** is responsible for approving Pull Requests. It validates the YAML and [manifest spec](/doc/ManifestSpecv1.0.md) for spec compliance. - - -## Building the client - -### Prerequisites - -* Windows 10 1809 (17763) or later -* [Developer Mode enabled](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) -* [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) - * Or use winget to install it ;) (although you may need to adjust the workloads via Tools->Get Tools and Features...) -* The following workloads: - * .NET Desktop Development - * Desktop Development with C++ - * Universal Windows Platform Development -* The following extensions: - * [Microsoft Visual Studio Installer Projects](https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects) - -### Building - -We currently only build using the solution; command line methods of building a VS solution should work as well. - -## Credit - -We would like to thank [Keivan Beigi (@kayone)](https://github.com/kayone) for his work on AppGet which helped us on the initial project direction for Windows Package Manager. - - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. More -information is available in our [CONTRIBUTING.md](/CONTRIBUTING.md) file. - -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information, please refer to the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Data/Telemetry - -The winget.exe client is instrumented to collect usage and diagnostic (error) data and sends it to Microsoft to help improve the product. - -If you build the client yourself the instrumentation will not be enabled and no data will be sent to Microsoft. - -The winget.exe client respects machine wide privacy settings and users can opt-out on their device, as documented in the Microsoft Windows privacy statement [here](https://support.microsoft.com/en-us/help/4468236/diagnostics-feedback-and-privacy-in-windows-10-microsoft-privacy). - -In short to opt-out, go to `Start`, then select `Settings` > `Privacy` > `Diagnostics & feedback`, and select `Basic`. - -See the [privacy statement](privacy.md) for more details. +# Welcome to the Windows Package Manager Client (aka winget.exe) repository + +This repository contains the source code for the Windows Package Manager Client (aka winget.exe). + +![winget install Microsoft.WindowsTerminal](.github/images/WingetInstallTerminal.gif) + +The packages available to the client are in the [Community repo](https://github.com/microsoft/winget-pkgs). + +> The Windows Package Manager project is in Preview. We welcome all feedback, and that feedback might lead to big (maybe even breaking) changes. + +## Installing the client + +> The client requires Windows 10 1809 (build 17763) or later at this time. + +### Microsoft Store [Recommended] + +The client is distributed within the [App Installer](https://www.microsoft.com/en-us/p/app-installer/9nblggh4nns1) package. While this package is pre-installed on Windows, the client will not be made generally available during the Preview period. In order to get automatic updates from the Microsoft Store that contain the client, one must do one of the following: + +* Install a [Windows 10 Insider](https://insider.windows.com/) build +* Join the Preview flight ring by [signing up](http://aka.ms/winget-InsiderProgram) + +Note: it may take a few days to get the updated App Installer after you receive e-mail confirmation from joining the Windows Package Manager Insider program. If you decide to install the latest release from GitHub, and you have successfully joined the insider program, you will receive updates when the next stable release has been added to the Microsoft Store. + +Once you have received the updated App Installer you should be able to execute `winget`. Some users have reported [issues](https://github.com/microsoft/winget-cli/issues/210) with the client not being on their PATH. + +### Manually Update + +The same Microsoft Store package will be made available via our [Releases](https://github.com/microsoft/winget-cli/releases). Note that installing this package will give you the WinGet client, but it will not enable automatic updates from the Microsoft Store. + +> You may need to install the [VC++ v14 Desktop Framework Package](https://docs.microsoft.com/en-us/troubleshoot/cpp/c-runtime-packages-desktop-bridge#how-to-install-and-update-desktop-framework-packages). +> This should only be necessary on older builds of Windows 10 and only if you get an error about missing framework packages. + +### Troubleshooting + +Please read our [troubleshooting guide](/doc/troubleshooting/README.md). + +## Administrator considerations + +Installer behavior can be different depending on whether you are running **winget** with administrator privileges. + +* When running **winget** without administrator privileges, some applications may [require elevation](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/) to install. When the installer runs, Windows will prompt you to [elevate](https://docs.microsoft.com/windows/security/identity-protection/user-account-control). If you choose not to elevate, the application will fail to install. + +* When running **winget** in an Administrator Command Prompt, you will not see [elevation prompts](/windows/security/identity-protection/user-account-control/how-user-account-control-works) if the application requires it. Always use caution when running your command prompt as an administrator, and only install applications you trust. + +### Build your own + +You can also [build the client yourself](#building-the-client). While the client should be perfectly functional, we are not ready to provide full support for clients running outside of the official distribution mechanisms yet. Feel free to file an Issue, but know that it may get lower prioritization. + +## Build Status + +[![Build Status](https://dev.azure.com/ms/winget-cli/_apis/build/status/microsoft.winget-cli?branchName=master)](https://dev.azure.com/ms/winget-cli/_build/latest?definitionId=344&branchName=master) + +## Windows Package Manager 1.0 Roadmap +The plan for delivering Windows Package Manager v1.0 [is described here](doc/windows-package-manager-v1-roadmap.md), and will be updated as the project proceeds. + +## Overview of the Windows Package Manager +The **Windows Package Manager** is a tool designed to help you quickly and easily discover and install those tools that make your PC environment special. By using the **Windows Package Manager**, from one command, you can install your favorite tool: +```winget install ``` + +For Preview, the goal is to get something usable in your hands as soon as possible. At preview you can **search**, **show**, and **install** packages. Soon we will have **uninstall**, **list** and **update**. These items are available on our [backlog](https://github.com/microsoft/winget-cli/issues), so feel free to upvote the features you want. + +## Overview + +### Client Repository +This winget-cli repository includes the source code designed to build the client. You are encouraged to participate in the development of this client. We have plenty of backlog features in our [Issues](https://github.com/microsoft/winget-cli/issues). You can upvote the ones you want, add more, or even [get started on one.](https://github.com/microsoft/winget-cli/projects/1) + +### Sources +The client is built around the concept of sources; a set of packages effectively. Sources provide the ability to discover and retrieve the metadata about the packages, so that the client can act on it. + +The default source reflects that data available from the [Community repo](https://github.com/microsoft/winget-pkgs). + +We plan to better support additional sources, and additional types of sources, in the future. For now, additional sources can be configured, but only one used at a time. + +### Package Manager Service +The **Package Manager Service** is responsible for approving Pull Requests. It validates the YAML and [manifest spec](/doc/ManifestSpecv1.0.md) for spec compliance. + + +## Building the client + +### Prerequisites + +* Windows 10 1809 (17763) or later +* [Developer Mode enabled](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) +* [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) + * Or use winget to install it ;) (although you may need to adjust the workloads via Tools->Get Tools and Features...) +* The following workloads: + * .NET Desktop Development + * Desktop Development with C++ + * Universal Windows Platform Development +* The following extensions: + * [Microsoft Visual Studio Installer Projects](https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects) + +### Building + +We currently only build using the solution; command line methods of building a VS solution should work as well. + +## Credit + +We would like to thank [Keivan Beigi (@kayone)](https://github.com/kayone) for his work on AppGet which helped us on the initial project direction for Windows Package Manager. + + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. More +information is available in our [CONTRIBUTING.md](/CONTRIBUTING.md) file. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information, please refer to the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Data/Telemetry + +The winget.exe client is instrumented to collect usage and diagnostic (error) data and sends it to Microsoft to help improve the product. + +If you build the client yourself the instrumentation will not be enabled and no data will be sent to Microsoft. + +The winget.exe client respects machine wide privacy settings and users can opt-out on their device, as documented in the Microsoft Windows privacy statement [here](https://support.microsoft.com/en-us/help/4468236/diagnostics-feedback-and-privacy-in-windows-10-microsoft-privacy). + +In short to opt-out, go to `Start`, then select `Settings` > `Privacy` > `Diagnostics & feedback`, and select `Basic`. + +See the [privacy statement](privacy.md) for more details. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 625397eb75..b74287981e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -17,7 +17,7 @@ pool: variables: solution: 'src/AppInstallerCLI.sln' - buildPlatform: 'x86|x64|ARM' + buildPlatform: 'x86|x64' buildConfiguration: 'Release' appxPackageDir: '$(build.artifactStagingDirectory)\AppxPackages\\' diff --git a/doc/Settings.md b/doc/Settings.md index 2e0f26e7be..da531ed741 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -105,9 +105,12 @@ The `downloader` setting controls which code is used when downloading packages. `wininet` uses the [WinINet](https://docs.microsoft.com/en-us/windows/win32/wininet/about-wininet) APIs, while `do` uses the [Delivery Optimization](https://support.microsoft.com/en-us/windows/delivery-optimization-in-windows-10-0656e53c-15f2-90de-a87a-a2172c94cf6d) service. +The `doProgressTimeoutInSeconds` setting updates the number of seconds to wait without progress before fallback. The default number of seconds is 60, minimum is 1 and the maximum is 600. + ```json "network": { - "downloader": "do" + "downloader": "do", + "doProgressTimeoutInSeconds": 60 } ``` @@ -134,6 +137,15 @@ Microsoft Store App support in WinGet is currently implemented as an experimenta }, ``` +### packagedAPI + +Support in WinGet for packaged callers is currently implemented as an experimental feature. It allows other programs on Windows to use the Windows Package Manager. You can enable the feature as shown below. + +```json + "experimentalFeatures": { + "packagedAPI": true + }, +``` ### Dependencies Experimental feature with the aim of managing dependencies, as of now it only shows package dependency information. You can enable the feature as shown below. @@ -142,4 +154,4 @@ Experimental feature with the aim of managing dependencies, as of now it only sh "experimentalFeatures": { "dependencies": true }, -``` \ No newline at end of file +``` diff --git a/doc/specs/#1155 - Enhancements to list command.md b/doc/specs/#1155 - Enhancements to list command.md new file mode 100644 index 0000000000..c01f700ea2 --- /dev/null +++ b/doc/specs/#1155 - Enhancements to list command.md @@ -0,0 +1,70 @@ +--- +author: Demitrius Nelon @denelon +created on: 2021-06-11 +last updated: 2021-06-11 +issue id: 1155 +--- + +# Enhancements to List Command + +For [#1155](https://github.com/microsoft/winget-cli/issues/1155). + +## Abstract + +The `winget list` command was designed to display all packages installed on a users Windows 10 machine in Add / Remove Programs (ARP). The command would also show which Apps with possible upgrades were available from sources configured in the Windows Package Manager. This specification proposes enhancements to the output provided by the list command. + +The first enhancement is adding the values for "Available" and "Source" for every package in configured sources rather that just packages with upgrades available. + +The second enhancement is reducing the set of packages to the source specified when the source is passed as an argument to the list command. + +The third enhancement is adding an argument to provide the list of Apps with no matching package in configured sources. + +## Inspiration + +Several suggestions have been made on how to improve user experience. Recently, many users have been attempting to identify Apps on their system with no corresponding package in the Microsoft Community Repository. + +## Solution Design + +Modify the output behavior for `winget list` to display available version from any source. When a package is available via more than once source, the first configured source should be displayed. + +>Note: Users will be able to see if packages are available from individual sources by specifying the source as an argument to the list command. + +Modify the output behavior for `winget list -s ` to display only packages available from the specified source. + +Add a new argument `-u, --unavailable` to the list command to display Apps installed in ARP not matching any configured source. + +## UI/UX Design + +The UI would remain consistent with the current tabular output. + +## Capabilities + +### Accessibility + +This should not change impact accessibility for users of screen readers, assistive input devices, etc. + +### Security + +This should not change or impact security? + +### Reliability + +This should not impact reliability. + +### Compatibility + +This will change existing behaviors. The `winget list` output currently displays available versions and sources in addition to all other installed Apps. The `winget upgrade` command should be used instead to view available upgrades. + +### Performance, Power, and Efficiency + +## Potential Issues + +This may impact performance, but it should be minor. Another Issue [#964](https://github.com/microsoft/winget-cli/issues/964) "Improvements to the list command" was created to address performance issues related to the `winget list` command. + +## Future considerations + +This feature may simplify the process of identifying packages missing from source repositories. + +## Resources + +Issue [#977](https://github.com/microsoft/winget-cli/issues/977) "`winget list` should be able to show hidden apps" is also related to the `winget list` command. diff --git a/doc/troubleshooting/README.md b/doc/troubleshooting/README.md new file mode 100644 index 0000000000..2f606684ba --- /dev/null +++ b/doc/troubleshooting/README.md @@ -0,0 +1,75 @@ +# Troubleshooting + +## How do I get the Windows Package Manager? + +### Prerequisites + +The first thing to check is which version of Windows 10 you have. + +[Check your version of Windows](https://support.microsoft.com/en-us/windows/see-which-version-of-windows-10-you-have-12d35019-4da9-0cb1-ba47-f8b031b712ad). + +The Windows Package Manager requires at least Version 1809 (October 2018 Update). + +The next requirement is ensuring you have the [App Installer](https://www.microsoft.com/en-us/p/app-installer/9nblggh4nns1) from the Microsoft Store. The Windows Package Manager is delivered as an MSIX package. The App Installer is required to install MSIX packages on Windows 10. + +>Note: The Windows Package Manager is shipped with later versions of the App Installer. + +### Stable Releases + +The first stable release of the Windows Package Manager was v1.0.11451. This release was **not** published as an automatic update to all Windows 10 users on a supported version of Windows 10. This was an intentional decision made to provide enterprise customers (IT Professionals) sufficient time to configure and deploy Group Policy for the Windows Package Manager. + +Customers may install the [latest stable release](https://github.com/microsoft/winget-cli/releases/latest/) directly from the GitHub repository. These packages are signed, and customers will receive automatic updates if IT Policy does not block the Microsoft Store. + +### Developer Releases (Pre-Release) + +>Note: There is a known problem restoring the client to the latest stable App Installer release. We will be distributing the latest stable builds and providing instructions once they are made available. + +During the initial Windows Package Manager Preview period, releases were distributed to all Windows Insider channels. Customers who [sign up](http://aka.ms/winget-InsiderProgram) to become members of the Windows Package Manager Insider program also receive pre-release builds. The final process for inclusion into the program requires manual steps, so the App Installer update may not be available for a few days **after** receiving their e-mail notification. + +Customers may install any [release](https://github.com/microsoft/winget-cli/releases/) including pre-release builds directly from the GitHub repository. These packages are signed, and customers will receive automatic updates if IT Policy does not block the Microsoft Store. + +>Note: Insiders will receive updates to the latest build (stable or pre-release) if IT Policy does not block the Microsoft Store. Other customers will receive updates to the latest stable build once a newer stable version is published if IT Policy does not block the Microsoft Store. + +Only the Windows Insider DEV channel will continue receiving pre-release builds of the Windows Package Manager after v1.0.11451. Other Windows Insider channels will only receive stable release candidates or updated versions of the Windows Package Manager with critical bug fixes. + +## Common Issues + +### Executing `winget` doesn't display help + +The following error is displayed when executed in CMD. `The system cannot execute the specified program.` + +The following error is displayed when executed in PowerShell. + +``` +Program 'winget.exe' failed to run: The file cannot be accessed by the systemAt line:1 char:1 ++ winget ++ ~~~~~~. +At line:1 char:1 ++ winget ++ ~~~~~~ + + CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException + + FullyQualifiedErrorId : NativeCommandFailed +``` + +These errors most commonly occur for one of three reasons. +1. The App Installer does not contain the Windows Package Manager. You should check to ensure the version of App Installer is greater than 1.11.11451. You can check by executing the following command in PowerShell: + + >`Get-AppxPackage microsoft.desktopappinstaller` + +2. The App Execution Alias for the Windows Package Manager is disabled. You should enable the App Execution Aias for the Windows Package Manager +3. The App Installer did not automatically add the PATH environment variable. You should add the path environment variable. The value to add is "%userprofile%\AppData\Local\Microsoft\WindowsApps". + +## Common Errors + + +#### Error 0x801901a0 + +This error is related to Delivery Optimization (DO). You may configure the Windows Package Manager settings to use the standard `WININET` library. Add the following network setting: + +>`"network": {"downloader": "wininet"}` + +#### Error 0x80d03002 + +This error is related to Delivery Optimization (DO). You may configure the Windows Package Manager settings to use the standard `WININET` library. Add the following network setting: + +>`"network": {"downloader": "wininet"}` diff --git a/doc/windows/package-manager/package/repository.md b/doc/windows/package-manager/package/repository.md index 267acc1ff7..49525eaf9d 100644 --- a/doc/windows/package-manager/package/repository.md +++ b/doc/windows/package-manager/package/repository.md @@ -46,7 +46,7 @@ If your validation fails, use the errors to locate the line number and make a co Next, create a fork of the repository and clone it. 1. Go to [https://github.com/microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) in your browser and click **Fork**. - ![picture of fork](images\fork.png) + ![picture of fork](images/fork.png) 2. From a command line environment such as the Windows Command Prompt or PowerShell, use the following command to clone your fork. ```CMD @@ -96,7 +96,7 @@ You're now ready to push your new manifest to the remote repository. After you push your changes, return to [https://github.com/microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) and create a **pull request** to merge your fork or branch to the main branch. -![picture of pull request tab](images\pull-request.png) +![picture of pull request tab](images/pull-request.png) ## Submission process diff --git a/doc/windows/package-manager/winget/export.md b/doc/windows/package-manager/winget/export.md index 418a4b2874..cee224ed34 100644 --- a/doc/windows/package-manager/winget/export.md +++ b/doc/windows/package-manager/winget/export.md @@ -20,7 +20,7 @@ The **export** command is often used to create a file that you can share with ot `winget export [-o] []` -![export](images\export.png) +![export](images/export.png) ## Arguments @@ -57,6 +57,6 @@ Note: matching an application depends on metadata in the manifest from a configu In the example below, you will see warnings for _reSearch_ and _Angry Birds_. -![export](images\export-command.png) +![export](images/export-command.png) Once the export is complete, you can edit the resulting JSON file in your favorite editor. You can remove apps you do not wish to import in the future. diff --git a/doc/windows/package-manager/winget/features.md b/doc/windows/package-manager/winget/features.md index 457f31235d..d6bc822afa 100644 --- a/doc/windows/package-manager/winget/features.md +++ b/doc/windows/package-manager/winget/features.md @@ -20,7 +20,7 @@ You can find the latest up to date information features on the [experimental fea `winget features` -![features command](images\features.png) +![features command](images/features.png) Notice above that the status of each feature is listed. If the feature is **disabled** you will not be able to use it. If the feature is **enabled** you will notice that the command will be available to you through winget. diff --git a/doc/windows/package-manager/winget/import.md b/doc/windows/package-manager/winget/import.md index 40a68e998c..a96defb1de 100644 --- a/doc/windows/package-manager/winget/import.md +++ b/doc/windows/package-manager/winget/import.md @@ -18,7 +18,7 @@ The **import** command is often used to share your developer environment or buil `winget import [-i] []` -![import](images\import.png) +![import](images/import.png) ## Arguments @@ -51,6 +51,6 @@ The JSON file includes the following hierarchy: When the Windows Package Manager imports the JSON file, it attempts to install the specified applications in a serial fashion. If the application is not available or the application is already installed, it will notify the user of that case. -![import](images\import-command.png) +![import](images/import-command.png) You will notice in the example above, the Microsoft.WindowsTerminal was already installed. Therefore the import command skipped passed the installation. diff --git a/doc/windows/package-manager/winget/index.md b/doc/windows/package-manager/winget/index.md index 5b6cb18c79..c70fc0c138 100644 --- a/doc/windows/package-manager/winget/index.md +++ b/doc/windows/package-manager/winget/index.md @@ -43,10 +43,10 @@ One of the most common usage scenarios is to search for and install a favorite t 1. To [search](search.md) for a tool, type `winget search `. 2. After you have confirmed that the tool you want is available, you can [install](install.md) the tool by typing `winget install `. The **winget** tool will launch the installer and install the application on your PC. - ![winget commandline](images\install.png) + ![winget commandline](images/install.png) 3. In addition to install and search, **winget** provides a number of other commands that enable you to [show details](show.md) on applications, [change sources](source.md), and [validate packages](validate.md). To get a complete list of commands, type: `winget --help`. - ![winget help](images\help.png) + ![winget help](images/help.png) ### Commands diff --git a/doc/windows/package-manager/winget/install.md b/doc/windows/package-manager/winget/install.md index 5889a378c0..746f59b55f 100644 --- a/doc/windows/package-manager/winget/install.md +++ b/doc/windows/package-manager/winget/install.md @@ -18,7 +18,7 @@ The **install** command requires that you specify the exact string to install. I `winget install [[-q] \] [\]` -![search command](images\install.png) +![search command](images/install.png) ## Arguments diff --git a/doc/windows/package-manager/winget/list.md b/doc/windows/package-manager/winget/list.md index b65bee54da..c024a1ab39 100644 --- a/doc/windows/package-manager/winget/list.md +++ b/doc/windows/package-manager/winget/list.md @@ -20,7 +20,7 @@ The **list** command also supports filters which can be used to limit your list `winget list [[-q] \] [\]` -![list help command](images\list.png) +![list help command](images/list.png) ## Arguments @@ -68,7 +68,7 @@ The following example limits the output of list to 12 apps. winget list -n 12 ``` -![list output command](images\list-count.png) +![list output command](images/list-count.png) ## List with Update @@ -76,7 +76,7 @@ As stated above, the **list** command allows you to see what apps you have insta In the image below, you will notice the preview version of Terminal has an update available. -![list output command](images\list-update.png) +![list output command](images/list-update.png) The **list** command will show not only the update version available, but the source that the update is available from. diff --git a/doc/windows/package-manager/winget/search.md b/doc/windows/package-manager/winget/search.md index e322a915cf..ebec86eb1f 100644 --- a/doc/windows/package-manager/winget/search.md +++ b/doc/windows/package-manager/winget/search.md @@ -18,7 +18,7 @@ The **search** command can show all applications available, or it can be filtere `winget search [[-q] \] [\]` -![Screenshot of the Windows Power Shell window displaying the results of the winget search.](images\search.png) +![Screenshot of the Windows Power Shell window displaying the results of the winget search.](images/search.png) ## Arguments diff --git a/doc/windows/package-manager/winget/settings.md b/doc/windows/package-manager/winget/settings.md index 3da7aaacd8..09feab0b12 100644 --- a/doc/windows/package-manager/winget/settings.md +++ b/doc/windows/package-manager/winget/settings.md @@ -20,7 +20,7 @@ The **settings** command will launch your default MD editor. Windows by default Launch your default JSON editing tool: `winget settings` -![Screenshot of the Windows Package Manager Settings.](images\settings.png) +![Screenshot of the Windows Package Manager Settings.](images/settings.png) When you launch the settings for the first time, there will be no settings specified. At the top of the JSON we provide a [link](https://aka.ms/winget-settings) where you can discover the latest experimental features and settings. diff --git a/doc/windows/package-manager/winget/show.md b/doc/windows/package-manager/winget/show.md index 2940032a7b..5835ac9da3 100644 --- a/doc/windows/package-manager/winget/show.md +++ b/doc/windows/package-manager/winget/show.md @@ -18,7 +18,7 @@ The **show** command only shows metadata that was submitted with the application `winget show [[-q] \] [\]` -![show command](images\show.png) +![show command](images/show.png) ## Arguments diff --git a/doc/windows/package-manager/winget/source.md b/doc/windows/package-manager/winget/source.md index c75ca8f4cb..d30cc9c27e 100644 --- a/doc/windows/package-manager/winget/source.md +++ b/doc/windows/package-manager/winget/source.md @@ -21,7 +21,7 @@ A source provides the data for you to discover and install applications. Only ad `winget source \ \` -![Source image](images\source.png) +![Source image](images/source.png) ## Arguments diff --git a/doc/windows/package-manager/winget/uninstall.md b/doc/windows/package-manager/winget/uninstall.md index 8927a84bca..8c95f1bd0c 100644 --- a/doc/windows/package-manager/winget/uninstall.md +++ b/doc/windows/package-manager/winget/uninstall.md @@ -18,7 +18,7 @@ The **uninstall** command requires that you specify the exact string to uninstal `winget uninstall [[-q] \] [\]` -![uninstall command](images\uninstall.png) +![uninstall command](images/uninstall.png) ## Arguments @@ -48,7 +48,7 @@ The options allow you to customize the uninstall experience to meet your needs. Once you have successfully identified the application intended to uninstall, winget will execute the uninstall command. In the example below, the **name** 'orca' and the **id** was passed in. -![uninstall command](images\uninstall-execute.png) +![uninstall command](images/uninstall-execute.png) ### Example queries @@ -69,12 +69,12 @@ winget uninstall --id "{24559D0F-481C-F3BE-8DD0-D908923A38F8}" If the query provided to **winget** does not result in a single application to uninstall, then **winget** will display multiple results. You can then use additional filters to refine the search for a correct application. -![uninstall command](images\uninstall-multiple.png) +![uninstall command](images/uninstall-multiple.png) ## Uninstalling apps not installed with Windows Package Manager As mentioned in [**list**](.\list.md), the **winget list** command will display more than just apps installed with the **winget**. Therefore you can use these commands to quickly and easily remove apps from your PC. In this example, **list** was used to find the application, and then the **id** was passed in as part of uninstall. -![uninstall with list command](images\uninstall-with-list.png) +![uninstall with list command](images/uninstall-with-list.png) diff --git a/doc/windows/package-manager/winget/upgrade.md b/doc/windows/package-manager/winget/upgrade.md index b87386bd40..75b8b6ca8f 100644 --- a/doc/windows/package-manager/winget/upgrade.md +++ b/doc/windows/package-manager/winget/upgrade.md @@ -18,7 +18,7 @@ The **upgrade** command requires that you specify the exact string to upgrade. I `winget upgrade [[-q] \] [\]` -![search command](images\upgrade.png) +![search command](images/upgrade.png) ## Arguments @@ -75,7 +75,7 @@ It is common to use the [**list**](.\list.md) command to identify apps in need o In the example below you will see [**list**](.\list.md) identifies that an update is available for Microsoft.WindowsTerminalPreview, and then the user uses **upgrade** to update the application. -![search command](images\upgrade.gif) +![search command](images/upgrade.gif) ## **upgrade** --all diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index ad329e6d06..a1a1342b9f 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -94,7 +94,7 @@ "doProgressTimeoutInSeconds": { "description": "Number of seconds to wait without progress before fallback", "type": "integer", - "default": 20, + "default": 60, "minimum": 1, "maximum": 600 } diff --git a/src/AppInstallerCLI.sln b/src/AppInstallerCLI.sln index ebf82fab6c..c40948d17d 100644 --- a/src/AppInstallerCLI.sln +++ b/src/AppInstallerCLI.sln @@ -3,6 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 16.0.29409.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "AppInstallerCLIPackage", "AppInstallerCLIPackage\AppInstallerCLIPackage.wapproj", "{6AA3791A-0713-4548-A357-87A323E7AC3A}" + ProjectSection(ProjectDependencies) = postProject + {1CC41A9A-AE66-459D-9210-1E572DD7BE69} = {1CC41A9A-AE66-459D-9210-1E572DD7BE69} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerCLI", "AppInstallerCLI\AppInstallerCLI.vcxproj", "{5B6F90DF-FD19-4BAE-83D9-24DAD128E777}" EndProject @@ -105,6 +108,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution CodeAnalysis.ruleset = CodeAnalysis.ruleset EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Deployment", "Microsoft.Management.Deployment\Microsoft.Management.Deployment.vcxproj", "{1CC41A9A-AE66-459D-9210-1E572DD7BE69}" + ProjectSection(ProjectDependencies) = postProject + {866C3F06-636F-4BE8-BC24-5F86ECC606A1} = {866C3F06-636F-4BE8-BC24-5F86ECC606A1} + {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D} = {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D} + {5EB88068-5FB9-4E69-89B2-72DBC5E068F9} = {5EB88068-5FB9-4E69-89B2-72DBC5E068F9} + {8BB94BB8-374F-4294-BCA1-C7811514A6B7} = {8BB94BB8-374F-4294-BCA1-C7811514A6B7} + {82B39FDA-E86B-4713-A873-9D56DE00247A} = {82B39FDA-E86B-4713-A873-9D56DE00247A} + {5890D6ED-7C3B-40F3-B436-B54F640D9E65} = {5890D6ED-7C3B-40F3-B436-B54F640D9E65} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinGetServer", "WinGetServer\WinGetServer.vcxproj", "{2B00D362-AC92-41F3-A8D2-5B1599BDCA01}" + ProjectSection(ProjectDependencies) = postProject + {1CC41A9A-AE66-459D-9210-1E572DD7BE69} = {1CC41A9A-AE66-459D-9210-1E572DD7BE69} + EndProjectSection +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ManifestSchema\ManifestSchema.vcxitems*{1622da16-914f-4f57-a259-d5169003cc8c}*SharedItemsImports = 4 @@ -470,6 +488,54 @@ Global {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|x64.Build.0 = Release|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|x86.ActiveCfg = Release|Win32 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|x86.Build.0 = Release|Win32 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM.ActiveCfg = Debug|ARM + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM.Build.0 = Debug|ARM + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM64.Build.0 = Debug|ARM64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x64.ActiveCfg = Debug|x64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x64.Build.0 = Debug|x64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x86.ActiveCfg = Debug|Win32 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x86.Build.0 = Debug|Win32 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|ARM.ActiveCfg = Debug|ARM + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|ARM.Build.0 = Debug|ARM + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|ARM64.Build.0 = Debug|ARM64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x64.ActiveCfg = Debug|x64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x64.Build.0 = Debug|x64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x86.ActiveCfg = Debug|Win32 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x86.Build.0 = Debug|Win32 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM.ActiveCfg = Release|ARM + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM.Build.0 = Release|ARM + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM64.ActiveCfg = Release|ARM64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM64.Build.0 = Release|ARM64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x64.ActiveCfg = Release|x64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x64.Build.0 = Release|x64 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x86.ActiveCfg = Release|Win32 + {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x86.Build.0 = Release|Win32 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM.ActiveCfg = Debug|ARM + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM.Build.0 = Debug|ARM + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM64.Build.0 = Debug|ARM64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x64.ActiveCfg = Debug|x64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x64.Build.0 = Debug|x64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x86.ActiveCfg = Debug|Win32 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x86.Build.0 = Debug|Win32 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|ARM.ActiveCfg = Debug|ARM + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|ARM.Build.0 = Debug|ARM + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|ARM64.Build.0 = Debug|ARM64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x64.ActiveCfg = Debug|x64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x64.Build.0 = Debug|x64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x86.ActiveCfg = Debug|Win32 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x86.Build.0 = Debug|Win32 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM.ActiveCfg = Release|ARM + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM.Build.0 = Release|ARM + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM64.ActiveCfg = Release|ARM64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM64.Build.0 = Release|ARM64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x64.ActiveCfg = Release|x64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x64.Build.0 = Release|x64 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x86.ActiveCfg = Release|Win32 + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/AppInstallerCLI/AppInstallerCLI.vcxproj b/src/AppInstallerCLI/AppInstallerCLI.vcxproj index 24e4297647..b0c7a740b0 100644 --- a/src/AppInstallerCLI/AppInstallerCLI.vcxproj +++ b/src/AppInstallerCLI/AppInstallerCLI.vcxproj @@ -1,6 +1,6 @@  - + true true @@ -291,13 +291,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/src/AppInstallerCLI/packages.config b/src/AppInstallerCLI/packages.config index 938a92d6a5..1a341db8e9 100644 --- a/src/AppInstallerCLI/packages.config +++ b/src/AppInstallerCLI/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index 52515b8426..e877e9e147 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -1,6 +1,6 @@  - + true true @@ -336,15 +336,15 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/AppInstallerCLICore/COMContext.cpp b/src/AppInstallerCLICore/COMContext.cpp index ee2cb11fea..88171bc432 100644 --- a/src/AppInstallerCLICore/COMContext.cpp +++ b/src/AppInstallerCLICore/COMContext.cpp @@ -5,6 +5,7 @@ namespace AppInstaller { + static constexpr std::string_view s_comLogFileNamePrefix = "WPM"sv; NullStream::NullStream() { @@ -32,4 +33,26 @@ namespace AppInstaller m_executionStage = executionStage; m_comProgressCallback(ReportType::ExecutionPhaseUpdate, 0, 0, ProgressType::None, m_executionStage); } -} \ No newline at end of file + + void COMContext::SetLoggerContext(const std::wstring_view telemetryCorelationJson, const std::string& caller) + { + Logging::SetActivityId(); + Logging::Telemetry().SetTelemetryCorelationJson(telemetryCorelationJson); + Logging::Telemetry().SetCaller(caller); + Logging::Telemetry().LogStartup(true); + } + + void COMContext::SetLoggers() + { + Logging::Log().SetLevel(Logging::Level::Verbose); + Logging::Log().EnableChannel(Logging::Channel::All); + + // TODO: Log to file for COM API calls only when debugging in visual studio + Logging::AddFileLogger(s_comLogFileNamePrefix); + Logging::BeginLogFileCleanup(); + + Logging::AddTraceLogger(); + + Logging::EnableWilFailureTelemetry(); + } +} diff --git a/src/AppInstallerCLICore/COMContext.h b/src/AppInstallerCLICore/COMContext.h index 9eb2039800..e6b2ce1edd 100644 --- a/src/AppInstallerCLICore/COMContext.h +++ b/src/AppInstallerCLICore/COMContext.h @@ -61,8 +61,16 @@ namespace AppInstaller m_comProgressCallback = std::move(f); } + // Set COM call context for diagnostic and telemetry loggers + // This should be called for every COMContext object instance + void SetLoggerContext(const std::wstring_view telemetryCorelationJson, const std::string& caller); + + // Set Diagnostic and Telemetry loggers, Wil failure callback + // This should be called only once per COM Server instance + static void SetLoggers(); + private: CLI::Workflow::ExecutionStage m_executionStage = CLI::Workflow::ExecutionStage::Initial; ProgressCallBackFunction m_comProgressCallback; }; -} \ No newline at end of file +} diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index 21b7457dcc..d9a3b3af8d 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -55,6 +55,7 @@ namespace AppInstaller::CLI // Set output to UTF8 ConsoleOutputCPRestore utf8CP(CP_UTF8); + Logging::Telemetry().SetCaller("winget-cli"); Logging::Telemetry().LogStartup(); // Initiate the background cleanup of the log file location. diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 47385895ce..73e539f8b2 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "ExecutionContext.h" +#include "COMContext.h" #include "winget/UserSettings.h" namespace AppInstaller::CLI::Execution @@ -10,54 +11,110 @@ namespace AppInstaller::CLI::Execution namespace { - // The context that will receive CTRL signals - Context* s_contextForCtrlHandler = nullptr; - - BOOL WINAPI CtrlHandlerForContext(DWORD ctrlType) + // Type to contain the CTRL signal handler. + struct CtrlHandler { - AICLI_LOG(CLI, Info, << "Got CTRL type: " << ctrlType); + static CtrlHandler& Instance() + { + static CtrlHandler s_instance; + return s_instance; + } - // Won't save us from every crash, but a few more than direct access. - Context* context = s_contextForCtrlHandler; - if (!context) + void AddContext(Context* context) { - return FALSE; + std::lock_guard lock{ m_contextsLock }; + + // TODO: COMContexts are currently only used specifically for install operations, which Windows does not reliably support concurrently. + // As a temporary fix, this location which already has locking and is tracking the contexts is convenient to prevent those + // installs from happening concurrently. Future work will provide a more robust synchronization mechanism which can queue those requests + // rather than failing. + for (auto& existingContext : m_contexts) + { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING), (dynamic_cast(existingContext) != 0)); + } + + auto itr = std::find(m_contexts.begin(), m_contexts.end(), context); + THROW_HR_IF(E_NOT_VALID_STATE, itr != m_contexts.end()); + m_contexts.push_back(context); } - switch (ctrlType) + void RemoveContext(Context* context) { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - context->Terminate(APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED); - context->Reporter.CancelInProgressTask(false); - return TRUE; - // According to MSDN, we should never receive these due to having gdi32/user32 loaded in our process. - // But handle them as a force terminate anyway. - case CTRL_CLOSE_EVENT: - case CTRL_LOGOFF_EVENT: - case CTRL_SHUTDOWN_EVENT: - context->Terminate(APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED); - context->Reporter.CancelInProgressTask(true); + std::lock_guard lock{ m_contextsLock }; + + auto itr = std::find(m_contexts.begin(), m_contexts.end(), context); + THROW_HR_IF(E_NOT_VALID_STATE, itr == m_contexts.end()); + m_contexts.erase(itr); + } + + private: + CtrlHandler() + { + LOG_IF_WIN32_BOOL_FALSE(SetConsoleCtrlHandler(StaticCtrlHandlerFunction, TRUE)); + } + + static BOOL WINAPI StaticCtrlHandlerFunction(DWORD ctrlType) + { + return Instance().CtrlHandlerFunction(ctrlType); + } + + BOOL CtrlHandlerFunction(DWORD ctrlType) + { + switch (ctrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + return TerminateContexts(ctrlType, false); + // According to MSDN, we should never receive these due to having gdi32/user32 loaded in our process. + // But handle them as a force terminate anyway. + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + return TerminateContexts(ctrlType, true); + default: + return FALSE; + } + } + + // Terminates the currently attached contexts. + // Returns FALSE if no contexts attached; TRUE otherwise. + BOOL TerminateContexts(DWORD ctrlType, bool force) + { + if (m_contexts.empty()) + { + return FALSE; + } + + { + std::lock_guard lock{ m_contextsLock }; + + // TODO: Move this to be logged per active context when we have thread static globals + AICLI_LOG(CLI, Info, << "Got CTRL type: " << ctrlType); + + for (auto& context : m_contexts) + { + context->Cancel(true, force); + } + } + return TRUE; - default: - return FALSE; } - } - void SetCtrlHandlerContext(Context* context) + std::mutex m_contextsLock; + std::vector m_contexts; + }; + + void SetCtrlHandlerContext(bool add, Context* context) { - // Only one is allowed right now. - THROW_HR_IF(E_UNEXPECTED, s_contextForCtrlHandler != nullptr && context != nullptr); + THROW_HR_IF(E_POINTER, context == nullptr); - if (context == nullptr) + if (add) { - LOG_IF_WIN32_BOOL_FALSE(SetConsoleCtrlHandler(CtrlHandlerForContext, FALSE)); - s_contextForCtrlHandler = nullptr; + CtrlHandler::Instance().AddContext(context); } else { - s_contextForCtrlHandler = context; - LOG_IF_WIN32_BOOL_FALSE(SetConsoleCtrlHandler(CtrlHandlerForContext, TRUE)); + CtrlHandler::Instance().RemoveContext(context); } } } @@ -74,12 +131,17 @@ namespace AppInstaller::CLI::Execution { auto clone = std::make_unique(Reporter); clone->m_flags = m_flags; + // If the parent is hooked up to the CTRL signal, have the clone be as well + if (m_disableCtrlHandlerOnExit) + { + clone->EnableCtrlHandler(); + } return clone; } void Context::EnableCtrlHandler(bool enabled) { - SetCtrlHandlerContext(enabled ? this : nullptr); + SetCtrlHandlerContext(enabled, this); m_disableCtrlHandlerOnExit = enabled; } @@ -126,9 +188,15 @@ namespace AppInstaller::CLI::Execution m_isTerminated = true; m_terminationHR = hr; } - + + void Context::Cancel(bool exitIfStuck, bool bypassUser) + { + Terminate(exitIfStuck ? APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED : E_ABORT); + Reporter.CancelInProgressTask(bypassUser); + } + void Context::SetExecutionStage(Workflow::ExecutionStage stage, bool allowBackward) - { + { if (m_executionStage == stage) { return; diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 0b43412450..4e688f1ad1 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -82,6 +82,11 @@ namespace AppInstaller::CLI::Execution // Set the context to the terminated state. void Terminate(HRESULT hr, std::string_view file = {}, size_t line = {}); + // Cancel the context; this terminates it as well as informing any in progress task to stop cooperatively. + // Multiple attempts with exitIfStuck == true may cause the process to simply exit. + // The bypassUser indicates whether the user should be asked for cancellation (does not currently have any effect). + void Cancel(bool exitIfStuck = false, bool bypassUser = false); + // Gets context flags ContextFlag GetFlags() const { @@ -102,6 +107,8 @@ namespace AppInstaller::CLI::Execution virtual void SetExecutionStage(Workflow::ExecutionStage stage, bool); + Workflow::ExecutionStage GetExecutionStage() const { return m_executionStage; } + #ifndef AICLI_DISABLE_TEST_HOOKS // Enable tests to override behavior virtual bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask&) { return true; } diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index 3a69d3dc8b..359ee08ed0 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -284,7 +284,13 @@ namespace AppInstaller::CLI::Workflow if (searchContext.IsTerminated()) { - if (searchContext.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE) + if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) + { + // This means that the subcontext being terminated is due to an overall abort + context.Reporter.Info() << Resource::String::Cancelled << std::endl; + return; + } + else if (searchContext.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE) { AICLI_LOG(CLI, Info, << "Package is already installed: [" << packageRequest.Id << "]"); context.Reporter.Info() << Resource::String::ImportPackageAlreadyInstalled << ' ' << packageRequest.Id << std::endl; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 4d2aeb3dab..806bcd52ac 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -488,6 +488,13 @@ namespace AppInstaller::CLI::Workflow if (installContext.IsTerminated()) { + if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) + { + // This means that the subcontext being terminated is due to an overall abort + context.Reporter.Info() << Resource::String::Cancelled << std::endl; + return; + } + allSucceeded = false; } } diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp index 23d8bfee85..cabdbf0a31 100644 --- a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -138,6 +138,7 @@ namespace AppInstaller::CLI::Workflow auto path = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation); path /= Logging::FileLogger::DefaultPrefix(); + path += '-'; path += Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); path += '-'; path += Utility::GetCurrentTimeForFilename(); @@ -256,8 +257,23 @@ namespace AppInstaller::CLI::Workflow break; } - // std::filesystem::rename() handles motw correctly if applicable. - std::filesystem::rename(installerPath, renamedDownloadedInstaller); + // If rename fails, don't give up quite yet. + bool retry = true; + + try + { + // std::filesystem::rename() handles motw correctly if applicable. + std::filesystem::rename(installerPath, renamedDownloadedInstaller); + retry = false; + } + CATCH_LOG(); + + if (retry) + { + std::this_thread::sleep_for(250ms); + // If it fails again, let that one throw + std::filesystem::rename(installerPath, renamedDownloadedInstaller); + } installerPath.assign(renamedDownloadedInstaller); AICLI_LOG(CLI, Info, << "Successfully renamed downloaded installer. Path: " << installerPath); diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index e86c5a084c..c363658a5a 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -125,6 +125,12 @@ namespace AppInstaller::CLI::Workflow { updateAllHasFailure = true; } + + if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) + { + context.Reporter.Info() << Resource::String::Cancelled << std::endl; + return; + } } if (!updateAllFoundUpdate) diff --git a/src/AppInstallerCLICore/packages.config b/src/AppInstallerCLICore/packages.config index bca9425304..5e899619db 100644 --- a/src/AppInstallerCLICore/packages.config +++ b/src/AppInstallerCLICore/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/BaseCommand.cs b/src/AppInstallerCLIE2ETests/BaseCommand.cs index 77c284701a..c1c0e48a3b 100644 --- a/src/AppInstallerCLIE2ETests/BaseCommand.cs +++ b/src/AppInstallerCLIE2ETests/BaseCommand.cs @@ -61,6 +61,7 @@ public void InitializeAllFeatures(bool status) list = status, upgrade = status, uninstall = status, + packagedAPI = status, } }; diff --git a/src/AppInstallerCLIE2ETests/FeaturesCommand.cs b/src/AppInstallerCLIE2ETests/FeaturesCommand.cs index 237a8e014d..a59f091470 100644 --- a/src/AppInstallerCLIE2ETests/FeaturesCommand.cs +++ b/src/AppInstallerCLIE2ETests/FeaturesCommand.cs @@ -38,6 +38,7 @@ public void EnableExperimentalFeatures() ConfigureFeature("experimentalArg", true); ConfigureFeature("experimentalCmd", true); ConfigureFeature("experimentalMSStore", true); + ConfigureFeature("packagedAPI", true); var result = TestCommon.RunAICLICommand("features", ""); Assert.True(result.StdOut.Contains("Enabled")); } diff --git a/src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj b/src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj index 27638ec633..54e089886b 100644 --- a/src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj +++ b/src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj @@ -140,6 +140,7 @@ + @@ -148,4 +149,21 @@ copy "$(TargetDir)\resources.pri" "$(TargetDir)\AppInstallerCLI\resources.pri" + + + $(OutputPath)\..\Microsoft.Management.Deployment\Microsoft.Management.Deployment.winmd + $(TargetDir)..\..\..\..\$(PlatformTarget)\$(Configuration)\Microsoft.Management.Deployment\Microsoft.Management.Deployment.winmd + Microsoft.Management.Deployment.winmd + + + + + + + + + $(MicrosoftManagementDeploymentFileName) + + + \ No newline at end of file diff --git a/src/AppInstallerCLIPackage/Package.appxmanifest b/src/AppInstallerCLIPackage/Package.appxmanifest index 5f33bf692b..e73602a888 100644 --- a/src/AppInstallerCLIPackage/Package.appxmanifest +++ b/src/AppInstallerCLIPackage/Package.appxmanifest @@ -1,44 +1,61 @@ - - - - - WinGet Dev CLI - Microsoft Corporation - Images\StoreLogo.png - - - - - - - - - - - - - - - - com.microsoft.winget.source - - - - - - - - - - - - - - + + + + + WinGet Dev CLI + Microsoft Corporation + Images\StoreLogo.png + + + + + + + + + + + + + + + + com.microsoft.winget.source + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 38ddc73844..12b5b35288 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -555,15 +555,15 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/AppInstallerCLITests/main.cpp b/src/AppInstallerCLITests/main.cpp index f7e16a29fa..e7d1b29b0e 100644 --- a/src/AppInstallerCLITests/main.cpp +++ b/src/AppInstallerCLITests/main.cpp @@ -73,7 +73,7 @@ int main(int argc, char** argv) else if ("-logto"s == argv[i]) { ++i; - Logging::AddFileLogger(argv[i]); + Logging::AddFileLogger(std::string_view{ argv[i] }); } else if ("-tdd"s == argv[i]) { diff --git a/src/AppInstallerCLITests/packages.config b/src/AppInstallerCLITests/packages.config index bca9425304..5e899619db 100644 --- a/src/AppInstallerCLITests/packages.config +++ b/src/AppInstallerCLITests/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index c28dda13dc..f8dc710af1 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -1,6 +1,6 @@  - + true true @@ -301,6 +301,7 @@ + @@ -360,6 +361,7 @@ + @@ -371,15 +373,15 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters index c1b010760e..4f0197f832 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -171,6 +171,9 @@ Header Files + + Public\winget + @@ -290,6 +293,9 @@ Source Files + + Source Files + diff --git a/src/AppInstallerCommonCore/AppInstallerLogging.cpp b/src/AppInstallerCommonCore/AppInstallerLogging.cpp index 1f0d6cf57b..d2dfa21050 100644 --- a/src/AppInstallerCommonCore/AppInstallerLogging.cpp +++ b/src/AppInstallerCommonCore/AppInstallerLogging.cpp @@ -4,6 +4,7 @@ #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerFileLogger.h" +#include "Public/winget/TraceLogger.h" #include "Public/AppInstallerTelemetry.h" #include "Public/AppInstallerDateTime.h" #include "Public/AppInstallerRuntime.h" @@ -129,11 +130,26 @@ namespace AppInstaller::Logging } } + void AddFileLogger() + { + Log().AddLogger(std::make_unique()); + } + void AddFileLogger(const std::filesystem::path& filePath) { Log().AddLogger(std::make_unique(filePath)); } + void AddFileLogger(std::string_view fileNamePrefix) + { + Log().AddLogger(std::make_unique(fileNamePrefix)); + } + + void AddTraceLogger() + { + Log().AddLogger(std::make_unique()); + } + void BeginLogFileCleanup() { FileLogger::BeginCleanup(Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation)); diff --git a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp index 9550cf1b29..679d475fff 100644 --- a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp +++ b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp @@ -11,6 +11,15 @@ #define AICLI_TraceLoggingStringView(_sv_,_name_) TraceLoggingCountedUtf8String(_sv_.data(), static_cast(_sv_.size()), _name_) #define AICLI_TraceLoggingWStringView(_sv_,_name_) TraceLoggingCountedWideString(_sv_.data(), static_cast(_sv_.size()), _name_) +#define AICLI_TraceLoggingWriteActivity(_eventName_,...) TraceLoggingWriteActivity(\ +g_hTraceProvider,\ +_eventName_,\ +GetActivityId(false),\ +nullptr,\ +TraceLoggingCountedUtf8String(m_caller.c_str(), static_cast(m_caller.size()), "Caller"),\ +TraceLoggingPackedFieldEx(m_telemetryCorelationJsonW.c_str(), static_cast((m_telemetryCorelationJsonW.size() + 1) * sizeof(wchar_t)), TlgInUNICODESTRING, TlgOutJSON, "CvJson"),\ +__VA_ARGS__) + // Helper to print a GUID std::ostream& operator<<(std::ostream& out, const GUID& guid) { @@ -53,12 +62,21 @@ namespace AppInstaller::Logging (void)CoCreateGuid(&result); return result; } + } - const GUID* GetActivityId() + const GUID* GetActivityId(bool isNewActivity) + { + static GUID activityId; + if (isNewActivity == true) { - static GUID activityId = CreateGuid(); - return &activityId; + activityId = CreateGuid(); } + return &activityId; + } + + void SetActivityId() + { + GetActivityId(true); } TelemetryTraceLogger::TelemetryTraceLogger() @@ -91,16 +109,46 @@ namespace AppInstaller::Logging m_isRuntimeEnabled = true; } + void TelemetryTraceLogger::SetTelemetryCorelationJson(const std::wstring_view jsonStr_view) noexcept + { + // Check if passed in string is a valid Json formatted before returning the value + // If invalid, return empty Json + Json::CharReaderBuilder jsonBuilder; + std::unique_ptr jsonReader(jsonBuilder.newCharReader()); + std::unique_ptr pJsonValue = std::make_unique(); + std::string errors; + std::wstring jsonStrW{ jsonStr_view }; + std::string jsonStr = ConvertToUTF8(jsonStrW.c_str()); + + bool result = jsonReader->parse(jsonStr.c_str(), + jsonStr.c_str() + jsonStr.size(), + pJsonValue.get(), + &errors); + + if (result) + { + m_telemetryCorelationJsonW = jsonStrW; + AICLI_LOG(Core, Info, << "Passed in Corelation Vector Json is valid: " << jsonStr); + } + else + { + AICLI_LOG(Core, Error, << "Passed in Corelation Vector Json is invalid: " << jsonStr << "; Error: " << errors); + } + } + + void TelemetryTraceLogger::SetCaller(const std::string& caller) + { + m_caller = caller; + } + void TelemetryTraceLogger::LogFailure(const wil::FailureInfo& failure) const noexcept { if (IsTelemetryEnabled()) { auto anonMessage = AnonymizeString(failure.pszMessage); - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "FailureInfo", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TraceLoggingHResult(failure.hr, "HResult"), AICLI_TraceLoggingWStringView(anonMessage, "Message"), @@ -116,13 +164,13 @@ namespace AppInstaller::Logging // Also send failure to the log AICLI_LOG(Fail, Error, << [&]() { - wchar_t message[2048]; - GetFailureLogString(message, ARRAYSIZE(message), failure); - return Utility::ConvertToUTF8(message); + wchar_t message[2048]; + GetFailureLogString(message, ARRAYSIZE(message), failure); + return Utility::ConvertToUTF8(message); }()); } - void TelemetryTraceLogger::LogStartup() const noexcept + void TelemetryTraceLogger::LogStartup(bool isCOMCall) const noexcept { LocIndString version = Runtime::GetClientVersion(); LocIndString packageVersion; @@ -133,33 +181,31 @@ namespace AppInstaller::Logging if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "ClientVersion", - GetActivityId(), - nullptr, + TraceLoggingBool(isCOMCall, "IsCOMCall"), TraceLoggingCountedString(version->c_str(), static_cast(version->size()), "Version"), TraceLoggingCountedString(packageVersion->c_str(), static_cast(packageVersion->size()), "PackageVersion"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); } - AICLI_LOG(Core, Info, << "WinGet, version [" << version << "], activity [" << *GetActivityId() << ']'); + AICLI_LOG(Core, Info, << "WinGet, version [" << version << "], activity [" << *GetActivityId(false) << ']'); AICLI_LOG(Core, Info, << "OS: " << Runtime::GetOSVersion()); AICLI_LOG(Core, Info, << "Command line Args: " << Utility::ConvertToUTF8(GetCommandLineW())); if (Runtime::IsRunningInPackagedContext()) { AICLI_LOG(Core, Info, << "Package: " << packageVersion); } + AICLI_LOG(Core, Info, << "IsCOMCall:" << isCOMCall << "; Caller: " << m_caller); } - + void TelemetryTraceLogger::LogCommand(std::string_view commandName) const noexcept { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "CommandFound", - GetActivityId(), - nullptr, AICLI_TraceLoggingStringView(commandName, "Command"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); @@ -172,10 +218,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "CommandSuccess", - GetActivityId(), - nullptr, AICLI_TraceLoggingStringView(commandName, "Command"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); @@ -188,10 +232,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "CommandTermination", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TraceLoggingHResult(hr, "HResult"), AICLI_TraceLoggingStringView(file, "File"), @@ -210,10 +252,8 @@ namespace AppInstaller::Logging { auto anonMessage = AnonymizeString(Utility::ConvertToUTF16(message)); - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "Exception", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(commandName, "Command"), AICLI_TraceLoggingStringView(type, "Type"), @@ -230,10 +270,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "GetManifest", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TraceLoggingBool(isLocalManifest, "IsManifestLocal"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), @@ -245,13 +283,11 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "ManifestFields", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), - AICLI_TraceLoggingStringView(name,"Name"), + AICLI_TraceLoggingStringView(name, "Name"), AICLI_TraceLoggingStringView(version, "Version"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); @@ -264,10 +300,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "NoAppMatch", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); @@ -280,10 +314,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "MultiAppMatch", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); @@ -296,10 +328,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "AppFound", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(name, "Name"), AICLI_TraceLoggingStringView(id, "Id"), @@ -314,10 +344,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "SelectedInstaller", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TraceLoggingInt32(arch, "Arch"), AICLI_TraceLoggingStringView(url, "Url"), @@ -349,10 +377,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "SearchRequest", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(type, "Type"), AICLI_TraceLoggingStringView(query, "Query"), @@ -372,10 +398,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "SearchResultCount", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TraceLoggingUInt64(resultCount, "ResultCount"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), @@ -393,10 +417,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "HashMismatch", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), @@ -420,10 +442,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "InstallerFailure", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), @@ -441,10 +461,8 @@ namespace AppInstaller::Logging { if (IsTelemetryEnabled()) { - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "UninstallerFailure", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), @@ -481,10 +499,8 @@ namespace AppInstaller::Logging } catch (...) {} - TraceLoggingWriteActivity(g_hTelemetryProvider, + AICLI_TraceLoggingWriteActivity( "InstallARPChange", - GetActivityId(), - nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(sourceIdentifier, "SourceIdentifier"), AICLI_TraceLoggingStringView(packageIdentifier, "PackageIdentifier"), @@ -514,6 +530,19 @@ namespace AppInstaller::Logging } } + void TelemetryTraceLogger::LogNonFatalDOError(std::string_view url, HRESULT hr) const noexcept + { + if (IsTelemetryEnabled()) + { + AICLI_TraceLoggingWriteActivity( + "NonFatalDOError", + TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + AICLI_TraceLoggingStringView(url, "Url"), + TraceLoggingHResult(hr, "HResult"), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + } + } + bool TelemetryTraceLogger::IsTelemetryEnabled() const noexcept { return g_IsTelemetryProviderEnabled && m_isSettingEnabled && m_isRuntimeEnabled; @@ -590,4 +619,4 @@ namespace AppInstaller::Logging s_TelemetryTraceLogger_TestOverride = std::move(ttl); } #endif -} \ No newline at end of file +} diff --git a/src/AppInstallerCommonCore/DODownloader.cpp b/src/AppInstallerCommonCore/DODownloader.cpp index e06ae9e6bd..abadaeefaa 100644 --- a/src/AppInstallerCommonCore/DODownloader.cpp +++ b/src/AppInstallerCommonCore/DODownloader.cpp @@ -14,7 +14,13 @@ namespace AppInstaller::Utility { namespace DeliveryOptimization { -#define DO_E_DOWNLOAD_NO_PROGRESS HRESULT(0x80D02002L) // Download of a file saw no progress within the defined period +// TODO: Once the SDK headers are available, remove these defines +#define DO_E_DOWNLOAD_NO_PROGRESS HRESULT(0x80D02002L) // Download of a file saw no progress within the defined period + +#define DO_E_BLOCKED_BY_COST_TRANSFER_POLICY HRESULT(0x80D03801L) // DO core paused the job due to cost policy restrictions +#define DO_E_BLOCKED_BY_CELLULAR_POLICY HRESULT(0x80D03803L) // DO core paused the job due to detection of cellular network and policy restrictions +#define DO_E_BLOCKED_BY_POWER_STATE HRESULT(0x80D03804L) // DO core paused the job due to detection of power state change into non-AC mode +#define DO_E_BLOCKED_BY_NO_NETWORK HRESULT(0x80D03805L) // DO core paused the job due to loss of network connectivity // Represents a download work item for Delivery Optimization. struct Download @@ -404,4 +410,15 @@ namespace AppInstaller::Utility return {}; } + + bool IsDOErrorFatal(HRESULT error) + { + // If this gets to be large, store in a sorted array and binary search on it. + // There will be more to update here, which we should be able to discover through telemetry. + return + error == DO_E_BLOCKED_BY_COST_TRANSFER_POLICY || + error == DO_E_BLOCKED_BY_CELLULAR_POLICY || + error == DO_E_BLOCKED_BY_POWER_STATE || + error == DO_E_BLOCKED_BY_NO_NETWORK; + } } diff --git a/src/AppInstallerCommonCore/DODownloader.h b/src/AppInstallerCommonCore/DODownloader.h index a1f71d9e3d..1950daeb46 100644 --- a/src/AppInstallerCommonCore/DODownloader.h +++ b/src/AppInstallerCommonCore/DODownloader.h @@ -21,4 +21,8 @@ namespace AppInstaller::Utility IProgressCallback& progress, bool computeHash, std::optional info); + + // Returns true if the error from DODownload should be treated as fatal; + // false if we should be able to fall back to other download methods. + bool IsDOErrorFatal(HRESULT error); } diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp index a287d82d26..3a7918cac9 100644 --- a/src/AppInstallerCommonCore/Downloader.cpp +++ b/src/AppInstallerCommonCore/Downloader.cpp @@ -7,6 +7,7 @@ #include "Public/AppInstallerSHA256.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerLogging.h" +#include "Public/AppInstallerTelemetry.h" #include "Public/winget/UserSettings.h" #include "DODownloader.h" @@ -167,18 +168,40 @@ namespace AppInstaller::Utility if (setting == InstallerDownloader::Default || setting == InstallerDownloader::DeliveryOptimization) { - auto result = DODownload(url, dest, progress, computeHash, info); - // Since we cannot pre-apply to the file with DO, post-apply the MotW to the file. - // Only do so if the file exists, because cancellation will not throw here. - if (std::filesystem::exists(dest)) + try + { + auto result = DODownload(url, dest, progress, computeHash, info); + // Since we cannot pre-apply to the file with DO, post-apply the MotW to the file. + // Only do so if the file exists, because cancellation will not throw here. + if (std::filesystem::exists(dest)) + { + ApplyMotwIfApplicable(dest, URLZONE_INTERNET); + } + return result; + } + catch (const wil::ResultException& re) { - ApplyMotwIfApplicable(dest, URLZONE_INTERNET); + // Fall back to WinINet below unless the specific error is not one that should be ignored. + // We need to be careful not to bypass metered networks or other reasons that might + // intentionally cause the download to be blocked. + HRESULT hr = re.GetErrorCode(); + if (IsDOErrorFatal(hr)) + { + throw; + } + else + { + // Send telemetry so that we can understand the reasons for DO failing + Logging::Telemetry().LogNonFatalDOError(url, hr); + } } - return result; - // If DO becomes an issue, we may choose to catch exceptions and fall back to WinINet below. - // We would need to be careful not to bypass metered networks or other reasons that might - // intentionally cause the download to be blocked. + // If we reach this point, we are intending to fall through to WinINet. + // Remove any file that may have been placed in the target location. + if (std::filesystem::exists(dest)) + { + std::filesystem::remove(dest); + } } } diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp index ead9618d3f..7b9638f6d9 100644 --- a/src/AppInstallerCommonCore/Errors.cpp +++ b/src/AppInstallerCommonCore/Errors.cpp @@ -141,6 +141,8 @@ namespace AppInstaller return "Invalid rest source contract version"; case APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE: return "The source data is corrupted or tampered"; + case APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE: + return "Error reading from the stream"; default: return "Unknown Error Code"; } diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index 5dcf9d3d26..a4d83d4d7b 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -44,6 +44,8 @@ namespace AppInstaller::Settings return userSettings.Get(); case ExperimentalFeature::Feature::ExperimentalMSStore: return userSettings.Get(); + case ExperimentalFeature::Feature::PackagedAPI: + return userSettings.Get(); case ExperimentalFeature::Feature::Dependencies: return userSettings.Get(); default: @@ -74,8 +76,6 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "Argument Sample", "experimentalArg", "https://aka.ms/winget-settings", Feature::ExperimentalArg }; case Feature::ExperimentalMSStore: return ExperimentalFeature{ "Microsoft Store Support", "experimentalMSStore", "https://aka.ms/winget-settings", Feature::ExperimentalMSStore }; - case Feature::Dependencies: - return ExperimentalFeature{ "Show Dependencies Information", "dependencies", "https://aka.ms/winget-settings", Feature::Dependencies }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/FileLogger.cpp b/src/AppInstallerCommonCore/FileLogger.cpp index 3d13a40884..9920b6e802 100644 --- a/src/AppInstallerCommonCore/FileLogger.cpp +++ b/src/AppInstallerCommonCore/FileLogger.cpp @@ -13,22 +13,24 @@ namespace AppInstaller::Logging using namespace std::string_view_literals; using namespace std::chrono_literals; - static constexpr std::string_view s_fileLoggerDefaultFilePrefix = "WinGet-"sv; + static constexpr std::string_view s_fileLoggerDefaultFilePrefix = "WinGet"sv; static constexpr std::string_view s_fileLoggerDefaultFileExt = ".log"sv; + FileLogger::FileLogger() : FileLogger(s_fileLoggerDefaultFilePrefix) {} + FileLogger::FileLogger(const std::filesystem::path& filePath) { - if (filePath.empty()) - { - m_name = "file"; - m_filePath = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation); - m_filePath /= s_fileLoggerDefaultFilePrefix.data() + Utility::GetCurrentTimeForFilename() + s_fileLoggerDefaultFileExt.data(); - } - else - { - m_name = GetNameForPath(filePath); - m_filePath = filePath; - } + m_name = GetNameForPath(filePath); + m_filePath = filePath; + + m_stream.open(m_filePath); + } + + FileLogger::FileLogger(const std::string_view fileNamePrefix) + { + m_name = "file"; + m_filePath = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation); + m_filePath /= fileNamePrefix.data() + ('-' + Utility::GetCurrentTimeForFilename() + s_fileLoggerDefaultFileExt.data()); m_stream.open(m_filePath); } @@ -83,8 +85,7 @@ namespace AppInstaller::Logging for (auto& file : std::filesystem::directory_iterator{ filePath }) { if (file.is_regular_file() && - now - file.last_write_time() > (7 * 24h) && - Utility::CaseInsensitiveStartsWith(file.path().filename().string(), s_fileLoggerDefaultFilePrefix)) + now - file.last_write_time() > (7 * 24h)) { std::filesystem::remove(file.path()); } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h index 6b57eee1f3..c28e2765f3 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -76,6 +76,7 @@ #define APPINSTALLER_CLI_ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE ((HRESULT)0x8a15003D) #define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION ((HRESULT)0x8a15003E) #define APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE ((HRESULT)0x8a15003F) +#define APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE ((HRESULT)0x8a150040) namespace AppInstaller { diff --git a/src/AppInstallerCommonCore/Public/AppInstallerFileLogger.h b/src/AppInstallerCommonCore/Public/AppInstallerFileLogger.h index efa63ae19f..40183828e3 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerFileLogger.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerFileLogger.h @@ -13,7 +13,9 @@ namespace AppInstaller::Logging // Logs to a file. struct FileLogger : public ILogger { - FileLogger(const std::filesystem::path& filePath = {}); + FileLogger(); + explicit FileLogger(const std::filesystem::path& filePath); + explicit FileLogger(const std::string_view fileNamePrefix); ~FileLogger(); @@ -29,9 +31,9 @@ namespace AppInstaller::Logging static std::string_view DefaultExt(); // ILogger - virtual std::string GetName() const override; + std::string GetName() const override; - virtual void Write(Channel channel, Level level, std::string_view message) noexcept override; + void Write(Channel channel, Level level, std::string_view message) noexcept override; // Starts a background task to clean up old log files. static void BeginCleanup(const std::filesystem::path& filePath); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerLogging.h b/src/AppInstallerCommonCore/Public/AppInstallerLogging.h index e6b7163e09..66d8fac090 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerLogging.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerLogging.h @@ -133,7 +133,12 @@ namespace AppInstaller::Logging } // Adds the default file logger to the DiagnosticLogger. - void AddFileLogger(const std::filesystem::path& filePath = {}); + void AddFileLogger(); + void AddFileLogger(const std::filesystem::path& filePath); + void AddFileLogger(std::string_view fileNamePrefix); + + // Adds the trace logger to the DiagnosticLogger. + void AddTraceLogger(); // Starts a background task to clean up old log files. void BeginLogFileCleanup(); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerSHA256.h b/src/AppInstallerCommonCore/Public/AppInstallerSHA256.h index af42ce6d36..d6049b3021 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerSHA256.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerSHA256.h @@ -44,10 +44,10 @@ namespace AppInstaller::Utility { } // Computes the hash of the given buffer immediately. - static std::vector ComputeHash(const uint8_t* buffer, std::uint32_t cbBuffer); + static HashBuffer ComputeHash(const uint8_t* buffer, std::uint32_t cbBuffer); // Computes the hash from a given stream. - static std::vector ComputeHash(std::istream& in); + static HashBuffer ComputeHash(std::istream& in); static std::string ConvertToString(const HashBuffer& hashBuffer); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h index 28fad5e21a..94ad231d56 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h @@ -30,11 +30,17 @@ namespace AppInstaller::Logging bool DisableRuntime(); void EnableRuntime(); + // Store the passed in name of the Caller for COM calls + void SetCaller(const std::string& caller); + + // Store the passed in Telemetry Corelation Json for COM calls + void SetTelemetryCorelationJson(const std::wstring_view jsonStr_view) noexcept; + // Logs the failure info. void LogFailure(const wil::FailureInfo& failure) const noexcept; // Logs the initial process startup. - void LogStartup() const noexcept; + void LogStartup(bool isCOMCall = false) const noexcept; // Logs the invoked command. void LogCommand(std::string_view commandName) const noexcept; @@ -114,6 +120,8 @@ namespace AppInstaller::Logging std::string_view arpPublisher, std::string_view arpLanguage) const noexcept; + void LogNonFatalDOError(std::string_view url, HRESULT hr) const noexcept; + protected: TelemetryTraceLogger(); @@ -127,6 +135,9 @@ namespace AppInstaller::Logging bool m_isSettingEnabled = true; std::atomic_bool m_isRuntimeEnabled{ true }; + std::wstring m_telemetryCorelationJsonW = L"{}"; + std::string m_caller; + // Data that is needed by AnonymizeString std::wstring m_userProfile; }; @@ -137,6 +148,11 @@ namespace AppInstaller::Logging // Turns on wil failure telemetry and logging. void EnableWilFailureTelemetry(); + const GUID* GetActivityId(bool isNewActivity); + + // Set ActivityId + void SetActivityId(); + // An RAII object to disable telemetry during its lifetime. // Primarily used by the complete command to prevent messy input from spamming us. struct DisableTelemetryScope diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index e76118fec1..64e44dadbc 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -21,7 +21,8 @@ namespace AppInstaller::Settings { None = 0x0, ExperimentalMSStore = 0x1, - Dependencies = 0x2, + PackagedAPI = 0x2, + Dependencies = 0x4, Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command diff --git a/src/AppInstallerCommonCore/Public/winget/TraceLogger.h b/src/AppInstallerCommonCore/Public/winget/TraceLogger.h new file mode 100644 index 0000000000..f70d2672cd --- /dev/null +++ b/src/AppInstallerCommonCore/Public/winget/TraceLogger.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include +#include + +#include +#include + +namespace AppInstaller::Logging +{ + // Log ETW events for tracing. + // Doesn't save events to a file on disk. + struct TraceLogger : ILogger + { + TraceLogger() = default; + + ~TraceLogger() = default; + + // ILogger + std::string GetName() const override; + void Write(Channel channel, Level, std::string_view message) noexcept override; + }; +} diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 3a4467d545..455e6a5026 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -76,6 +76,7 @@ namespace AppInstaller::Settings NetworkDOProgressTimeoutInSeconds, InstallLocalePreference, InstallLocaleRequirement, + EFPackagedAPI, Max }; @@ -122,6 +123,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDOProgressTimeoutInSeconds, uint32_t, std::chrono::seconds, 60s, ".network.doProgressTimeoutInSeconds"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocalePreference, std::vector, std::vector, {}, ".installBehavior.preferences.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocaleRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.locale"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFPackagedAPI, bool, bool, false, ".experimentalFeatures.packagedAPI"sv); // Used to deduce the SettingVariant type; making a variant that includes std::monostate and all SettingMapping types. template diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index ef56fdf9ba..667bc39a39 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -88,6 +88,16 @@ namespace AppInstaller::Runtime return knownFolder.get(); } + // Gets the user's temp path + std::filesystem::path GetPathToUserTemp() + { + wchar_t tempPath[MAX_PATH + 1]; + DWORD tempChars = GetTempPathW(ARRAYSIZE(tempPath), tempPath); + THROW_LAST_ERROR_IF(!tempChars); + THROW_HR_IF(E_UNEXPECTED, tempChars > ARRAYSIZE(tempPath)); + return { std::wstring_view{ tempPath, static_cast(tempChars) } }; + } + // Gets the path to the appdata root. // *Only used by non packaged version!* std::filesystem::path GetPathToAppDataRoot() @@ -225,8 +235,10 @@ namespace AppInstaller::Runtime switch (path) { case PathName::Temp: - result.assign(appStorage.TemporaryFolder().Path().c_str()); + { + result = GetPathToUserTemp(); result /= s_DefaultTempDirectory; + } break; case PathName::LocalState: case PathName::UserFileSettings: @@ -297,16 +309,14 @@ namespace AppInstaller::Runtime case PathName::Temp: case PathName::DefaultLogLocation: { - wchar_t tempPath[MAX_PATH + 1]; - DWORD tempChars = GetTempPathW(ARRAYSIZE(tempPath), tempPath); - result.assign(std::wstring_view{ tempPath, static_cast(tempChars) }); - + result = GetPathToUserTemp(); result /= s_DefaultTempDirectory; } break; case PathName::DefaultLogLocationForDisplay: result.assign("%TEMP%"); result /= s_DefaultTempDirectory; + create = false; break; case PathName::LocalState: result = GetPathToAppDataDir(s_AppDataDir_State); @@ -343,18 +353,12 @@ namespace AppInstaller::Runtime if (create && result.is_absolute()) { - if (std::filesystem::exists(result)) - { - if (!std::filesystem::is_directory(result)) - { - // STATUS_NOT_A_DIRECTORY: A requested opened file is not a directory. - THROW_NTSTATUS_MSG(0xC0000103, "Location is not a directory"); - } - } - else + if (std::filesystem::exists(result) && !std::filesystem::is_directory(result)) { - std::filesystem::create_directories(result); + std::filesystem::remove(result); } + + std::filesystem::create_directories(result); } return result; diff --git a/src/AppInstallerCommonCore/SHA256.cpp b/src/AppInstallerCommonCore/SHA256.cpp index 2553572f4b..ff21a176cd 100644 --- a/src/AppInstallerCommonCore/SHA256.cpp +++ b/src/AppInstallerCommonCore/SHA256.cpp @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. - #include #define WIN32_NO_STATUS #include #include "Public/AppInstallerSHA256.h" #include "Public/AppInstallerRuntime.h" +#include "Public/AppInstallerErrors.h" using namespace AppInstaller::Runtime; @@ -108,7 +108,7 @@ namespace AppInstaller::Utility { return std::string(resultBuffer); } - std::vector SHA256::ConvertToBytes(const std::string& hashStr) + SHA256::HashBuffer SHA256::ConvertToBytes(const std::string& hashStr) { if (hashStr.size() != HashStringSizeInChars) { @@ -116,7 +116,7 @@ namespace AppInstaller::Utility { } auto hashCStr = hashStr.c_str(); - std::vector resultBuffer; + SHA256::HashBuffer resultBuffer; resultBuffer.resize(HashBufferSizeInBytes); @@ -128,34 +128,42 @@ namespace AppInstaller::Utility { return resultBuffer; } - std::vector SHA256::ComputeHash(const std::uint8_t* buffer, std::uint32_t cbBuffer) + SHA256::HashBuffer SHA256::ComputeHash(const std::uint8_t* buffer, std::uint32_t cbBuffer) { SHA256 hasher; hasher.Add(buffer, cbBuffer); - - std::vector result; - hasher.Get(result); - - return result; + return hasher.Get(); } - std::vector SHA256::ComputeHash(std::istream& in) + SHA256::HashBuffer SHA256::ComputeHash(std::istream& in) { + // Throw exceptions on badbit + auto excState = in.exceptions(); + auto revertExcState = wil::scope_exit([excState, &in]() { in.exceptions(excState); }); + in.exceptions(std::ios_base::badbit); + const int bufferSize = 1024 * 1024; // 1MB auto buffer = std::make_unique(bufferSize); SHA256 hasher; - while (!in.eof()) + while (in.good()) { in.read((char*)(buffer.get()), bufferSize); - hasher.Add(buffer.get(), static_cast(in.gcount())); + if (in.gcount()) + { + hasher.Add(buffer.get(), static_cast(in.gcount())); + } } - std::vector result; - hasher.Get(result); - - return result; + if (in.eof()) + { + return hasher.Get(); + } + else + { + THROW_HR(APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE); + } } void SHA256::SHA256ContextDeleter::operator()(SHA256Context* context) diff --git a/src/AppInstallerCommonCore/Telemetry/TraceLogging.cpp b/src/AppInstallerCommonCore/Telemetry/TraceLogging.cpp index 06ce64dff2..f069f08e5c 100644 --- a/src/AppInstallerCommonCore/Telemetry/TraceLogging.cpp +++ b/src/AppInstallerCommonCore/Telemetry/TraceLogging.cpp @@ -6,7 +6,7 @@ // GUID for Microsoft.PackageManager.Client : {c0cf606f-569b-5c20-27d9-88a745fa2175} TRACELOGGING_DEFINE_PROVIDER( - g_hTelemetryProvider, + g_hTraceProvider, "Microsoft.PackageManager.Client", (0xc0cf606f, 0x569b, 0x5c20, 0x27, 0xd9, 0x88, 0xa7, 0x45, 0xfa, 0x21, 0x75), TraceLoggingOptionMicrosoftTelemetry()); @@ -14,7 +14,6 @@ TRACELOGGING_DEFINE_PROVIDER( bool g_IsTelemetryProviderEnabled{}; UCHAR g_TelemetryProviderLevel{}; ULONGLONG g_TelemetryProviderMatchAnyKeyword{}; -GUID g_TelemetryProviderActivityId{}; void WINAPI TelemetryProviderEnabledCallback( _In_ LPCGUID /*sourceId*/, @@ -32,27 +31,10 @@ void WINAPI TelemetryProviderEnabledCallback( void RegisterTraceLogging() { - HRESULT hr = S_OK; - - TraceLoggingRegisterEx(g_hTelemetryProvider, TelemetryProviderEnabledCallback, nullptr); - //Generate the ActivityId used to track the session - hr = CoCreateGuid(&g_TelemetryProviderActivityId); - if (FAILED(hr)) - { - TraceLoggingWriteActivity( - g_hTelemetryProvider, - "CreateGuidError", - nullptr, - nullptr, - TraceLoggingHResult(hr), - TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), - TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); - - g_TelemetryProviderActivityId = GUID_NULL; - }; + TraceLoggingRegisterEx(g_hTraceProvider, TelemetryProviderEnabledCallback, nullptr); } void UnRegisterTraceLogging() { - TraceLoggingUnregister(g_hTelemetryProvider); + TraceLoggingUnregister(g_hTraceProvider); } diff --git a/src/AppInstallerCommonCore/Telemetry/TraceLogging.h b/src/AppInstallerCommonCore/Telemetry/TraceLogging.h index b9d8337da4..8b3eb2065e 100644 --- a/src/AppInstallerCommonCore/Telemetry/TraceLogging.h +++ b/src/AppInstallerCommonCore/Telemetry/TraceLogging.h @@ -60,11 +60,10 @@ // TraceLogging provider name for telemetry. #define TELEMETRY_PROVIDER_NAME "Microsoft.PackageManager.Client" -TRACELOGGING_DECLARE_PROVIDER(g_hTelemetryProvider); +TRACELOGGING_DECLARE_PROVIDER(g_hTraceProvider); extern bool g_IsTelemetryProviderEnabled; extern UCHAR g_TelemetryProviderLevel; extern ULONGLONG g_TelemetryProviderMatchAnyKeyword; -extern GUID g_TelemetryProviderActivityId; extern void RegisterTraceLogging(); extern void UnRegisterTraceLogging(); diff --git a/src/AppInstallerCommonCore/TraceLogger.cpp b/src/AppInstallerCommonCore/TraceLogger.cpp new file mode 100644 index 0000000000..8a1289370f --- /dev/null +++ b/src/AppInstallerCommonCore/TraceLogger.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/TraceLogger.h" +#include "Public/AppInstallerTelemetry.h" + +namespace AppInstaller::Logging +{ + void TraceLogger::Write(Channel channel, Level, std::string_view message) noexcept try + { + // Send to a string first to create a single block to log to a trace. + std::stringstream strstr; + strstr << std::chrono::system_clock::now() << " [" << std::setw(GetMaxChannelNameLength()) << std::left << std::setfill(' ') << GetChannelName(channel) << "] " << message << std::endl; + + TraceLoggingWriteActivity(g_hTraceProvider, + "Diagnostics", + AppInstaller::Logging::GetActivityId(false), + nullptr, + TraceLoggingString(strstr.str().c_str(), "LogMessage")); + } + catch (...) + { + // Just eat any exceptions here; better to lose logs than functionality + } + + std::string TraceLogger::GetName() const + { + return "Trace"; + } +} diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 81e63813eb..0025663598 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -227,6 +227,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFExperimentalMSStore) WINGET_VALIDATE_PASS_THROUGH(EFDependencies) WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) + WINGET_VALIDATE_PASS_THROUGH(EFPackagedAPI) WINGET_VALIDATE_SIGNATURE(InstallScopePreference) { diff --git a/src/AppInstallerCommonCore/packages.config b/src/AppInstallerCommonCore/packages.config index bca9425304..5e899619db 100644 --- a/src/AppInstallerCommonCore/packages.config +++ b/src/AppInstallerCommonCore/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index a40933659a..de08205e68 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -1,6 +1,6 @@  - + true true @@ -337,15 +337,15 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index a6245afcfe..21ec7c1a9a 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -436,92 +436,97 @@ namespace AppInstaller::Repository { CompositeResult result; - // Search installed source - SearchResult installedResult = m_installedSource->Search(request); - result.Truncated = installedResult.Truncated; - - for (auto&& match : installedResult.Matches) + // If the search behavior is for AllPackages or Installed then the result can contain packages that are + // only in the Installed source, but do not have an AvailableVersion. + if (m_searchBehavior == CompositeSearchBehavior::AllPackages || m_searchBehavior == CompositeSearchBehavior::Installed) { - auto compositePackage = std::make_shared(std::move(match.Package)); - - // Create a search request to run against all available sources - // TODO: Determine if we should create a single search or one for each installed package. - SearchRequest systemReferenceSearch; + // Search installed source + SearchResult installedResult = m_installedSource->Search(request); + result.Truncated = installedResult.Truncated; - auto installedVersion = compositePackage->GetInstalledVersion(); - auto installedPackageData = result.GetSystemReferenceStrings(installedVersion.get()); - - if (!installedPackageData.SystemReferenceStrings.empty()) + for (auto&& match : installedResult.Matches) { - for (const auto& srs : installedPackageData.SystemReferenceStrings) - { - srs.AddToFilters(systemReferenceSearch.Inclusions); - } + auto compositePackage = std::make_shared(std::move(match.Package)); - std::shared_ptr availablePackage; + // Create a search request to run against all available sources + // TODO: Determine if we should create a single search or one for each installed package. + SearchRequest systemReferenceSearch; - // Search sources and add to result - for (const auto& source : m_availableSources) - { - SearchResult availableResult = source->Search(systemReferenceSearch); + auto installedVersion = compositePackage->GetInstalledVersion(); + auto installedPackageData = result.GetSystemReferenceStrings(installedVersion.get()); - if (availableResult.Matches.empty()) + if (!installedPackageData.SystemReferenceStrings.empty()) + { + for (const auto& srs : installedPackageData.SystemReferenceStrings) { - continue; + srs.AddToFilters(systemReferenceSearch.Inclusions); } - if (availableResult.Matches.size() == 1) - { - availablePackage = std::move(availableResult.Matches[0].Package); - } - else // availableResult.Matches.size() > 1 + std::shared_ptr availablePackage; + + // Search sources and add to result + for (const auto& source : m_availableSources) { - AICLI_LOG(Repo, Info, - << "Found multiple matches for installed package [" << installedVersion->GetProperty(PackageVersionProperty::Id) << - "] in source [" << source->GetIdentifier() << "] when searching for [" << systemReferenceSearch.ToString() << "]"); + SearchResult availableResult = source->Search(systemReferenceSearch); - // More than one match found for the system reference; run some heuristics to check for a match - for (auto&& availableMatch : availableResult.Matches) + if (availableResult.Matches.empty()) + { + continue; + } + + if (availableResult.Matches.size() == 1) + { + availablePackage = std::move(availableResult.Matches[0].Package); + } + else // availableResult.Matches.size() > 1 { - AICLI_LOG(Repo, Info, << " Checking match with package id: " << - availableMatch.Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Id)); + AICLI_LOG(Repo, Info, + << "Found multiple matches for installed package [" << installedVersion->GetProperty(PackageVersionProperty::Id) << + "] in source [" << source->GetIdentifier() << "] when searching for [" << systemReferenceSearch.ToString() << "]"); - if (IsStrongMatchField(availableMatch.MatchCriteria.Field)) + // More than one match found for the system reference; run some heuristics to check for a match + for (auto&& availableMatch : availableResult.Matches) { - if (!availablePackage) - { - availablePackage = std::move(availableMatch.Package); - } - else + AICLI_LOG(Repo, Info, << " Checking match with package id: " << + availableMatch.Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Id)); + + if (IsStrongMatchField(availableMatch.MatchCriteria.Field)) { - AICLI_LOG(Repo, Info, << " Found multiple packages with strong match fields"); - availablePackage.reset(); - break; + if (!availablePackage) + { + availablePackage = std::move(availableMatch.Package); + } + else + { + AICLI_LOG(Repo, Info, << " Found multiple packages with strong match fields"); + availablePackage.reset(); + break; + } } } - } - if (!availablePackage) - { - AICLI_LOG(Repo, Warning, << " Appropriate available package could not be determined"); + if (!availablePackage) + { + AICLI_LOG(Repo, Warning, << " Appropriate available package could not be determined"); + } } + + // We found some matching packages here, don't keep going + break; } - // We found some matching packages here, don't keep going - break; + compositePackage->SetAvailablePackage(std::move(availablePackage)); } - compositePackage->SetAvailablePackage(std::move(availablePackage)); + // Move the installed result into the composite result + result.Matches.emplace_back(std::move(compositePackage), std::move(match.MatchCriteria)); } - // Move the installed result into the composite result - result.Matches.emplace_back(std::move(compositePackage), std::move(match.MatchCriteria)); - } - - // Optimization for the "everything installed" case, no need to allow for reverse correlations - if (request.IsForEverything()) - { - return std::move(result); + // Optimization for the "everything installed" case, no need to allow for reverse correlations + if (request.IsForEverything() && m_searchBehavior == CompositeSearchBehavior::Installed) + { + return std::move(result); + } } // Search available sources @@ -559,7 +564,7 @@ namespace AppInstaller::Repository } // If there was no correlation for this package, add it without one. - if (m_searchBehavior == CompositeSearchBehavior::AllPackages && !foundInstalledMatch) + if ((m_searchBehavior == CompositeSearchBehavior::AllPackages || m_searchBehavior == CompositeSearchBehavior::AvailablePackages) && !foundInstalledMatch) { result.Matches.emplace_back(std::make_shared(std::shared_ptr{}, std::move(match.Package)), match.MatchCriteria); } diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h index 08e43463ae..9a75410f3e 100644 --- a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h +++ b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h @@ -117,6 +117,9 @@ namespace AppInstaller::Repository // Passing an empty string as the name of the source will return a source that aggregates all others. OpenSourceResult OpenSource(std::string_view name, IProgressCallback& progress); + // Opens an existing source. + OpenSourceResult OpenSourceFromDetails(SourceDetails& details, IProgressCallback& progress); + // A predefined source. // These sources are not under the direct control of the user, such as packages installed on the system. enum class PredefinedSource @@ -126,6 +129,16 @@ namespace AppInstaller::Repository MSIX, }; + // A well known source. + // These come with the app and can be disabled but not removed. + enum class WellKnownSource + { + WinGet, + }; + + SourceDetails GetPredefinedSourceDetails(PredefinedSource source); + SourceDetails GetWellKnownSourceDetails(WellKnownSource source); + // Opens a predefined source. // These sources are not under the direct control of the user, such as packages installed on the system. std::shared_ptr OpenPredefinedSource(PredefinedSource source, IProgressCallback& progress); @@ -139,6 +152,8 @@ namespace AppInstaller::Repository Installed, // Search both installed and available packages. AllPackages, + // Search only available packages. + AvailablePackages, }; // Creates a source that merges the installed packages with the given available packages. @@ -148,6 +163,13 @@ namespace AppInstaller::Repository const std::shared_ptr& availableSource, CompositeSearchBehavior searchBehavior = CompositeSearchBehavior::Installed); + // Creates a source that merges the installed packages with the given available packages from multiple sources. + // The source can search for installed packages only, or also include non-installed available packages. + std::shared_ptr CreateCompositeSource( + const std::shared_ptr& installedSource, + const std::vector>& availableSources, + CompositeSearchBehavior searchBehavior = CompositeSearchBehavior::Installed); + // Updates an existing source. // Return value indicates whether the named source was found. bool UpdateSource(std::string_view name, IProgressCallback& progress); diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp index c6abea4e11..22910b9eb2 100644 --- a/src/AppInstallerRepositoryCore/RepositorySource.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -42,7 +42,7 @@ namespace AppInstaller::Repository namespace { - // SourceDetails with additional data used by this file. + // SourceDetails with additional data. struct SourceDetailsInternal : public SourceDetails { // If true, this is a tombstone, marking the deletion of a source at a lower priority origin. @@ -684,6 +684,8 @@ namespace AppInstaller::Repository void AddSource(const SourceDetailsInternal& source); void RemoveSource(const SourceDetailsInternal& source); + void UpdateSourceLastUpdateTime(const SourceDetails& source); + // Save source metadata. Currently only LastTimeUpdated is used. void SaveMetadata() const; @@ -901,10 +903,10 @@ namespace AppInstaller::Repository OpenSourceResult OpenSource(std::string_view name, IProgressCallback& progress) { SourceListInternal sourceList; - auto currentSources = sourceList.GetCurrentSourceRefs(); if (name.empty()) { + auto currentSources = sourceList.GetCurrentSourceRefs(); if (currentSources.empty()) { AICLI_LOG(Repo, Info, << "Default source requested, but no sources configured"); @@ -988,7 +990,51 @@ namespace AppInstaller::Repository } } + OpenSourceResult OpenSourceFromDetails(SourceDetails& details, IProgressCallback& progress) + { + OpenSourceResult result; + + // Get the details again by name from the source list because SaveMetadata only updates the LastUpdateTime + // if the details came from the same instance of the list that's being saved. + SourceListInternal sourceList; + auto source = sourceList.GetSource(details.Name); + if (!source) + { + AICLI_LOG(Repo, Info, << "Named source no longer found. Source may have been removed by the user: " << details.Name); + return {}; + } + else if (source->IsTombstone) + { + AICLI_LOG(Repo, Info, << "Named source no longer found. Source was tombstoned: " << details.Name); + return {}; + } + if (ShouldUpdateBeforeOpen(*source)) + { + try + { + if (BackgroundUpdateSourceFromDetails(*source, progress)) + { + sourceList.SaveMetadata(); + } + } + catch (...) + { + AICLI_LOG(Repo, Warning, << "Failed to update source: " << details.Name); + result.SourcesWithUpdateFailure.emplace_back(*source); + } + } + + result.Source = CreateSourceFromDetails(details, progress); + return result; + } + std::shared_ptr OpenPredefinedSource(PredefinedSource source, IProgressCallback& progress) + { + SourceDetails details = GetPredefinedSourceDetails(source); + return CreateSourceFromDetails(details, progress); + } + + SourceDetails GetPredefinedSourceDetails(PredefinedSource source) { SourceDetails details; details.Origin = SourceOrigin::Predefined; @@ -998,15 +1044,34 @@ namespace AppInstaller::Repository case PredefinedSource::Installed: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::None); - return CreateSourceFromDetails(details, progress); + return details; case PredefinedSource::ARP: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::ARP); - return CreateSourceFromDetails(details, progress); + return details; case PredefinedSource::MSIX: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::MSIX); - return CreateSourceFromDetails(details, progress); + return details; + } + + THROW_HR(E_UNEXPECTED); + } + + SourceDetails GetWellKnownSourceDetails(WellKnownSource source) + { + switch (source) + { + case WellKnownSource::WinGet: + SourceDetailsInternal details; + details.Origin = SourceOrigin::Default; + details.Name = s_Source_WingetCommunityDefault_Name; + details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Arg = s_Source_WingetCommunityDefault_Arg; + details.Data = s_Source_WingetCommunityDefault_Data; + details.Identifier = s_Source_WingetCommunityDefault_Identifier; + details.TrustLevel = SourceTrustLevel::Trusted | SourceTrustLevel::StoreOrigin; + return details; } THROW_HR(E_UNEXPECTED); @@ -1027,6 +1092,26 @@ namespace AppInstaller::Repository return result; } + std::shared_ptr CreateCompositeSource( + const std::shared_ptr& installedSource, + const std::vector>& availableSources, + CompositeSearchBehavior searchBehavior) + { + std::shared_ptr result = std::make_shared("*CompositeSource"); + + for (const auto& availableSource : availableSources) + { + result->AddAvailableSource(availableSource); + } + + if (installedSource) + { + result->SetInstalledSource(installedSource, searchBehavior); + } + + return result; + } + bool UpdateSource(std::string_view name, IProgressCallback& progress) { THROW_HR_IF(E_INVALIDARG, name.empty()); diff --git a/src/AppInstallerRepositoryCore/packages.config b/src/AppInstallerRepositoryCore/packages.config index bca9425304..5e899619db 100644 --- a/src/AppInstallerRepositoryCore/packages.config +++ b/src/AppInstallerRepositoryCore/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp new file mode 100644 index 0000000000..5d61bb6904 --- /dev/null +++ b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include +#include +#include "CatalogPackage.h" +#include "CatalogPackage.g.cpp" +#include "PackageCatalog.h" +#include "PackageVersionInfo.h" +#include "PackageVersionId.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void CatalogPackage::Initialize( + std::shared_ptr source, + std::shared_ptr<::AppInstaller::Repository::IPackage> package) + { + m_source = std::move(source); + m_package = std::move(package); + } + hstring CatalogPackage::Id() + { + return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Id).get()); + } + hstring CatalogPackage::Name() + { + return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Name)); + } + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::InstalledVersion() + { + std::call_once(m_installedVersionOnceFlag, + [&]() + { + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> installedVersion = m_package.get()->GetInstalledVersion(); + if (installedVersion) + { + auto installedVersionImpl = winrt::make_self>(); + installedVersionImpl->Initialize(std::move(installedVersion)); + m_installedVersion = *installedVersionImpl; + } + }); + return m_installedVersion; + } + Windows::Foundation::Collections::IVectorView CatalogPackage::AvailableVersions() + { + std::call_once(m_availableVersionsOnceFlag, + [&]() + { + // Vector hasn't been populated yet. + for (auto const& versionKey : m_package.get()->GetAvailableVersionKeys()) + { + auto packageVersionId = winrt::make_self>(); + packageVersionId->Initialize(versionKey); + m_availableVersions.Append(*packageVersionId); + } + }); + return m_availableVersions.GetView(); + } + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::DefaultInstallVersion() + { + std::call_once(m_defaultInstallVersionOnceFlag, + [&]() + { + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> latestVersion = m_package.get()->GetLatestAvailableVersion(); + if (latestVersion) + { + // DefaultInstallVersion hasn't been created yet, create and populate it. + // DefaultInstallVersion is the LatestAvailableVersion of the internal package object. + auto latestVersionImpl = winrt::make_self>(); + latestVersionImpl->Initialize(std::move(latestVersion)); + m_defaultInstallVersion = *latestVersionImpl; + } + }); + return m_defaultInstallVersion; + } + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::GetPackageVersionInfo(Microsoft::Management::Deployment::PackageVersionId const& versionKey) + { + winrt::Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo{ nullptr }; + + ::AppInstaller::Repository::PackageVersionKey internalVersionKey(winrt::to_string(versionKey.PackageCatalogId()), winrt::to_string(versionKey.Version()), winrt::to_string(versionKey.Channel())); + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> availableVersion = m_package.get()->GetAvailableVersion(internalVersionKey); + if (availableVersion) + { + auto packageVersionInfoImpl = winrt::make_self>(); + packageVersionInfoImpl->Initialize(std::move(availableVersion)); + packageVersionInfo =*packageVersionInfoImpl; + } + return packageVersionInfo; + } + bool CatalogPackage::IsUpdateAvailable() + { + return m_package->IsUpdateAvailable(); + } +} diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h new file mode 100644 index 0000000000..6fdd3b6fbf --- /dev/null +++ b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "CatalogPackage.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct CatalogPackage : CatalogPackageT + { + CatalogPackage() = default; + void Initialize( + std::shared_ptr source, + std::shared_ptr<::AppInstaller::Repository::IPackage> package); + + hstring Id(); + hstring Name(); + winrt::Microsoft::Management::Deployment::PackageVersionInfo InstalledVersion(); + winrt::Windows::Foundation::Collections::IVectorView AvailableVersions(); + winrt::Microsoft::Management::Deployment::PackageVersionInfo DefaultInstallVersion(); + winrt::Microsoft::Management::Deployment::PackageVersionInfo GetPackageVersionInfo(winrt::Microsoft::Management::Deployment::PackageVersionId const& versionKey); + bool IsUpdateAvailable(); + private: + std::shared_ptr m_source; + std::shared_ptr<::AppInstaller::Repository::IPackage> m_package; + Windows::Foundation::Collections::IVector m_availableVersions{ winrt::single_threaded_vector() }; + winrt::Microsoft::Management::Deployment::PackageVersionInfo m_installedVersion{ nullptr }; + winrt::Microsoft::Management::Deployment::PackageVersionInfo m_defaultInstallVersion{ nullptr }; + std::once_flag m_installedVersionOnceFlag; + std::once_flag m_availableVersionsOnceFlag; + std::once_flag m_defaultInstallVersionOnceFlag; + }; +} \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/ConnectResult.cpp b/src/Microsoft.Management.Deployment/ConnectResult.cpp new file mode 100644 index 0000000000..6bd690f133 --- /dev/null +++ b/src/Microsoft.Management.Deployment/ConnectResult.cpp @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConnectResult.h" +#include "ConnectResult.g.cpp" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void ConnectResult::Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus status, winrt::Microsoft::Management::Deployment::PackageCatalog packageCatalog) + { + m_status = status; + m_packageCatalog = packageCatalog; + } + winrt::Microsoft::Management::Deployment::ConnectResultStatus ConnectResult::Status() + { + return m_status; + } + winrt::Microsoft::Management::Deployment::PackageCatalog ConnectResult::PackageCatalog() + { + return m_packageCatalog; + } +} diff --git a/src/Microsoft.Management.Deployment/ConnectResult.h b/src/Microsoft.Management.Deployment/ConnectResult.h new file mode 100644 index 0000000000..2e969175dc --- /dev/null +++ b/src/Microsoft.Management.Deployment/ConnectResult.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConnectResult.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct ConnectResult : ConnectResultT + { + ConnectResult() = default; + void Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus status, winrt::Microsoft::Management::Deployment::PackageCatalog packageCatalog); + + winrt::Microsoft::Management::Deployment::ConnectResultStatus Status(); + winrt::Microsoft::Management::Deployment::PackageCatalog PackageCatalog(); + private: + winrt::Microsoft::Management::Deployment::ConnectResultStatus m_status = winrt::Microsoft::Management::Deployment::ConnectResultStatus::Ok; + winrt::Microsoft::Management::Deployment::PackageCatalog m_packageCatalog{ nullptr }; + }; +} diff --git a/src/Microsoft.Management.Deployment/Converters.cpp b/src/Microsoft.Management.Deployment/Converters.cpp new file mode 100644 index 0000000000..4861500201 --- /dev/null +++ b/src/Microsoft.Management.Deployment/Converters.cpp @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include +#include +#include "Microsoft/PredefinedInstalledSourceFactory.h" +#include "Workflows/WorkflowBase.h" +#include "Converters.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + Microsoft::Management::Deployment::PackageMatchField GetDeploymentMatchField(::AppInstaller::Repository::PackageMatchField field) + { + Microsoft::Management::Deployment::PackageMatchField matchField = Microsoft::Management::Deployment::PackageMatchField::Id; + switch (field) + { + case ::AppInstaller::Repository::PackageMatchField::Command: + matchField = Microsoft::Management::Deployment::PackageMatchField::Command; + break; + case ::AppInstaller::Repository::PackageMatchField::Id: + matchField = Microsoft::Management::Deployment::PackageMatchField::Id; + break; + case ::AppInstaller::Repository::PackageMatchField::Moniker: + matchField = Microsoft::Management::Deployment::PackageMatchField::Moniker; + break; + case ::AppInstaller::Repository::PackageMatchField::Name: + matchField = Microsoft::Management::Deployment::PackageMatchField::Name; + break; + case ::AppInstaller::Repository::PackageMatchField::Tag: + matchField = Microsoft::Management::Deployment::PackageMatchField::Tag; + break; + default: + matchField = Microsoft::Management::Deployment::PackageMatchField::Id; + break; + } + return matchField; + } + + ::AppInstaller::Repository::PackageMatchField GetRepositoryMatchField(Microsoft::Management::Deployment::PackageMatchField field) + { + ::AppInstaller::Repository::PackageMatchField matchField = ::AppInstaller::Repository::PackageMatchField::Id; + switch (field) + { + case Microsoft::Management::Deployment::PackageMatchField::Command: + matchField = ::AppInstaller::Repository::PackageMatchField::Command; + break; + case Microsoft::Management::Deployment::PackageMatchField::Id: + matchField = ::AppInstaller::Repository::PackageMatchField::Id; + break; + case Microsoft::Management::Deployment::PackageMatchField::Moniker: + matchField = ::AppInstaller::Repository::PackageMatchField::Moniker; + break; + case Microsoft::Management::Deployment::PackageMatchField::Name: + matchField = ::AppInstaller::Repository::PackageMatchField::Name; + break; + case Microsoft::Management::Deployment::PackageMatchField::Tag: + matchField = ::AppInstaller::Repository::PackageMatchField::Tag; + break; + default: + matchField = ::AppInstaller::Repository::PackageMatchField::Id; + break; + } + return matchField; + } + + Microsoft::Management::Deployment::PackageFieldMatchOption GetDeploymentMatchOption(::AppInstaller::Repository::MatchType type) + { + Microsoft::Management::Deployment::PackageFieldMatchOption matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; + switch (type) + { + case ::AppInstaller::Repository::MatchType::CaseInsensitive: + matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::EqualsCaseInsensitive; + break; + case ::AppInstaller::Repository::MatchType::Exact: + matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; + break; + case ::AppInstaller::Repository::MatchType::StartsWith: + matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::StartsWithCaseInsensitive; + break; + case ::AppInstaller::Repository::MatchType::Substring: + matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::ContainsCaseInsensitive; + break; + default: + matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; + break; + } + return matchOption; + } + + ::AppInstaller::Repository::MatchType GetRepositoryMatchType(Microsoft::Management::Deployment::PackageFieldMatchOption option) + { + ::AppInstaller::Repository::MatchType packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Exact; + switch (option) + { + case Microsoft::Management::Deployment::PackageFieldMatchOption::EqualsCaseInsensitive: + packageFieldMatchOption = ::AppInstaller::Repository::MatchType::CaseInsensitive; + break; + case Microsoft::Management::Deployment::PackageFieldMatchOption::Equals: + packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Exact; + break; + case Microsoft::Management::Deployment::PackageFieldMatchOption::StartsWithCaseInsensitive: + packageFieldMatchOption = ::AppInstaller::Repository::MatchType::StartsWith; + break; + case Microsoft::Management::Deployment::PackageFieldMatchOption::ContainsCaseInsensitive: + packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Substring; + break; + default: + packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Exact; + break; + } + return packageFieldMatchOption; + } + + ::AppInstaller::Repository::CompositeSearchBehavior GetRepositoryCompositeSearchBehavior(Microsoft::Management::Deployment::CompositeSearchBehavior searchBehavior) + { + ::AppInstaller::Repository::CompositeSearchBehavior repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AllPackages; + switch (searchBehavior) + { + case Microsoft::Management::Deployment::CompositeSearchBehavior::LocalCatalogs: + repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::Installed; + break; + case Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs: + repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AvailablePackages; + break; + case Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromAllCatalogs: + repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AvailablePackages; + break; + case Microsoft::Management::Deployment::CompositeSearchBehavior::AllCatalogs: + default: + repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AllPackages; + break; + } + return repositorySearchBehavior; + } + + ::AppInstaller::Repository::PackageVersionMetadata GetRepositoryPackageVersionMetadata(Microsoft::Management::Deployment::PackageVersionMetadataField packageVersionMetadataField) + { + ::AppInstaller::Repository::PackageVersionMetadata metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledLocation; + switch (packageVersionMetadataField) + { + case Microsoft::Management::Deployment::PackageVersionMetadataField::InstalledLocation: + metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledLocation; + break; + case Microsoft::Management::Deployment::PackageVersionMetadataField::InstalledScope: + metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledScope; + break; + case Microsoft::Management::Deployment::PackageVersionMetadataField::InstallerType: + metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledType; + break; + case Microsoft::Management::Deployment::PackageVersionMetadataField::PublisherDisplayName: + metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::Publisher; + break; + case Microsoft::Management::Deployment::PackageVersionMetadataField::SilentUninstallCommand: + metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::SilentUninstallCommand; + break; + case Microsoft::Management::Deployment::PackageVersionMetadataField::StandardUninstallCommand: + metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::StandardUninstallCommand; + break; + } + return metadataKey; + } + + winrt::Microsoft::Management::Deployment::InstallResultStatus GetInstallResultStatus(::AppInstaller::CLI::Workflow::ExecutionStage executionStage, winrt::hresult hresult) + { + winrt::Microsoft::Management::Deployment::InstallResultStatus resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::Ok; + + // Map some known hresults to specific statuses, otherwise use the execution stage to determine the status. + switch (hresult) + { + case S_OK: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::Ok; + break; + case APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY: + case APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY: + case APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED: + case APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::BlockedByPolicy; + break; + case APPINSTALLER_CLI_ERROR_INVALID_MANIFEST: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::ManifestError; + break; + case E_INVALIDARG: + case APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InvalidOptions; + break; + case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::NoApplicableInstallers; + break; + case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: + case APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED: + case APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY: + case APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION: + case APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE: + case APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA: + case APPINSTALLER_CLI_ERROR_LIBYAML_ERROR: + case APPINSTALLER_CLI_ERROR_INTERNAL_ERROR: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; + break; + default: + switch (executionStage) + { + case ::AppInstaller::CLI::Workflow::ExecutionStage::Initial: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::ParseArgs: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InvalidOptions; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Discovery: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::CatalogError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Download: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::DownloadError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::PreExecution: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Execution: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InstallError; + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::PostExecution: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; + break; + default: + resultStatus = winrt::Microsoft::Management::Deployment::InstallResultStatus::InternalError; + break; + } + } + + return resultStatus; + } + + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResultStatus(winrt::hresult hresult) + { + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; + switch (hresult) + { + case(S_OK): + resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; + break; + case APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY: + resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::BlockedByPolicy; + break; + case APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE: + case APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA: + case APPINSTALLER_CLI_ERROR_RESTSOURCE_INTERNAL_ERROR: + case APPINSTALLER_CLI_ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE: + case APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION: + case APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE: + resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::CatalogError; + break; + case E_INVALIDARG: + case APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS: + resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::InvalidOptions; + break; + case APPINSTALLER_CLI_ERROR_COMMAND_FAILED: + case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: + case APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED: + default: + resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::InternalError; + break; + } + return resultStatus; + } + + bool IsLocalPackageCatalog(winrt::Microsoft::Management::Deployment::PackageCatalogInfo info) + { + return (winrt::to_string(info.Type()).compare(::AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory::Type()) == 0); + } +} \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/Converters.h b/src/Microsoft.Management.Deployment/Converters.h new file mode 100644 index 0000000000..a8affa0bf7 --- /dev/null +++ b/src/Microsoft.Management.Deployment/Converters.h @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageMatchFilter.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + winrt::Microsoft::Management::Deployment::PackageMatchField GetDeploymentMatchField(::AppInstaller::Repository::PackageMatchField field); + ::AppInstaller::Repository::PackageMatchField GetRepositoryMatchField(winrt::Microsoft::Management::Deployment::PackageMatchField field); + winrt::Microsoft::Management::Deployment::PackageFieldMatchOption GetDeploymentMatchOption(::AppInstaller::Repository::MatchType type); + ::AppInstaller::Repository::MatchType GetRepositoryMatchType(winrt::Microsoft::Management::Deployment::PackageFieldMatchOption option); + ::AppInstaller::Repository::CompositeSearchBehavior GetRepositoryCompositeSearchBehavior(winrt::Microsoft::Management::Deployment::CompositeSearchBehavior searchBehavior); + ::AppInstaller::Repository::PackageVersionMetadata GetRepositoryPackageVersionMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField packageVersionMetadataField); + winrt::Microsoft::Management::Deployment::InstallResultStatus GetInstallResultStatus(::AppInstaller::CLI::Workflow::ExecutionStage executionStage, winrt::hresult hresult); + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResultStatus(winrt::hresult hresult); + bool IsLocalPackageCatalog(winrt::Microsoft::Management::Deployment::PackageCatalogInfo info); +} \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.cpp b/src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.cpp new file mode 100644 index 0000000000..d9834ddd93 --- /dev/null +++ b/src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.cpp @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 6388 Allow CreateInstance. +#include +// 4467 Allow use of uuid attribute for com object creation. +#include "CreateCompositePackageCatalogOptions.h" +#pragma warning( pop ) +#include "CreateCompositePackageCatalogOptions.g.cpp" +#include "Helpers.h" + +const GUID CreateCompositePackageCatalogOptionsCLSID1 = { 0x526534B8, 0x7E46, 0x47C8, { 0x84, 0x16, 0xB1, 0x68, 0x5C, 0x32, 0x7D, 0x37 } }; //526534B8-7E46-47C8-8416-B1685C327D37 +const GUID CreateCompositePackageCatalogOptionsCLSID2 = { 0x6444B10D, 0xFE84, 0x430F, { 0x93, 0x2B, 0x3D, 0x4F, 0xE5, 0x19, 0x5B, 0xDF } }; //6444B10D-FE84-430F-932B-3D4FE5195BDF + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + Windows::Foundation::Collections::IVector CreateCompositePackageCatalogOptions::Catalogs() + { + return m_catalogs; + } + winrt::Microsoft::Management::Deployment::CompositeSearchBehavior CreateCompositePackageCatalogOptions::CompositeSearchBehavior() + { + return m_compositeSearchBehavior; + } + void CreateCompositePackageCatalogOptions::CompositeSearchBehavior(winrt::Microsoft::Management::Deployment::CompositeSearchBehavior const& value) + { + m_compositeSearchBehavior = value; + } + CoCreatableCppWinRtClassWithCLSID(CreateCompositePackageCatalogOptions, 1, &CreateCompositePackageCatalogOptionsCLSID1); + CoCreatableCppWinRtClassWithCLSID(CreateCompositePackageCatalogOptions, 2, &CreateCompositePackageCatalogOptionsCLSID2); +} diff --git a/src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.h b/src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.h new file mode 100644 index 0000000000..0a1079a5ab --- /dev/null +++ b/src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "CreateCompositePackageCatalogOptions.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + [uuid("526534B8-7E46-47C8-8416-B1685C327D37")] + struct CreateCompositePackageCatalogOptions : CreateCompositePackageCatalogOptionsT + { + CreateCompositePackageCatalogOptions() = default; + + winrt::Windows::Foundation::Collections::IVector Catalogs(); + winrt::Microsoft::Management::Deployment::CompositeSearchBehavior CompositeSearchBehavior(); + void CompositeSearchBehavior(winrt::Microsoft::Management::Deployment::CompositeSearchBehavior const& value); + private: + winrt::Windows::Foundation::Collections::IVector m_catalogs{ winrt::single_threaded_vector() }; + winrt::Microsoft::Management::Deployment::CompositeSearchBehavior m_compositeSearchBehavior = winrt::Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromAllCatalogs; + }; +} +namespace winrt::Microsoft::Management::Deployment::factory_implementation +{ + struct CreateCompositePackageCatalogOptions : CreateCompositePackageCatalogOptionsT + { + }; +} diff --git a/src/Microsoft.Management.Deployment/FindPackagesOptions.cpp b/src/Microsoft.Management.Deployment/FindPackagesOptions.cpp new file mode 100644 index 0000000000..81d1f507d4 --- /dev/null +++ b/src/Microsoft.Management.Deployment/FindPackagesOptions.cpp @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 6388 Allow CreateInstance. +#include +// 4467 Allow use of uuid attribute for com object creation. +#include "FindPackagesOptions.h" +#pragma warning( pop ) +#include "FindPackagesOptions.g.cpp" +#include "Helpers.h" + +const GUID FindPackagesOptionsCLSID1 = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A +const GUID FindPackagesOptionsCLSID2 = { 0x2CAD6C15, 0xDF8E, 0x49DD, { 0xA7, 0x48, 0x96, 0xAD, 0xE0, 0xFE, 0x31, 0xB7 } }; //2CAD6C15-DF8E-49DD-A748-96ADE0FE31B7 + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + winrt::Windows::Foundation::Collections::IVector FindPackagesOptions::Selectors() + { + return m_selectors; + } + winrt::Windows::Foundation::Collections::IVector FindPackagesOptions::Filters() + { + return m_filters; + } + uint32_t FindPackagesOptions::ResultLimit() + { + return m_resultLimit; + } + void FindPackagesOptions::ResultLimit(uint32_t value) + { + m_resultLimit = value; + } + CoCreatableCppWinRtClassWithCLSID(FindPackagesOptions, 1, &FindPackagesOptionsCLSID1); + CoCreatableCppWinRtClassWithCLSID(FindPackagesOptions, 2, &FindPackagesOptionsCLSID2); +} diff --git a/src/Microsoft.Management.Deployment/FindPackagesOptions.h b/src/Microsoft.Management.Deployment/FindPackagesOptions.h new file mode 100644 index 0000000000..be828417d6 --- /dev/null +++ b/src/Microsoft.Management.Deployment/FindPackagesOptions.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "FindPackagesOptions.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct FindPackagesOptions : FindPackagesOptionsT + { + FindPackagesOptions() = default; + + winrt::Windows::Foundation::Collections::IVector Selectors(); + winrt::Windows::Foundation::Collections::IVector Filters(); + uint32_t ResultLimit(); + void ResultLimit(uint32_t value); + private: + uint32_t m_resultLimit = 0; + Windows::Foundation::Collections::IVector m_selectors{ + winrt::single_threaded_vector() }; + Windows::Foundation::Collections::IVector m_filters{ + winrt::single_threaded_vector() }; + }; +} +namespace winrt::Microsoft::Management::Deployment::factory_implementation +{ + struct FindPackagesOptions : FindPackagesOptionsT + { + }; +} diff --git a/src/Microsoft.Management.Deployment/FindPackagesResult.cpp b/src/Microsoft.Management.Deployment/FindPackagesResult.cpp new file mode 100644 index 0000000000..8ea847d031 --- /dev/null +++ b/src/Microsoft.Management.Deployment/FindPackagesResult.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "FindPackagesResult.h" +#include "FindPackagesResult.g.cpp" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void FindPackagesResult::Initialize( + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus status, + bool wasLimitExceeded, + Windows::Foundation::Collections::IVector matches) + { + m_status = status; + m_matches = matches; + m_wasLimitExceeded = wasLimitExceeded; + } + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResult::Status() + { + return m_status; + } + winrt::Windows::Foundation::Collections::IVectorView FindPackagesResult::Matches() + { + return m_matches.GetView(); + } + bool FindPackagesResult::WasLimitExceeded() + { + return m_wasLimitExceeded; + } +} diff --git a/src/Microsoft.Management.Deployment/FindPackagesResult.h b/src/Microsoft.Management.Deployment/FindPackagesResult.h new file mode 100644 index 0000000000..7b5d2e0b46 --- /dev/null +++ b/src/Microsoft.Management.Deployment/FindPackagesResult.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "FindPackagesResult.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct FindPackagesResult : FindPackagesResultT + { + FindPackagesResult() = default; + void Initialize( + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus errorCode, + bool wasLimitExceeded, + Windows::Foundation::Collections::IVector matches); + + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus Status(); + winrt::Windows::Foundation::Collections::IVectorView Matches(); + bool WasLimitExceeded(); + + private: + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus m_status = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; + Windows::Foundation::Collections::IVector m_matches{ + winrt::single_threaded_vector() }; + bool m_wasLimitExceeded = false; + }; +} diff --git a/src/Microsoft.Management.Deployment/Helpers.h b/src/Microsoft.Management.Deployment/Helpers.h new file mode 100644 index 0000000000..31ec2c7a64 --- /dev/null +++ b/src/Microsoft.Management.Deployment/Helpers.h @@ -0,0 +1,6 @@ +#pragma once + +//A version of CoCreatableCppWinRtClass that lets you pass in a uuid rather than getting it from a class property. +#define CoCreatableClassWithCLSIDWithFactory(className, instance, clsid, factory) \ + InternalWrlCreateCreatorMap(className##instance##_COM, clsid, nullptr, ::Microsoft::WRL::Details::CreateClassFactory, "minATL$__f") +#define CoCreatableCppWinRtClassWithCLSID(className, instance, clsid) CoCreatableClassWithCLSIDWithFactory(className, instance, clsid, ::wil::wrl_factory_for_winrt_com_class) \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/InstallOptions.cpp b/src/Microsoft.Management.Deployment/InstallOptions.cpp new file mode 100644 index 0000000000..56535c33f0 --- /dev/null +++ b/src/Microsoft.Management.Deployment/InstallOptions.cpp @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 6388 Allow CreateInstance. +#include +// 4467 Allow use of uuid attribute for com object creation. +#include "InstallOptions.h" +#pragma warning( pop ) +#include "InstallOptions.g.cpp" +#include "Helpers.h" + +const GUID InstallOptionsCLSID1 = { 0x1095F097, 0xEB96, 0x453B, 0xB4, 0xE6, 0x16, 0x13, 0x63, 0x7F, 0x3B, 0x14 }; //1095F097-EB96-453B-B4E6-1613637F3B14 +const GUID InstallOptionsCLSID2 = { 0x05F7019A, 0x8FAC, 0x4422, 0xBC, 0xD5, 0x4C, 0xB3, 0x4F, 0xFB, 0x44, 0xA8 }; //05F7019A-8FAC-4422-BCD5-4CB34FFB44A8 + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + winrt::Microsoft::Management::Deployment::PackageVersionId InstallOptions::PackageVersionId() + { + return m_packageVersionId; + } + void InstallOptions::PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value) + { + m_packageVersionId = value; + } + hstring InstallOptions::PreferredInstallLocation() + { + return hstring(m_preferredInstallLocation); + } + void InstallOptions::PreferredInstallLocation(hstring const& value) + { + m_preferredInstallLocation = value; + } + winrt::Microsoft::Management::Deployment::PackageInstallScope InstallOptions::PackageInstallScope() + { + return m_packageInstallScope; + } + void InstallOptions::PackageInstallScope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value) + { + m_packageInstallScope = value; + } + winrt::Microsoft::Management::Deployment::PackageInstallMode InstallOptions::PackageInstallMode() + { + return m_packageInstallMode; + } + void InstallOptions::PackageInstallMode(winrt::Microsoft::Management::Deployment::PackageInstallMode const& value) + { + m_packageInstallMode = value; + } + hstring InstallOptions::LogOutputPath() + { + return hstring(m_logOutputPath); + } + void InstallOptions::LogOutputPath(hstring const& value) + { + m_logOutputPath = value; + } + bool InstallOptions::AllowHashMismatch() + { + return m_allowHashMismatch; + } + void InstallOptions::AllowHashMismatch(bool value) + { + m_allowHashMismatch = value; + } + hstring InstallOptions::ReplacementInstallerArguments() + { + return hstring(m_replacementInstallerArguments); + } + void InstallOptions::ReplacementInstallerArguments(hstring const& value) + { + m_replacementInstallerArguments = value; + } + hstring InstallOptions::CorrelationData() + { + return hstring(m_correlationData); + } + void InstallOptions::CorrelationData(hstring const& value) + { + m_correlationData = value; + } + hstring InstallOptions::AdditionalPackageCatalogArguments() + { + return hstring(m_additionalPackageCatalogArguments); + } + void InstallOptions::AdditionalPackageCatalogArguments(hstring const& value) + { + m_additionalPackageCatalogArguments = value; + } + CoCreatableCppWinRtClassWithCLSID(InstallOptions, 1, &InstallOptionsCLSID1); + CoCreatableCppWinRtClassWithCLSID(InstallOptions, 2, &InstallOptionsCLSID2); +} diff --git a/src/Microsoft.Management.Deployment/InstallOptions.h b/src/Microsoft.Management.Deployment/InstallOptions.h new file mode 100644 index 0000000000..509936e03e --- /dev/null +++ b/src/Microsoft.Management.Deployment/InstallOptions.h @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "InstallOptions.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct InstallOptions : InstallOptionsT + { + InstallOptions() = default; + + winrt::Microsoft::Management::Deployment::PackageVersionId PackageVersionId(); + void PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value); + hstring PreferredInstallLocation(); + void PreferredInstallLocation(hstring const& value); + winrt::Microsoft::Management::Deployment::PackageInstallScope PackageInstallScope(); + void PackageInstallScope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value); + winrt::Microsoft::Management::Deployment::PackageInstallMode PackageInstallMode(); + void PackageInstallMode(winrt::Microsoft::Management::Deployment::PackageInstallMode const& value); + hstring LogOutputPath(); + void LogOutputPath(hstring const& value); + bool AllowHashMismatch(); + void AllowHashMismatch(bool value); + hstring ReplacementInstallerArguments(); + void ReplacementInstallerArguments(hstring const& value); + hstring CorrelationData(); + void CorrelationData(hstring const& value); + hstring AdditionalPackageCatalogArguments(); + void AdditionalPackageCatalogArguments(hstring const& value); + private: + winrt::Microsoft::Management::Deployment::PackageVersionId m_packageVersionId{ nullptr }; + std::wstring m_preferredInstallLocation = L""; + winrt::Microsoft::Management::Deployment::PackageInstallScope m_packageInstallScope = winrt::Microsoft::Management::Deployment::PackageInstallScope::User; + winrt::Microsoft::Management::Deployment::PackageInstallMode m_packageInstallMode = winrt::Microsoft::Management::Deployment::PackageInstallMode::Default; + std::wstring m_logOutputPath = L""; + bool m_allowHashMismatch = false; + std::wstring m_replacementInstallerArguments = L""; + std::wstring m_correlationData = L""; + std::wstring m_additionalPackageCatalogArguments = L""; + }; +} +namespace winrt::Microsoft::Management::Deployment::factory_implementation +{ + struct InstallOptions : InstallOptionsT + { + }; +} diff --git a/src/Microsoft.Management.Deployment/InstallResult.cpp b/src/Microsoft.Management.Deployment/InstallResult.cpp new file mode 100644 index 0000000000..dbfff06418 --- /dev/null +++ b/src/Microsoft.Management.Deployment/InstallResult.cpp @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "InstallResult.h" +#include "InstallResult.g.cpp" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void InstallResult::Initialize( + winrt::Microsoft::Management::Deployment::InstallResultStatus status, + winrt::hresult extendedErrorCode, + hstring const& correlationData, + bool rebootRequired) + { + m_status = status; + m_extendedErrorCode = extendedErrorCode; + m_correlationData = correlationData; + m_rebootRequired = rebootRequired; + } + hstring InstallResult::CorrelationData() + { + return hstring(m_correlationData); + } + bool InstallResult::RebootRequired() + { + return m_rebootRequired; + } + winrt::Microsoft::Management::Deployment::InstallResultStatus InstallResult::Status() + { + return m_status; + } + winrt::hresult InstallResult::ExtendedErrorCode() + { + return m_extendedErrorCode; + } +} diff --git a/src/Microsoft.Management.Deployment/InstallResult.h b/src/Microsoft.Management.Deployment/InstallResult.h new file mode 100644 index 0000000000..1fe4cc02a6 --- /dev/null +++ b/src/Microsoft.Management.Deployment/InstallResult.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "InstallResult.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct InstallResult : InstallResultT + { + InstallResult() = default; + void Initialize( + winrt::Microsoft::Management::Deployment::InstallResultStatus status, + winrt::hresult extendedErrorCode, + hstring const& correlationData, + bool rebootRequired); + + hstring CorrelationData(); + bool RebootRequired(); + winrt::Microsoft::Management::Deployment::InstallResultStatus Status(); + winrt::hresult ExtendedErrorCode(); + private: + std::wstring m_correlationData = L""; + bool m_rebootRequired = false; + winrt::Microsoft::Management::Deployment::InstallResultStatus m_status = winrt::Microsoft::Management::Deployment::InstallResultStatus::Ok; + winrt::hresult m_extendedErrorCode = S_OK; + }; +} diff --git a/src/Microsoft.Management.Deployment/MatchResult.cpp b/src/Microsoft.Management.Deployment/MatchResult.cpp new file mode 100644 index 0000000000..84c5ff0207 --- /dev/null +++ b/src/Microsoft.Management.Deployment/MatchResult.cpp @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include +#include +#include "MatchResult.h" +#include "MatchResult.g.cpp" +#include "CatalogPackage.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void MatchResult::Initialize(Microsoft::Management::Deployment::CatalogPackage package, Microsoft::Management::Deployment::PackageMatchFilter matchCriteria) + { + m_catalogPackage = package; + m_matchCriteria = matchCriteria; + } + winrt::Microsoft::Management::Deployment::CatalogPackage MatchResult::CatalogPackage() + { + return m_catalogPackage; + } + winrt::Microsoft::Management::Deployment::PackageMatchFilter MatchResult::MatchCriteria() + { + return m_matchCriteria; + } +} diff --git a/src/Microsoft.Management.Deployment/MatchResult.h b/src/Microsoft.Management.Deployment/MatchResult.h new file mode 100644 index 0000000000..03f5c3ae00 --- /dev/null +++ b/src/Microsoft.Management.Deployment/MatchResult.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "MatchResult.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct MatchResult : MatchResultT + { + MatchResult() = default; + void Initialize(Microsoft::Management::Deployment::CatalogPackage package, Microsoft::Management::Deployment::PackageMatchFilter matchCriteria); + + winrt::Microsoft::Management::Deployment::CatalogPackage CatalogPackage(); + winrt::Microsoft::Management::Deployment::PackageMatchFilter MatchCriteria(); + private: + Microsoft::Management::Deployment::CatalogPackage m_catalogPackage{ nullptr }; + Microsoft::Management::Deployment::PackageMatchFilter m_matchCriteria{ nullptr }; + }; +} diff --git a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj new file mode 100644 index 0000000000..33248cbbb1 --- /dev/null +++ b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj @@ -0,0 +1,193 @@ + + + + + true + true + true + true + {1cc41a9a-ae66-459d-9210-1e572dd7be69} + Microsoft.Management.Deployment + Microsoft.Management.Deployment + en-US + 14.0 + 10.0 + 10.0.18362.0 + 10.0.17763.0 + -lib Microsoft_Management_Deployment + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + StaticLibrary + v140 + v141 + v142 + Unicode + false + + + true + true + + + false + true + false + Spectre + + + + + + + + + + + + + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + $(SolutionDir)$(PlatformTarget)\$(Configuration)\$(ProjectName)\ + $(PlatformTarget)\$(Configuration)\ + true + false + ..\CodeAnalysis.ruleset + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + true + true + _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + $(ProjectDir)..\AppInstallerCLICore;$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories) + + + Console + false + Microsoft_Management_Deployment.def + $(OutDir)$(ProjectName).winmd + AppInstallerCLICore.lib;AppInstallerCommonCore.lib;AppInstallerRepositoryCore.lib;JsonCppLib.lib;YamlCppLib.lib;cpprestsdk.lib;wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;%(AdditionalDependencies) + + + + + _DEBUG;%(PreprocessorDefinitions) + $(OutDir)$(TargetName)Debug.pdb + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/Microsoft_Management_Deployment.def b/src/Microsoft.Management.Deployment/Microsoft_Management_Deployment.def new file mode 100644 index 0000000000..8c1a02932d --- /dev/null +++ b/src/Microsoft.Management.Deployment/Microsoft_Management_Deployment.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/Microsoft.Management.Deployment/PackageCatalog.cpp b/src/Microsoft.Management.Deployment/PackageCatalog.cpp new file mode 100644 index 0000000000..58cc8da4af --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageCatalog.cpp @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include +#include "Workflows/WorkflowBase.h" +#include "Converters.h" +#include "PackageCatalog.h" +#include "PackageCatalog.g.cpp" +#include "PackageCatalogInfo.h" +#include "FindPackagesResult.h" +#include "MatchResult.h" +#include "CatalogPackage.h" +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 6388 Allow CreateInstance. +#include +// 4467 Allow use of uuid attribute for com object creation. +#include "PackageMatchFilter.h" +#pragma warning( pop ) +#include "Microsoft/PredefinedInstalledSourceFactory.h" +#include +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageCatalog::Initialize( + winrt::Microsoft::Management::Deployment::PackageCatalogInfo info, + std::shared_ptr source, + bool isComposite) + { + m_info = info; + m_source = std::move(source); + m_isComposite = isComposite; + } + bool PackageCatalog::IsComposite() + { + // Can't use m_source->IsComposite for this because all remote sources are turned into composite sources + // behind the scenes when being opened in PackageCatalogReference.cpp so that CatalogPackage.IsInstalled works. + return m_isComposite; + } + winrt::Microsoft::Management::Deployment::PackageCatalogInfo PackageCatalog::Info() + { + return m_info; + } + winrt::Windows::Foundation::IAsyncOperation PackageCatalog::FindPackagesAsync(winrt::Microsoft::Management::Deployment::FindPackagesOptions options) + { + co_return FindPackages(options); + } + + HRESULT PopulateSearchRequestFromVector( + ::AppInstaller::Repository::SearchRequest* searchRequest, + Windows::Foundation::Collections::IVector vector, + bool isSelector) + { + // Populates either the Filters vector of a searchRequest (if isSelector is false), + // or the Inclusions and Query (if true) + for (uint32_t i = 0; i < vector.Size(); ++i) + { + Microsoft::Management::Deployment::PackageMatchFilter filter = vector.GetAt(i); + + if (filter.Value().size() == 0) + { + // If the caller did not add a value it can't actually be used to filter or include anything so just ignore it. + continue; + } + ::AppInstaller::Repository::MatchType packageFieldMatchOption = GetRepositoryMatchType(filter.Option()); + ::AppInstaller::Repository::PackageMatchField matchField = GetRepositoryMatchField(filter.Field()); + + if (isSelector) + { + if (filter.Field() == Microsoft::Management::Deployment::PackageMatchField::CatalogDefault) + { + if (searchRequest->Query.has_value()) + { + // CatalogDefault match field can't be used twice. + return E_INVALIDARG; + } + searchRequest->Query = ::AppInstaller::Repository::RequestMatch(packageFieldMatchOption, winrt::to_string(filter.Value())); + } + else + { + auto matchFilter = ::AppInstaller::Repository::PackageMatchFilter(matchField, packageFieldMatchOption, winrt::to_string(filter.Value())); + searchRequest->Inclusions.emplace_back(matchFilter); + } + } + else + { + if (filter.Field() == Microsoft::Management::Deployment::PackageMatchField::CatalogDefault) + { + // CatalogDefault match fields can't be used in the Filters. + return E_INVALIDARG; + } + auto matchFilter = ::AppInstaller::Repository::PackageMatchFilter(matchField, packageFieldMatchOption, winrt::to_string(filter.Value())); + searchRequest->Filters.emplace_back(matchFilter); + } + } + return S_OK; + } + + HRESULT PopulateSearchRequest( + ::AppInstaller::Repository::SearchRequest* searchRequest, + winrt::Microsoft::Management::Deployment::FindPackagesOptions const& options) + { + RETURN_IF_FAILED(PopulateSearchRequestFromVector(searchRequest, options.Filters(), false)); + RETURN_IF_FAILED(PopulateSearchRequestFromVector(searchRequest, options.Selectors(), true)); + return S_OK; + } + + winrt::Microsoft::Management::Deployment::FindPackagesResult PackageCatalog::FindPackages(winrt::Microsoft::Management::Deployment::FindPackagesOptions const& options) + { + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; + bool isTruncated = false; + Windows::Foundation::Collections::IVector matches{ winrt::single_threaded_vector() }; + ::AppInstaller::Repository::SearchRequest searchRequest; + + HRESULT hr = PopulateSearchRequest(&searchRequest, options); + if (SUCCEEDED(hr)) + { + searchRequest.MaximumResults = options.ResultLimit(); + try + { + auto searchResult = m_source->Search(searchRequest); + + // Build the result object from the searchResult + for (size_t i = 0; i < searchResult.Matches.size(); ++i) + { + auto match = searchResult.Matches[i]; + auto catalogPackage = winrt::make_self>(); + catalogPackage->Initialize(m_source, match.Package); + + auto packageMatchFilter = winrt::make_self>(); + packageMatchFilter->Initialize(match.MatchCriteria); + + auto matchResult = winrt::make_self>(); + matchResult->Initialize(*catalogPackage, *packageMatchFilter); + + matches.Append(*matchResult); + } + isTruncated = searchResult.Truncated; + } + // Exceptions that may occur in the process of executing an arbitrary command + catch (const wil::ResultException& re) + { + hr = re.GetErrorCode(); + } + catch (const winrt::hresult_error& hre) + { + hr = hre.code(); + } + catch (const ::AppInstaller::Settings::GroupPolicyException&) + { + // Policy could have changed since server started. + hr = APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; + } + catch (const std::exception&) + { + hr = APPINSTALLER_CLI_ERROR_COMMAND_FAILED; + } + catch (...) + { + hr = APPINSTALLER_CLI_ERROR_COMMAND_FAILED; + } + } + auto findPackagesResult = winrt::make_self>(); + // TODO: Add search timeout and error code. + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus status = FindPackagesResultStatus(hr); + findPackagesResult->Initialize(status, isTruncated, matches); + + return *findPackagesResult; + } +} diff --git a/src/Microsoft.Management.Deployment/PackageCatalog.h b/src/Microsoft.Management.Deployment/PackageCatalog.h new file mode 100644 index 0000000000..1225a332e0 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageCatalog.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageCatalog.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageCatalog : PackageCatalogT + { + PackageCatalog() = default; + void Initialize( + winrt::Microsoft::Management::Deployment::PackageCatalogInfo info, + std::shared_ptr source, + bool isComposite); + + bool IsComposite(); + winrt::Microsoft::Management::Deployment::PackageCatalogInfo Info(); + winrt::Windows::Foundation::IAsyncOperation FindPackagesAsync(winrt::Microsoft::Management::Deployment::FindPackagesOptions options); + winrt::Microsoft::Management::Deployment::FindPackagesResult FindPackages(winrt::Microsoft::Management::Deployment::FindPackagesOptions const& options); + private: + winrt::Microsoft::Management::Deployment::PackageCatalogInfo m_info{ nullptr }; + std::shared_ptr m_source; + bool m_isComposite = false; + + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp b/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp new file mode 100644 index 0000000000..10161edc0d --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include "PackageCatalogInfo.h" +#include "PackageCatalogInfo.g.cpp" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageCatalogInfo::Initialize(const ::AppInstaller::Repository::SourceDetails& sourceDetails) + { + m_sourceDetails = sourceDetails; + } + ::AppInstaller::Repository::SourceDetails PackageCatalogInfo::GetSourceDetails() + { + return m_sourceDetails; + } + hstring PackageCatalogInfo::Id() + { + return winrt::to_hstring(m_sourceDetails.Identifier); + } + hstring PackageCatalogInfo::Name() + { + return winrt::to_hstring(m_sourceDetails.Name); + } + hstring PackageCatalogInfo::Type() + { + return winrt::to_hstring(m_sourceDetails.Type); + } + hstring PackageCatalogInfo::Argument() + { + return winrt::to_hstring(m_sourceDetails.Arg); + } + winrt::Windows::Foundation::DateTime PackageCatalogInfo::LastUpdateTime() + { + return winrt::clock::from_time_t(std::chrono::system_clock::to_time_t(m_sourceDetails.LastUpdateTime)); + } + winrt::Microsoft::Management::Deployment::PackageCatalogOrigin PackageCatalogInfo::Origin() + { + switch (m_sourceDetails.Origin) + { + case ::AppInstaller::Repository::SourceOrigin::Default : + case ::AppInstaller::Repository::SourceOrigin::Predefined: + return PackageCatalogOrigin::Predefined; + case ::AppInstaller::Repository::SourceOrigin::User: + case ::AppInstaller::Repository::SourceOrigin::GroupPolicy: + default: + return PackageCatalogOrigin::User; + } + } + winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel PackageCatalogInfo::TrustLevel() + { + switch (m_sourceDetails.TrustLevel) + { + case ::AppInstaller::Repository::SourceTrustLevel::Trusted: + return PackageCatalogTrustLevel::Trusted; + case ::AppInstaller::Repository::SourceTrustLevel::None: + default: + return PackageCatalogTrustLevel::None; + } + } +} diff --git a/src/Microsoft.Management.Deployment/PackageCatalogInfo.h b/src/Microsoft.Management.Deployment/PackageCatalogInfo.h new file mode 100644 index 0000000000..7b2a503934 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageCatalogInfo.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageCatalogInfo.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageCatalogInfo : PackageCatalogInfoT + { + PackageCatalogInfo() = default; + void Initialize(const ::AppInstaller::Repository::SourceDetails& sourceDetails); + ::AppInstaller::Repository::SourceDetails GetSourceDetails(); + + hstring Id(); + hstring Name(); + hstring Type(); + hstring Argument(); + winrt::Windows::Foundation::DateTime LastUpdateTime(); + winrt::Microsoft::Management::Deployment::PackageCatalogOrigin Origin(); + winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel TrustLevel(); + private: + ::AppInstaller::Repository::SourceDetails m_sourceDetails{}; + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp b/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp new file mode 100644 index 0000000000..e524cc9f3a --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include "PackageCatalogReference.h" +#include "PackageCatalogReference.g.cpp" +#include "PackageCatalogInfo.h" +#include "PackageCatalog.h" +#include "ConnectResult.h" +#include "Workflows/WorkflowBase.h" +#include "Converters.h" +#include "Microsoft/PredefinedInstalledSourceFactory.h" +#include +#include +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::PackageCatalogInfo packageCatalogInfo) + { + m_info = packageCatalogInfo; + } + void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions options) + { + m_compositePackageCatalogOptions = options; + m_isComposite = true; + } + bool PackageCatalogReference::IsComposite() + { + return m_isComposite; + } + winrt::Microsoft::Management::Deployment::PackageCatalogInfo PackageCatalogReference::Info() + { + return m_info; + } + winrt::Windows::Foundation::IAsyncOperation PackageCatalogReference::ConnectAsync() + { + co_return Connect(); + } + winrt::Microsoft::Management::Deployment::ConnectResult PackageCatalogReference::Connect() + { + try + { + ::AppInstaller::ProgressCallback progress; + std::shared_ptr<::AppInstaller::Repository::ISource> source; + if (m_compositePackageCatalogOptions) + { + std::vector> remoteSources; + + for (uint32_t i = 0; i < m_compositePackageCatalogOptions.Catalogs().Size(); ++i) + { + auto catalog = m_compositePackageCatalogOptions.Catalogs().GetAt(i); + winrt::Microsoft::Management::Deployment::implementation::PackageCatalogInfo* catalogInfoImpl = get_self(catalog.Info()); + ::AppInstaller::Repository::SourceDetails sourceDetails = catalogInfoImpl->GetSourceDetails(); + std::shared_ptr<::AppInstaller::Repository::ISource> remoteSource = ::AppInstaller::Repository::OpenSourceFromDetails(sourceDetails, progress).Source; + if (!remoteSource) + { + // If source is null, return the error. There's no way to get the hresult that caused the error right now. + auto connectResult = winrt::make_self>(); + connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::CatalogError, nullptr); + return *connectResult; + } + remoteSources.emplace_back(std::move(remoteSource)); + } + ::AppInstaller::Repository::CompositeSearchBehavior searchBehavior = GetRepositoryCompositeSearchBehavior(m_compositePackageCatalogOptions.CompositeSearchBehavior()); + + std::shared_ptr<::AppInstaller::Repository::ISource> installedSource; + // Check if search behavior indicates that the caller does not want to do local correlation. + if (m_compositePackageCatalogOptions.CompositeSearchBehavior() != Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs) + { + installedSource = ::AppInstaller::Repository::OpenPredefinedSource(::AppInstaller::Repository::PredefinedSource::Installed, progress); + } + + // Create the composite source. + source = ::AppInstaller::Repository::CreateCompositeSource(installedSource, remoteSources, searchBehavior); + } + else + { + winrt::Microsoft::Management::Deployment::implementation::PackageCatalogInfo* catalogInfoImpl = get_self(m_info); + ::AppInstaller::Repository::SourceDetails sourceDetails = catalogInfoImpl->GetSourceDetails(); + source = ::AppInstaller::Repository::OpenSourceFromDetails(sourceDetails, progress).Source; + } + + if (!source) + { + // If source is null, return the error. There's no way to get the hresult that caused the error right now. + auto connectResult = winrt::make_self>(); + connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::CatalogError, nullptr); + return *connectResult; + } + + // Have to make another package catalog info because source->GetDetails has more fields than m_info does. + // Specifically, Rest sources do not have the Ids filled in m_info since they only get the id from the rest server after being Opened. + auto packageCatalogInfo = winrt::make_self>(); + packageCatalogInfo->Initialize(source->GetDetails()); + auto connectResult = winrt::make_self>(); + auto packageCatalog = winrt::make_self>(); + packageCatalog->Initialize(*packageCatalogInfo, source, (m_compositePackageCatalogOptions != nullptr)); + connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::Ok, *packageCatalog); + return *connectResult; + } + catch (...) + { + } + auto connectResult = winrt::make_self>(); + connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::CatalogError, nullptr); + return *connectResult; + } +} diff --git a/src/Microsoft.Management.Deployment/PackageCatalogReference.h b/src/Microsoft.Management.Deployment/PackageCatalogReference.h new file mode 100644 index 0000000000..b7384fbd6b --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageCatalogReference.h @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageCatalogReference.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageCatalogReference : PackageCatalogReferenceT + { + PackageCatalogReference() = default; + void Initialize(winrt::Microsoft::Management::Deployment::PackageCatalogInfo packageCatalogInfo); + void Initialize(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions options); + + bool IsComposite(); + winrt::Microsoft::Management::Deployment::PackageCatalogInfo Info(); + winrt::Windows::Foundation::IAsyncOperation ConnectAsync(); + winrt::Microsoft::Management::Deployment::ConnectResult Connect(); + private: + winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions m_compositePackageCatalogOptions{ nullptr }; + winrt::Microsoft::Management::Deployment::PackageCatalogInfo m_info{ nullptr }; + bool m_isComposite = false; + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp new file mode 100644 index 0000000000..07afeea683 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -0,0 +1,354 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/AppInstallerCLICore.h" +#include "Microsoft/PredefinedInstalledSourceFactory.h" +#include "Commands/RootCommand.h" +#include "ComContext.h" +#include "ExecutionContext.h" +#include "Workflows/WorkflowBase.h" +#include +#include "Commands/InstallCommand.h" +#include +#include +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 6388 Allow CreateInstance. +#include +// 4467 Allow use of uuid attribute for com object creation. +#include "PackageManager.h" +#pragma warning( pop ) +#include "PackageManager.g.cpp" +#include "InstallResult.h" +#include "PackageCatalogInfo.h" +#include "PackageCatalogReference.h" +#include "PackageVersionInfo.h" +#include "PackageVersionId.h" +#include "Workflows/WorkflowBase.h" +#include "Converters.h" +#include "Helpers.h" + +using namespace std::literals::chrono_literals; + +const GUID PackageManagerCLSID1 = { 0xC53A4F16, 0x787E, 0x42A4, { 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 } }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 +const GUID PackageManagerCLSID2 = { 0xE65C7D5A, 0x95AF, 0x4A98, { 0xBE, 0x5F, 0xA7, 0x93, 0x02, 0x9C, 0xEB, 0x56 } }; //E65C7D5A-95AF-4A98-BE5F-A793029CEB56 + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + winrt::Windows::Foundation::Collections::IVectorView PackageManager::GetPackageCatalogs() + { + Windows::Foundation::Collections::IVector catalogs{ winrt::single_threaded_vector() }; + std::vector<::AppInstaller::Repository::SourceDetails> sources = ::AppInstaller::Repository::GetSources(); + for (uint32_t i = 0; i < sources.size(); i++) + { + auto packageCatalogInfo = winrt::make_self>(); + packageCatalogInfo->Initialize(sources.at(i)); + auto packageCatalogRef = winrt::make_self>(); + packageCatalogRef->Initialize(*packageCatalogInfo); + catalogs.Append(*packageCatalogRef); + } + return catalogs.GetView(); + } + winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::GetPredefinedPackageCatalog(winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog const& predefinedPackageCatalog) + { + ::AppInstaller::Repository::SourceDetails sourceDetails; + switch (predefinedPackageCatalog) + { + case winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog::OpenWindowsCatalog: + { + sourceDetails = GetWellKnownSourceDetails(::AppInstaller::Repository::WellKnownSource::WinGet); + auto packageCatalogInfo = winrt::make_self>(); + packageCatalogInfo->Initialize(sourceDetails); + auto packageCatalogRef = winrt::make_self>(); + packageCatalogRef->Initialize(*packageCatalogInfo); + return *packageCatalogRef; + } + default: + throw hresult_invalid_argument(); + } + } + winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::GetLocalPackageCatalog(winrt::Microsoft::Management::Deployment::LocalPackageCatalog const& localPackageCatalog) + { + // InstalledPackages is the only one supported right now, so return early if it's not that. + if(localPackageCatalog != Microsoft::Management::Deployment::LocalPackageCatalog::InstalledPackages) + { + throw hresult_invalid_argument(); + } + ::AppInstaller::Repository::SourceDetails sourceDetails = GetPredefinedSourceDetails(::AppInstaller::Repository::PredefinedSource::Installed); + auto packageCatalogInfo = winrt::make_self>(); + packageCatalogInfo->Initialize(sourceDetails); + auto packageCatalogImpl = winrt::make_self>(); + packageCatalogImpl->Initialize(*packageCatalogInfo); + return *packageCatalogImpl; + } + winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::GetPackageCatalogByName(hstring const& catalogName) + { + std::optional<::AppInstaller::Repository::SourceDetails> source = ::AppInstaller::Repository::GetSource(winrt::to_string(catalogName)); + // Create the catalog object if the source is found, otherwise return null. Don't throw. + if (source.has_value()) + { + auto packageCatalogInfo = winrt::make_self>(); + packageCatalogInfo->Initialize(source.value()); + auto packageCatalogRef = winrt::make_self>(); + packageCatalogRef->Initialize(*packageCatalogInfo); + return *packageCatalogRef; + } + else + { + return nullptr; + } + } + winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::CreateCompositePackageCatalog(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions const& options) + { + for (uint32_t i = 0; i < options.Catalogs().Size(); ++i) + { + auto catalog = options.Catalogs().GetAt(i); + if (catalog.IsComposite()) + { + // Can't make a composite source out of a source that's already a composite. + throw hresult_invalid_argument(); + } + } + auto packageCatalogImpl = winrt::make_self>(); + packageCatalogImpl->Initialize(options); + return *packageCatalogImpl; + } + + Windows::Foundation::IAsyncOperation ExecuteInstallAsync(::AppInstaller::CLI::Execution::Context& context, std::unique_ptr<::AppInstaller::CLI::Command>& command) + { + co_await winrt::resume_background(); + winrt::hresult result = ::AppInstaller::CLI::Execute(context, command); + return result; + } + winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::InstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options) + { + auto report_progress{ co_await winrt::get_progress_token() }; + auto cancellationToken{ co_await winrt::get_cancellation_token() }; + + InstallProgress queuedProgress{ PackageInstallProgressState::Queued, 0, 0, 0 }; + report_progress(queuedProgress); + + winrt::hresult terminationHR = S_OK; + ::AppInstaller::CLI::Workflow::ExecutionStage executionStage = ::AppInstaller::CLI::Workflow::ExecutionStage::Initial; + + try + { + Microsoft::Management::Deployment::PackageVersionId versionId{ nullptr }; + if (options) + { + versionId = options.PackageVersionId(); + } + + // If the version of the package is specified use that, otherwise use the default. + Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo{ nullptr }; + if (versionId) + { + packageVersionInfo = package.GetPackageVersionInfo(versionId); + } + else + { + packageVersionInfo = package.DefaultInstallVersion(); + } + + if (!packageVersionInfo) + { + // If no package version was found on the catalog then return a failure. This is unexpected, a catalog with no latest version should not be in the catalog. + terminationHR = APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER; + winrt::Microsoft::Management::Deployment::InstallResultStatus installResultStatus = GetInstallResultStatus(executionStage, terminationHR); + auto installResult = winrt::make_self>(); + installResult->Initialize(installResultStatus, terminationHR, options.CorrelationData(), false); + co_return *installResult; + } + + // Handle the progress from the installer + ::AppInstaller::COMContext context; + + // TODO: Exact ComCaller's process name needs to be retrieved from COM Client side in the future + context.SetLoggerContext(options.CorrelationData(), "COMCaller"); + + // Convert the options to arguments for the installer. + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Id, ::AppInstaller::Utility::ConvertToUTF8(package.Id())); + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Version, ::AppInstaller::Utility::ConvertToUTF8(packageVersionInfo.Version())); + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Channel, ::AppInstaller::Utility::ConvertToUTF8(packageVersionInfo.Channel())); + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Source, ::AppInstaller::Utility::ConvertToUTF8(packageVersionInfo.PackageCatalog().Info().Name())); + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Exact); + if (options) + { + if (!options.LogOutputPath().empty()) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Log, ::AppInstaller::Utility::ConvertToUTF8(options.LogOutputPath())); + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::VerboseLogs); + } + if (options.AllowHashMismatch()) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::HashOverride); + } + + // If the PackageInstallScope is anything other than ::Any then set it as a requirement. + if (options.PackageInstallScope() == PackageInstallScope::System) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::InstallScope, ScopeToString(::AppInstaller::Manifest::ScopeEnum::Machine)); + } + else if (options.PackageInstallScope() == PackageInstallScope::User) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::InstallScope, ScopeToString(::AppInstaller::Manifest::ScopeEnum::User)); + } + + if (options.PackageInstallMode() == PackageInstallMode::Interactive) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Interactive); + } + else if (options.PackageInstallMode() == PackageInstallMode::Silent) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Silent); + } + + if (!options.PreferredInstallLocation().empty()) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::InstallLocation, ::AppInstaller::Utility::ConvertToUTF8(options.PreferredInstallLocation())); + } + + if (!options.ReplacementInstallerArguments().empty()) + { + context.Args.AddArg(::AppInstaller::CLI::Execution::Args::Type::Override, ::AppInstaller::Utility::ConvertToUTF8(options.ReplacementInstallerArguments())); + } + } + + // TODO: AdditionalPackageCatalogArguments is not currently supported by the underlying implementation. + ::AppInstaller::CLI::RootCommand rootCommand; + std::unique_ptr<::AppInstaller::CLI::Command> command = std::make_unique<::AppInstaller::CLI::InstallCommand>(rootCommand.Name()); + rootCommand.ValidateArguments(context.Args); + + context.SetProgressCallbackFunction([=]( + ::AppInstaller::ReportType reportType, + uint64_t current, + uint64_t maximum, + ::AppInstaller::ProgressType progressType, + ::AppInstaller::CLI::Workflow::ExecutionStage executionPhase) + { + bool reportProgress = false; + PackageInstallProgressState progressState = PackageInstallProgressState::Queued; + double downloadProgress = 0; + double installProgress = 0; + uint64_t downloadBytesDownloaded = 0; + uint64_t downloadBytesRequired = 0; + switch (executionPhase) + { + case ::AppInstaller::CLI::Workflow::ExecutionStage::Initial: + case ::AppInstaller::CLI::Workflow::ExecutionStage::ParseArgs: + case ::AppInstaller::CLI::Workflow::ExecutionStage::Discovery: + // We already reported queued progress up front. + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Download: + progressState = PackageInstallProgressState::Downloading; + if (reportType == ::AppInstaller::ReportType::BeginProgress) + { + reportProgress = true; + } + else if (progressType == ::AppInstaller::ProgressType::Bytes) + { + downloadBytesDownloaded = current; + downloadBytesRequired = maximum; + if (maximum > 0 && maximum >= current) + { + reportProgress = true; + downloadProgress = static_cast(current) / static_cast(maximum); + } + } + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::PreExecution: + // Wait until installer starts to report Installing. + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::Execution: + progressState = PackageInstallProgressState::Installing; + downloadProgress = 1; + if (reportType == ::AppInstaller::ReportType::ExecutionPhaseUpdate) + { + // Install is starting. Send progress so callers know the AsyncOperation can't be cancelled. + reportProgress = true; + } + else if (reportType == ::AppInstaller::ReportType::EndProgress) + { + // Install is "finished". May not have succeeded. + reportProgress = true; + installProgress = 1; + } + else if (progressType == ::AppInstaller::ProgressType::Percent) + { + if (maximum > 0 && maximum >= current) + { + // Install is progressing + reportProgress = true; + installProgress = static_cast(current) / static_cast(maximum); + } + } + break; + case ::AppInstaller::CLI::Workflow::ExecutionStage::PostExecution: + if (reportType == ::AppInstaller::ReportType::ExecutionPhaseUpdate) + { + // Send PostInstall progress when it switches to PostExecution phase. + reportProgress = true; + progressState = PackageInstallProgressState::PostInstall; + downloadProgress = 1; + installProgress = 1; + } + break; + } + if (reportProgress) + { + winrt::Microsoft::Management::Deployment::InstallProgress contextProgress{ progressState, downloadBytesDownloaded, downloadBytesRequired, downloadProgress, installProgress }; + report_progress(contextProgress); + } + return; + } + ); + context.EnableCtrlHandler(); + + Windows::Foundation::IAsyncOperation executeOperation = ExecuteInstallAsync(context, command); + + cancellationToken.callback([&context] + { + context.Cancel(false, true); + }); + // Wait for the execute operation to finish. + // The cancellation of the AsyncOperation triggers Cancel which causes the executeOperation to end. + terminationHR = co_await executeOperation; + executionStage = context.GetExecutionStage(); + + } + // Exceptions that may occur in the process of executing an arbitrary command + catch (const wil::ResultException& re) + { + terminationHR = re.GetErrorCode(); + } + catch (const winrt::hresult_error& hre) + { + terminationHR = hre.code(); + } + catch (const ::AppInstaller::CLI::CommandException&) + { + terminationHR = APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS; + } + catch (const ::AppInstaller::Settings::GroupPolicyException&) + { + // Policy could have changed since server started + // or catalog could have been disabled since being returned. + terminationHR = APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; + } + catch (const std::exception&) + { + terminationHR = APPINSTALLER_CLI_ERROR_COMMAND_FAILED; + } + catch (...) + { + terminationHR = APPINSTALLER_CLI_ERROR_COMMAND_FAILED; + } + // TODO - RebootRequired not yet populated, msi arguments not returned from Execute. + winrt::Microsoft::Management::Deployment::InstallResultStatus installResultStatus = GetInstallResultStatus(executionStage, terminationHR); + auto installResult = winrt::make_self>(); + installResult->Initialize(installResultStatus, terminationHR, options.CorrelationData(), false); + co_return *installResult; + } + CoCreatableCppWinRtClassWithCLSID(PackageManager, 1, &PackageManagerCLSID1); + CoCreatableCppWinRtClassWithCLSID(PackageManager, 2, &PackageManagerCLSID2); +} diff --git a/src/Microsoft.Management.Deployment/PackageManager.h b/src/Microsoft.Management.Deployment/PackageManager.h new file mode 100644 index 0000000000..d077353b7a --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageManager.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageManager.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + [uuid("C53A4F16-787E-42A4-B304-29EFFB4BF597")] + struct PackageManager : PackageManagerT + { + PackageManager() = default; + + winrt::Windows::Foundation::Collections::IVectorView GetPackageCatalogs(); + winrt::Microsoft::Management::Deployment::PackageCatalogReference GetPredefinedPackageCatalog(winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog const& predefinedPackageCatalog); + winrt::Microsoft::Management::Deployment::PackageCatalogReference GetLocalPackageCatalog(winrt::Microsoft::Management::Deployment::LocalPackageCatalog const& localPackageCatalog); + winrt::Microsoft::Management::Deployment::PackageCatalogReference GetPackageCatalogByName(hstring const& catalogName); + winrt::Microsoft::Management::Deployment::PackageCatalogReference CreateCompositePackageCatalog(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions const& options); + winrt::Windows::Foundation::IAsyncOperationWithProgress + InstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options); + winrt::Windows::Foundation::IAsyncOperationWithProgress + GetInstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package); + }; +} +namespace winrt::Microsoft::Management::Deployment::factory_implementation +{ + struct PackageManager : PackageManagerT + { + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl new file mode 100644 index 0000000000..4e2d01b169 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -0,0 +1,536 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +namespace Microsoft.Management.Deployment +{ + [contractversion(1)] + apicontract WindowsPackageManagerContract{}; + + /// State of the install. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageInstallProgressState + { + /// The install is queued but not yet active. Cancellation of the IAsyncOperationWithProgress in this + /// state will prevent the package from downloading or installing. + Queued, + /// The installer is downloading. Cancellation of the IAsyncOperationWithProgress in this state will + /// end the download and prevent the package from installing. + Downloading, + /// The install is in progress. Cancellation of the IAsyncOperationWithProgress in this state will not + /// stop the installation or the post install cleanup. + Installing, + /// The installer has completed and cleanup actions are in progress. Cancellation of the + /// IAsyncOperationWithProgress in this state will not stop cleanup or roll back the install. + PostInstall, + /// The operation has completed. + Finished, + }; + + /// Progress object for the install + /// DESIGN NOTE: percentage for the install as a whole is purposefully not included as there is no way to + /// estimate progress when the installer is running. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + struct InstallProgress + { + /// State of the install + PackageInstallProgressState State; + /// DESIGN NOTE: BytesDownloaded may only be available for downloads done by Windows Package Manager itself. + /// Number of bytes downloaded if known + UInt64 BytesDownloaded; + /// DESIGN NOTE: BytesRequired may only be available for downloads done by Windows Package Manager itself. + /// Number of bytes required if known + UInt64 BytesRequired; + /// Download percentage completed + Double DownloadProgress; + /// Install percentage if known. + Double InstallationProgress; + }; + + /// Status of the Install call + /// Implementation Note: Errors mapped from AppInstallerErrors.h + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum InstallResultStatus + { + Ok, + BlockedByPolicy, + CatalogError, + InternalError, + InvalidOptions, + DownloadError, + InstallError, + ManifestError, + NoApplicableInstallers, + }; + + /// Result of the install + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass InstallResult + { + /// Used by a caller to correlate the install with a caller's data. + String CorrelationData{ get; }; + /// Whether a restart is required to complete the install. + Boolean RebootRequired{ get; }; + + /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED + InstallResultStatus Status{ get; }; + /// Specific error if known, from downloader or installer itself, example ERROR_INSTALL_PACKAGE_REJECTED + HRESULT ExtendedErrorCode{ get; }; + } + + /// IMPLEMENTATION NOTE: SourceOrigin from AppInstallerRepositorySource.h + /// Defines the origin of the package catalog details. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageCatalogOrigin + { + /// Predefined means it came as part of the Windows Package Manager package and cannot be removed. + Predefined, + /// User means it was added by the user and could be removed. + User, + }; + + /// IMPLEMENTATION NOTE: SourceTrustLevel from AppInstallerRepositorySource.h + /// Defines the trust level of the package catalog. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageCatalogTrustLevel + { + None, + Trusted, + }; + + /// IMPLEMENTATION NOTE: SourceDetails from AppInstallerRepositorySource.h + /// Interface for retrieving information about an package catalog without acting on it. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass PackageCatalogInfo + { + /// The package catalog's unique identifier. + /// SAMPLE VALUES: For OpenWindowsCatalog "Microsoft.Winget.Source_8wekyb3d8bbwe" + /// For contoso sample on msdn "contoso" + String Id { get; }; + /// The name of the package catalog. + /// SAMPLE VALUES: For OpenWindowsCatalog "winget". + /// For contoso sample on msdn "contoso" + String Name { get; }; + /// The type of the package catalog. + /// ALLOWED VALUES: "Microsoft.Rest", "Microsoft.PreIndexed.Package" + /// SAMPLE VALUES: For OpenWindowsCatalog "Microsoft.PreIndexed.Package". + /// For contoso sample on msdn "Microsoft.PreIndexed.Package" + String Type { get; }; + /// The argument used when adding the package catalog. + /// SAMPLE VALUES: For OpenWindowsCatalog "https://winget.azureedge.net/cache" + /// For contoso sample on msdn "https://pkgmgr-int.azureedge.net/cache" + String Argument { get; }; + /// The last time that this package catalog was updated. + Windows.Foundation.DateTime LastUpdateTime { get; }; + /// The origin of the package catalog. + PackageCatalogOrigin Origin { get; }; + /// The trust level of the package catalog + PackageCatalogTrustLevel TrustLevel { get; }; + } + + /// A metadata item of a package version. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageVersionMetadataField + { + /// The InstallerType of an installed package + InstallerType, + /// The Scope of an installed package + InstalledScope, + /// The system path where the package is installed + InstalledLocation, + /// The standard uninstall command; which may be interactive + StandardUninstallCommand, + /// An uninstall command that should be non-interactive + SilentUninstallCommand, + /// The publisher of the package + PublisherDisplayName, + }; + + /// IMPLEMENTATION NOTE: IPackageVersion from AppInstallerRepositorySearch.h + /// A single package version. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass PackageVersionInfo + { + /// IMPLEMENTATION NOTE: PackageVersionMetadata fields from AppInstallerRepositorySearch.h + /// Gets any metadata associated with this package version. + /// Primarily stores data on installed packages. + /// Metadata fields may have no value (e.g. packages that aren't installed will not have an InstalledLocation). + String GetMetadata(PackageVersionMetadataField metadataField); + /// IMPLEMENTATION NOTE: PackageVersionProperty fields from AppInstallerRepositorySearch.h + String Id { get; }; + String DisplayName { get; }; + String Version { get; }; + String Channel { get; }; + /// DESIGN NOTE: RelativePath from AppInstallerRepositorySearch.h is excluded as not needed. + /// String RelativePath; + + /// IMPLEMENTATION NOTE: PackageVersionMultiProperty fields from AppInstallerRepositorySearch.h + /// PackageFamilyName and ProductCode can have multiple values. + Windows.Foundation.Collections.IVectorView PackageFamilyNames { get; }; + Windows.Foundation.Collections.IVectorView ProductCodes { get; }; + + /// Gets the package catalog where this package version is from. + PackageCatalog PackageCatalog { get; }; + + /// DESIGN NOTE: + /// GetManifest from IPackageVersion in AppInstallerRepositorySearch is not implemented in V1. That class has + /// a lot of fields and no one requesting it. + /// Gets the manifest of this package version. + /// virtual Manifest::Manifest GetManifest() = 0; + } + + /// IMPLEMENTATION NOTE: PackageVersionKey from AppInstallerRepositorySearch.h + /// A key to identify a package version within a package. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass PackageVersionId + { + /// The package catalog id that this version came from. + String PackageCatalogId { get; }; + /// The version. + String Version { get; }; + /// The channel. + String Channel { get; }; + }; + + /// IMPLEMENTATION NOTE: IPackage from AppInstallerRepositorySearch.h + /// A package, potentially containing information about it's local state and the available versions. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass CatalogPackage + { + /// IMPLEMENTATION NOTE: PackageProperty fields from AppInstallerRepositorySearch.h + /// Gets a property of this package. + String Id { get; }; + String Name { get; }; + + /// Gets the installed package information if the package is installed. + PackageVersionInfo InstalledVersion{ get; }; + + /// Gets all available versions of this package. Ordering is not guaranteed. + Windows.Foundation.Collections.IVectorView AvailableVersions { get; }; + + /// Gets the version of this package that will be installed if version is not set in InstallOptions. + PackageVersionInfo DefaultInstallVersion { get; }; + + /// Gets a specific version of this package. + PackageVersionInfo GetPackageVersionInfo(PackageVersionId versionKey); + + /// Gets a value indicating whether an available version is newer than the installed version. + Boolean IsUpdateAvailable { get; }; + + /// DESIGN NOTE: + /// IsSame from IPackage in AppInstallerRepositorySearch is not implemented in V1. + /// Determines if the given IPackage refers to the same package as this one. + /// virtual bool IsSame(const IPackage*) const = 0; + } + + /// IMPLEMENTATION NOTE: CompositeSearchBehavior from AppInstallerRepositorySource.h + /// Search behavior for composite catalogs. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum CompositeSearchBehavior + { + /// Search local catalogs only + LocalCatalogs, + /// Search remote catalogs only, don't check local catalogs for InstalledVersion + RemotePackagesFromRemoteCatalogs, + /// Search remote catalogs, and check local catalogs for InstalledVersion + RemotePackagesFromAllCatalogs, + /// Search both local and remote catalogs. + AllCatalogs, + }; + + /// IMPLEMENTATION NOTE: PackageFieldMatchOption from AppInstallerRepositorySearch.h + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageFieldMatchOption + { + Equals, + EqualsCaseInsensitive, + StartsWithCaseInsensitive, + ContainsCaseInsensitive, + }; + + /// IMPLEMENTATION NOTE: PackageFieldMatchOption from AppInstallerRepositorySearch.h + /// The field to match on. + /// The values must be declared in order of preference in search results. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageMatchField + { + CatalogDefault, + Id, + Name, + Moniker, + Command, + Tag, + /// DESIGN NOTE: The following PackageFieldMatchOption from AppInstallerRepositorySearch.h are not implemented in V1. + /// PackageFamilyName, + /// ProductCode, + /// NormalizedNameAndPublisher, + }; + + /// IMPLEMENTATION NOTE: PackageMatchFilter from AppInstallerRepositorySearch.h + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass PackageMatchFilter + { + PackageMatchFilter(); + /// The type of string comparison for matching + PackageFieldMatchOption Option; + /// The field to search + PackageMatchField Field; + /// The value to match + String Value; + /// DESIGN NOTE: "Additional" from RequestMatch AppInstallerRepositorySearch.h is not implemented here. + } + + /// IMPLEMENTATION NOTE: MatchResult from AppInstallerRepositorySearch.h + /// A single result from the search. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass MatchResult + { + /// The package found by the search request. + CatalogPackage CatalogPackage { get; }; + + /// The highest order field on which the package matched the search. + PackageMatchFilter MatchCriteria { get; }; + } + + /// Status of the FindPackages call + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum FindPackagesResultStatus + { + Ok, + BlockedByPolicy, + CatalogError, + InternalError, + InvalidOptions + }; + + /// IMPLEMENTATION NOTE: SearchResult from AppInstallerRepositorySearch.h + /// Search result data returned from FindPackages + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass FindPackagesResult + { + /// Error codes + FindPackagesResultStatus Status{ get; }; + + /// The full set of results from the search. + Windows.Foundation.Collections.IVectorView Matches { get; }; + + /// If true, the results were truncated by the given ResultLimit + /// USAGE NOTE: Windows Package Manager does not support result pagination, there is no way to continue + /// getting more results. + Boolean WasLimitExceeded{ get; }; + } + + /// Options for FindPackages + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass FindPackagesOptions + { + FindPackagesOptions(); + + /// DESIGN NOTE: + /// This class maps to SearchRequest from AppInstallerRepositorySearch.h + /// That class is a container for data used to filter the available manifests in an package catalog. + /// Its properties can be thought of as: + /// (Query || Inclusions...) && Filters... + /// If Query and Inclusions are both empty, the starting data set will be the entire database. + /// Everything && Filters... + /// That has been translated in this api so that + /// Inclusions are Selectors below + /// Filters are Filters below + /// Query is PackageFieldMatchOption::PackageCatalogDefined and in the Selector list. + /// USAGE NOTE: Only one selector with PackageFieldMatchOption::PackageCatalogDefined is allowed. + + /// Selectors = you have to match at least one selector (if there are no selectors, then nothing is selected) + Windows.Foundation.Collections.IVector Selectors { get; }; + /// Filters = you have to match all filters(if there are no filters, then there is no filtering of selected items) + Windows.Foundation.Collections.IVector Filters{ get; }; + + /// Restricts the length of the returned results to the specified count. + UInt32 ResultLimit; + } + + /// IMPLEMENTATION NOTE: ISource from AppInstallerRepositorySource.h + /// A catalog for searching for packages + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass PackageCatalog + { + /// Gets a value indicating whether this package catalog is a composite of other package catalogs, + /// and thus the packages may come from disparate package catalogs as well. + Boolean IsComposite { get; }; + /// The details of the package catalog if it is not a composite. + PackageCatalogInfo Info { get; }; + + /// Searches for Packages in the catalog. + Windows.Foundation.IAsyncOperation FindPackagesAsync(FindPackagesOptions options); + FindPackagesResult FindPackages(FindPackagesOptions options); + } + + /// Status of the Connect call + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum ConnectResultStatus + { + Ok, + CatalogError, + }; + + /// Result of the Connect call + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass ConnectResult + { + /// Error codes + ConnectResultStatus Status{ get; }; + + PackageCatalog PackageCatalog { get; }; + } + + /// A reference to a catalog that callers can try to Connect. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass PackageCatalogReference + { + /// Gets a value indicating whether this package catalog is a composite of other package catalogs, + /// and thus the packages may come from disparate package catalogs as well. + Boolean IsComposite { get; }; + /// The details of the package catalog if it is not a composite. + PackageCatalogInfo Info { get; }; + + /// Opens a catalog. Required before searching. For remote catalogs (i.e. not Installed and Installing) this + /// may require downloading information from a server. + Windows.Foundation.IAsyncOperation ConnectAsync(); + ConnectResult Connect(); + } + + /// Catalogs with PackageCatalogOrigin Predefined + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PredefinedPackageCatalog + { + OpenWindowsCatalog, + }; + + /// Local Catalogs with PackageCatalogOrigin Predefined + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum LocalPackageCatalog + { + InstalledPackages, + }; + + /// Options for creating a composite catalog. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass CreateCompositePackageCatalogOptions + { + CreateCompositePackageCatalogOptions(); + + /// Create a composite catalog to allow searching a user defined or pre defined source + /// and a local source (Installed packages) together + IVector Catalogs { get; }; + /// Sets the default search behavior if the catalog is a composite catalog. + CompositeSearchBehavior CompositeSearchBehavior; + } + + /// Required install scope for the package. If the package does not have an installer that + /// supports the specified scope the Install call will fail with InstallResultStatus.NoApplicableInstallers + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageInstallScope + { + /// An installer with any install scope is valid. + Any, + /// Only User install scope installers are valid + User, + /// Only System installers will be valid + System, + }; + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + enum PackageInstallMode + { + /// The default experience for the installer. Installer may show some UI. + Default, + /// Runs the installer in silent mode. This suppresses the installer's UI to the extent + /// possible (installer may still show some required UI). + Silent, + /// Runs the installer in interactive mode. + Interactive, + }; + + /// Options when installing a package. + /// Intended to allow full compatibility with the "winget install" command line interface. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass InstallOptions + { + InstallOptions(); + + /// Optionally specifies the version from the package to install. If unspecified the version matching + /// CatalogPackage.GetLatestVersion() is used. + PackageVersionId PackageVersionId; + + /// Specifies alternate location to install package (if supported). + String PreferredInstallLocation; + /// User or Machine. + PackageInstallScope PackageInstallScope; + /// Silent, Interactive, or Default + PackageInstallMode PackageInstallMode; + /// Directs the logging to a log file. If provided, the installer must have have write access to the file + String LogOutputPath; + /// Continues the install even if the hash in the catalog does not match the linked installer. + Boolean AllowHashMismatch; + /// A string that will be passed to the installer. + /// IMPLEMENTATION NOTE: maps to "--override" in the winget cmd line + String ReplacementInstallerArguments; + + /// Used by a caller to correlate the install with a caller's data. + /// The string must be JSON encoded. + String CorrelationData; + /// A string that will be passed to the source server if using a REST source + String AdditionalPackageCatalogArguments; + } + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] + runtimeclass PackageManager + { + PackageManager(); + + /// Get the available catalogs. Each source will have a separate catalog. + /// This does not open the catalog. These catalogs can be used individually or merged with CreateCompositePackageCatalogAsync. + /// IMPLEMENTATION NOTE: This is a list of sources returned by Windows Package Manager source list + Windows.Foundation.Collections.IVectorView GetPackageCatalogs(); + /// Get a built in catalog + PackageCatalogReference GetPredefinedPackageCatalog(PredefinedPackageCatalog predefinedPackageCatalog); + /// Get a built in catalog + PackageCatalogReference GetLocalPackageCatalog(LocalPackageCatalog localPackageCatalog); + /// Get a catalog by a known name + PackageCatalogReference GetPackageCatalogByName(String catalogName); + /// Get a composite catalog to allow searching a user defined or pre defined source and a local source + /// (Installing, Installed) together at the same time. + PackageCatalogReference CreateCompositePackageCatalog(CreateCompositePackageCatalogOptions options); + + /// Install the specified package + Windows.Foundation.IAsyncOperationWithProgress InstallPackageAsync(CatalogPackage package, InstallOptions options); + } + + /// Force midl3 to generate vector marshalling info. + declare + { + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + } +} diff --git a/src/Microsoft.Management.Deployment/PackageMatchFilter.cpp b/src/Microsoft.Management.Deployment/PackageMatchFilter.cpp new file mode 100644 index 0000000000..96752a5d65 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageMatchFilter.cpp @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include +#include "Workflows/WorkflowBase.h" +#include "Converters.h" +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 6388 Allow CreateInstance. +#include +// 4467 Allow use of uuid attribute for com object creation. +#include "PackageMatchFilter.h" +#pragma warning( pop ) +#include "PackageMatchFilter.g.cpp" +#include "Helpers.h" + +const GUID PackageMatchFilterCLSID1 = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 +const GUID PackageMatchFilterCLSID2 = { 0xADBF3B4A, 0xDB8A, 0x496C, { 0xA5, 0x79, 0x62, 0xB5, 0x8F, 0x5F, 0xB1, 0x3F } }; //ADBF3B4A-DB8A-496C-A579-62B58F5FB13F + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageMatchFilter::Initialize(::AppInstaller::Repository::PackageMatchFilter matchFilter) + { + m_value = winrt::to_hstring(matchFilter.Value); + m_matchField = GetDeploymentMatchField(matchFilter.Field); + m_packageFieldMatchOption = GetDeploymentMatchOption(matchFilter.Type); + } + winrt::Microsoft::Management::Deployment::PackageFieldMatchOption PackageMatchFilter::Option() + { + return m_packageFieldMatchOption; + } + void PackageMatchFilter::Option(winrt::Microsoft::Management::Deployment::PackageFieldMatchOption const& value) + { + m_packageFieldMatchOption = value; + } + winrt::Microsoft::Management::Deployment::PackageMatchField PackageMatchFilter::Field() + { + return m_matchField; + } + void PackageMatchFilter::Field(winrt::Microsoft::Management::Deployment::PackageMatchField const& value) + { + m_matchField = value; + } + hstring PackageMatchFilter::Value() + { + return hstring(m_value); + } + void PackageMatchFilter::Value(hstring const& value) + { + m_value = value; + } + CoCreatableCppWinRtClassWithCLSID(PackageMatchFilter, 1, &PackageMatchFilterCLSID1); + CoCreatableCppWinRtClassWithCLSID(PackageMatchFilter, 2, &PackageMatchFilterCLSID2); +} diff --git a/src/Microsoft.Management.Deployment/PackageMatchFilter.h b/src/Microsoft.Management.Deployment/PackageMatchFilter.h new file mode 100644 index 0000000000..d250190afd --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageMatchFilter.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageMatchFilter.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + [uuid("D02C9DAF-99DC-429C-B503-4E504E4AB000")] + struct PackageMatchFilter : PackageMatchFilterT + { + PackageMatchFilter() = default; + void Initialize(::AppInstaller::Repository::PackageMatchFilter matchFilter); + + winrt::Microsoft::Management::Deployment::PackageFieldMatchOption Option(); + void Option(winrt::Microsoft::Management::Deployment::PackageFieldMatchOption const& value); + winrt::Microsoft::Management::Deployment::PackageMatchField Field(); + void Field(winrt::Microsoft::Management::Deployment::PackageMatchField const& value); + hstring Value(); + void Value(hstring const& value); + private: + hstring m_value; + winrt::Microsoft::Management::Deployment::PackageMatchField m_matchField = winrt::Microsoft::Management::Deployment::PackageMatchField::CatalogDefault; + winrt::Microsoft::Management::Deployment::PackageFieldMatchOption m_packageFieldMatchOption = winrt::Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; + }; +} +namespace winrt::Microsoft::Management::Deployment::factory_implementation +{ + struct PackageMatchFilter : PackageMatchFilterT + { + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageVersionId.cpp b/src/Microsoft.Management.Deployment/PackageVersionId.cpp new file mode 100644 index 0000000000..b9a6b7fa8b --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageVersionId.cpp @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include "PackageVersionId.h" +#include "PackageVersionId.g.cpp" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageVersionId::Initialize(::AppInstaller::Repository::PackageVersionKey packageVersionKey) + { + m_packageVersionKey = packageVersionKey; + } + hstring PackageVersionId::PackageCatalogId() + { + return winrt::to_hstring(m_packageVersionKey.SourceId); + } + hstring PackageVersionId::Version() + { + return winrt::to_hstring(m_packageVersionKey.Version); + } + hstring PackageVersionId::Channel() + { + return winrt::to_hstring(m_packageVersionKey.Channel); + } +} diff --git a/src/Microsoft.Management.Deployment/PackageVersionId.h b/src/Microsoft.Management.Deployment/PackageVersionId.h new file mode 100644 index 0000000000..2645d2af92 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageVersionId.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageVersionId.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageVersionId : PackageVersionIdT + { + PackageVersionId() = default; + void Initialize(::AppInstaller::Repository::PackageVersionKey packageVersionKey); + + hstring PackageCatalogId(); + hstring Version(); + hstring Channel(); + private: + ::AppInstaller::Repository::PackageVersionKey m_packageVersionKey{}; + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp b/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp new file mode 100644 index 0000000000..1742cd61dc --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include +#include "PackageVersionInfo.h" +#include "PackageVersionInfo.g.cpp" +#include "PackageCatalogInfo.h" +#include "PackageCatalog.h" +#include "CatalogPackage.h" +#include "Workflows/WorkflowBase.h" +#include "Converters.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageVersionInfo::Initialize(std::shared_ptr<::AppInstaller::Repository::IPackageVersion> packageVersion) + { + m_packageVersion = std::move(packageVersion); + } + hstring PackageVersionInfo::GetMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField const& metadataField) + { + ::AppInstaller::Repository::PackageVersionMetadata metadataKey = GetRepositoryPackageVersionMetadata(metadataField); + ::AppInstaller::Repository::IPackageVersion::Metadata metadata = m_packageVersion->GetMetadata(); + auto result = metadata.find(metadataKey); + hstring resultString = winrt::to_hstring(result->second); + // The api uses "System" rather than "Machine" for install scope. + if (metadataField == PackageVersionMetadataField::InstalledScope && resultString == L"Machine") + { + return winrt::to_hstring(L"System"); + } + return resultString; + } + hstring PackageVersionInfo::Id() + { + return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Id).get()); + } + hstring PackageVersionInfo::DisplayName() + { + return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Name).get()); + } + hstring PackageVersionInfo::Version() + { + return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Version).get()); + } + hstring PackageVersionInfo::Channel() + { + return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Channel).get()); + } + winrt::Windows::Foundation::Collections::IVectorView PackageVersionInfo::PackageFamilyNames() + { + if (!m_packageFamilyNames) + { + // Vector hasn't been created yet, create and populate it. + auto packageFamilyNames = winrt::single_threaded_vector(); + for (auto&& string : m_packageVersion->GetMultiProperty(::AppInstaller::Repository::PackageVersionMultiProperty::PackageFamilyName)) + { + packageFamilyNames.Append(winrt::to_hstring(string)); + } + m_packageFamilyNames = packageFamilyNames; + } + return m_packageFamilyNames.GetView(); + } + winrt::Windows::Foundation::Collections::IVectorView PackageVersionInfo::ProductCodes() + { + if (!m_productCodes) + { + // Vector hasn't been created yet, create and populate it. + auto productCodes = winrt::single_threaded_vector(); + for (auto&& string : m_packageVersion->GetMultiProperty(::AppInstaller::Repository::PackageVersionMultiProperty::ProductCode)) + { + productCodes.Append(winrt::to_hstring(string)); + } + m_productCodes = productCodes; + } + return m_productCodes.GetView(); + } + winrt::Microsoft::Management::Deployment::PackageCatalog PackageVersionInfo::PackageCatalog() + { + if (!m_packageCatalog) + { + auto packageCatalogInfo = winrt::make_self>(); + packageCatalogInfo->Initialize(m_packageVersion->GetSource()->GetDetails()); + auto packageCatalog = winrt::make_self>(); + packageCatalog->Initialize(*packageCatalogInfo, m_packageVersion->GetSource(), false); + m_packageCatalog = *packageCatalog; + } + return m_packageCatalog; + } +} diff --git a/src/Microsoft.Management.Deployment/PackageVersionInfo.h b/src/Microsoft.Management.Deployment/PackageVersionInfo.h new file mode 100644 index 0000000000..ff033e1ca5 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageVersionInfo.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageVersionInfo.g.h" + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageVersionInfo : PackageVersionInfoT + { + PackageVersionInfo() = default; + void Initialize(std::shared_ptr<::AppInstaller::Repository::IPackageVersion> packageVersion); + + hstring GetMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField const& metadataField); + hstring Id(); + hstring DisplayName(); + hstring Version(); + hstring Channel(); + winrt::Windows::Foundation::Collections::IVectorView PackageFamilyNames(); + winrt::Windows::Foundation::Collections::IVectorView ProductCodes(); + winrt::Microsoft::Management::Deployment::PackageCatalog PackageCatalog(); + private: + winrt::Microsoft::Management::Deployment::PackageCatalog m_packageCatalog{ nullptr }; + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> m_packageVersion; + Windows::Foundation::Collections::IVector m_packageFamilyNames{ nullptr }; + Windows::Foundation::Collections::IVector m_productCodes{ nullptr }; + }; +} diff --git a/src/Microsoft.Management.Deployment/PropertySheet.props b/src/Microsoft.Management.Deployment/PropertySheet.props new file mode 100644 index 0000000000..e34141b019 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/packages.config b/src/Microsoft.Management.Deployment/packages.config new file mode 100644 index 0000000000..5e899619db --- /dev/null +++ b/src/Microsoft.Management.Deployment/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/pch.cpp b/src/Microsoft.Management.Deployment/pch.cpp new file mode 100644 index 0000000000..147dc1b6e0 --- /dev/null +++ b/src/Microsoft.Management.Deployment/pch.cpp @@ -0,0 +1,3 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" diff --git a/src/Microsoft.Management.Deployment/pch.h b/src/Microsoft.Management.Deployment/pch.h new file mode 100644 index 0000000000..c25df203ef --- /dev/null +++ b/src/Microsoft.Management.Deployment/pch.h @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include +#include +#include diff --git a/src/WinGetServer/PropertySheet.props b/src/WinGetServer/PropertySheet.props new file mode 100644 index 0000000000..273b9fbfed --- /dev/null +++ b/src/WinGetServer/PropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/WinGetServer/WinGetServer.exe.manifest b/src/WinGetServer/WinGetServer.exe.manifest new file mode 100644 index 0000000000..0dba1d9b9e --- /dev/null +++ b/src/WinGetServer/WinGetServer.exe.manifest @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/WinGetServer/WinGetServer.rc b/src/WinGetServer/WinGetServer.rc new file mode 100644 index 0000000000000000000000000000000000000000..6d285329154a138f9216537b866148988d06f675 GIT binary patch literal 2664 zcmdUxTTAOe5Xb+|g5M#wFN$KlJo%`t7CE&_FQQN=VvQDRJTxu7_}M+@HyamYlPF#u zBFlC*)0x?s|6G!P&o$K)=tN^(YpMsu>`*g!=kQ|9b)^YUb*-}k-RedWdkTLB9l@JI zO>fTWnsdODSsUvwGMie~61UVGt-_7?cY^fD$yPG@o4QlQNtyg zL&rMRP#qn@ZE$X@rAs(neou4&r^VUdZ$6Z7dG9<8)C8ABj6+a*&^__bK*wX?I-RZ>XYU#ks|yti8}>b(odZbbi0 z$W%-X1X?BM5l_AkyPT4)jJ1|i1#zmU#tyHQH@!8&;=Ycks-&m7^iQAes&n&@>T0pf z?h`90)a+rSLk$nnYNNmAZf(JB!|g+xu1@!~ftBqApNcbSb$*UVf6#9Hx}MJOR-ap= zwzol~Sn`0#o37tYHh5Y2^K+oBmlPO~3yZ_E?q+hqO25VR5}je2>_7V)mVz&HI09JUAu* literal 0 HcmV?d00001 diff --git a/src/WinGetServer/WinGetServer.vcxproj b/src/WinGetServer/WinGetServer.vcxproj new file mode 100644 index 0000000000..8239fe8105 --- /dev/null +++ b/src/WinGetServer/WinGetServer.vcxproj @@ -0,0 +1,176 @@ + + + + + true + true + true + true + 15.0 + {2b00d362-ac92-41f3-a8d2-5b1599bdca01} + Win32Proj + WinGetServer + 10.0.18362.0 + 10.0.17763.0 + true + true + WinGetServer + WindowsPackageManagerServer + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + Application + v140 + v141 + v142 + Unicode + + + true + true + + + false + true + false + Spectre + + + + + + + + + + + + + + + $(SolutionDir)$(PlatformTarget)\$(Configuration)\$(ProjectName)\ + $(PlatformTarget)\$(Configuration)\ + $(OutDir)..\Microsoft.Management.Deployment;$(OutDir)..\AppInstallerCLICore;$(OutDir)..\JsonCppLib;$(OutDir)..\AppInstallerRepositoryCore;$(OutDir)..\YamlCppLib;$(OutDir)..\AppInstallerCommonCore;$(OutDir)..\cpprestsdk;$(LibraryPath) + true + false + ..\CodeAnalysis.ruleset + + + + NotUsing + pch.h + $(IntDir)pch.pch + _CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + Level4 + %(AdditionalOptions) /permissive- /bigobj + true + true + true + $(ProjectDir)..\AppInstallerCLICore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories) + + + Windows + false + $(OutDir)..\Microsoft.Management.Deployment;$(OutDir)..\AppInstallerCLICore;$(OutDir)..\JsonCppLib;$(OutDir)..\AppInstallerRepositoryCore;$(OutDir)..\YamlCppLib;$(OutDir)..\AppInstallerCommonCore;$(OutDir)..\cpprestsdk;%(AdditionalLibraryDirectories) + Microsoft.Management.Deployment.lib;AppInstallerCLICore.lib;AppInstallerCommonCore.lib;AppInstallerRepositoryCore.lib;JsonCppLib.lib;YamlCppLib.lib;cpprestsdk.lib;wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;%(AdditionalDependencies) + + + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + + + + + WIN32;%(PreprocessorDefinitions) + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + + + + + + + + {5890d6ed-7c3b-40f3-b436-b54f640d9e65} + + + {5eb88068-5fb9-4e69-89b2-72dbc5e068f9} + + + {866c3f06-636f-4be8-bc24-5f86ecc606a1} + + + {82b39fda-e86b-4713-a873-9d56de00247a} + + + {8bb94bb8-374f-4294-bca1-c7811514a6b7} + + + {1cc41a9a-ae66-459d-9210-1e572dd7be69} + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/WinGetServer/WinGetServer.vcxproj.filters b/src/WinGetServer/WinGetServer.vcxproj.filters new file mode 100644 index 0000000000..1b7135e521 --- /dev/null +++ b/src/WinGetServer/WinGetServer.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + + + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/WinGetServer/WinMain.cpp b/src/WinGetServer/WinMain.cpp new file mode 100644 index 0000000000..7299712360 --- /dev/null +++ b/src/WinGetServer/WinMain.cpp @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include "COMContext.h" + +using namespace winrt::Microsoft::Management::Deployment; + +CoCreatableClassWrlCreatorMapInclude(PackageManager1); +CoCreatableClassWrlCreatorMapInclude(PackageManager2); +CoCreatableClassWrlCreatorMapInclude(FindPackagesOptions1); +CoCreatableClassWrlCreatorMapInclude(FindPackagesOptions2); +CoCreatableClassWrlCreatorMapInclude(CreateCompositePackageCatalogOptions1); +CoCreatableClassWrlCreatorMapInclude(CreateCompositePackageCatalogOptions2); +CoCreatableClassWrlCreatorMapInclude(InstallOptions1); +CoCreatableClassWrlCreatorMapInclude(InstallOptions2); +CoCreatableClassWrlCreatorMapInclude(PackageMatchFilter1); +CoCreatableClassWrlCreatorMapInclude(PackageMatchFilter2); + +// Holds the wwinmain open until COM tells us there are no more server connections +wil::unique_event _comServerExitEvent; + +// Routine Description: +// - Called back when COM says there is nothing left for our server to do and we can tear down. +static void _releaseNotifier() noexcept +{ + _comServerExitEvent.SetEvent(); +} + +// Check whether the packaged api is enabled and the overarching winget group policy is enabled. +bool IsServerEnabled() +{ + if (!::AppInstaller::Settings::ExperimentalFeature::IsEnabled(::AppInstaller::Settings::ExperimentalFeature::Feature::PackagedAPI)) + { + return false; + } + if (!::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::WinGet)) + { + return false; + } + + return true; +} + +int __stdcall wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR, _In_ int) +{ + winrt::init_apartment(); + + // Enable fast rundown of objects so that the server exits faster when clients go away. + { + winrt::com_ptr globalOptions; + winrt::check_hresult(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&globalOptions))); + winrt::check_hresult(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN)); + } + + AppInstaller::COMContext::SetLoggers(); + + _comServerExitEvent.create(); + auto& module = ::Microsoft::WRL::Module<::Microsoft::WRL::ModuleType::OutOfProc>::Create(&_releaseNotifier); + try + { + if (!IsServerEnabled()) + { + return 0; + } + + // Register all the CoCreatableClassWrlCreatorMapInclude classes + RETURN_IF_FAILED(module.RegisterObjects()); + _comServerExitEvent.wait(); + RETURN_IF_FAILED(module.UnregisterObjects()); + + } + CATCH_RETURN() + + return 0; +} diff --git a/src/WinGetServer/packages.config b/src/WinGetServer/packages.config new file mode 100644 index 0000000000..5e899619db --- /dev/null +++ b/src/WinGetServer/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/WinGetServer/resource.h b/src/WinGetServer/resource.h new file mode 100644 index 0000000000..283fde90e5 --- /dev/null +++ b/src/WinGetServer/resource.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/WinGetUtil/Exports.cpp b/src/WinGetUtil/Exports.cpp index 9a4b3706bc..d94466a9b6 100644 --- a/src/WinGetUtil/Exports.cpp +++ b/src/WinGetUtil/Exports.cpp @@ -34,6 +34,7 @@ extern "C" if (!AppInstaller::Logging::Log().ContainsLogger(loggerName)) { + // Let FileLogger use default file prefix AppInstaller::Logging::AddFileLogger(pathAsPath); } diff --git a/src/WinGetUtil/WinGetUtil.vcxproj b/src/WinGetUtil/WinGetUtil.vcxproj index 9d5d04d3b9..1a2451a3b4 100644 --- a/src/WinGetUtil/WinGetUtil.vcxproj +++ b/src/WinGetUtil/WinGetUtil.vcxproj @@ -1,6 +1,6 @@  - + true true @@ -316,15 +316,15 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/WinGetUtil/packages.config b/src/WinGetUtil/packages.config index bca9425304..5e899619db 100644 --- a/src/WinGetUtil/packages.config +++ b/src/WinGetUtil/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file From 0bbe093d75e275cba5ad4d1df83655563f098801 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 24 Jun 2021 16:50:57 -0300 Subject: [PATCH 34/82] style and details --- .../Public/winget/ManifestCommon.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 53a0a5bf96..565ac9646e 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -3,7 +3,7 @@ #pragma once #include #include - +#include #include #include @@ -146,7 +146,13 @@ namespace AppInstaller::Manifest { const auto& newDependencyVersion = AppInstaller::Utility::Version(newDependency.MinVersion.value()); const auto& existingDependencyVersion = AppInstaller::Utility::Version(existingDependency->MinVersion.value()); - existingDependency->MinVersion.value() = newDependencyVersion <= existingDependencyVersion ? existingDependencyVersion.ToString() : newDependencyVersion.ToString(); + if (newDependencyVersion <= existingDependencyVersion) { + existingDependency->MinVersion.value() = existingDependencyVersion.ToString(); + } + else + { + existingDependency->MinVersion.value() = newDependencyVersion.ToString(); + } } else { @@ -186,7 +192,7 @@ namespace AppInstaller::Manifest return &dependency; } } - return NULL; + return nullptr; } // for testing purposes From f0f3676d9fe01cb077b154d5f04d9d9abe3010e9 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 24 Jun 2021 18:04:55 -0300 Subject: [PATCH 35/82] fix merge conflicts --- src/AppInstallerCommonCore/ExperimentalFeature.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index a4d83d4d7b..323e2c88fd 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -76,6 +76,10 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "Argument Sample", "experimentalArg", "https://aka.ms/winget-settings", Feature::ExperimentalArg }; case Feature::ExperimentalMSStore: return ExperimentalFeature{ "Microsoft Store Support", "experimentalMSStore", "https://aka.ms/winget-settings", Feature::ExperimentalMSStore }; + case Feature::PackagedAPI: + return ExperimentalFeature{ "Packaged API Support", "packagedAPI", "https://aka.ms/winget-settings", Feature::PackagedAPI }; + case Feature::Dependencies: + return ExperimentalFeature{ "Show Dependencies Information", "dependencies", "https://aka.ms/winget-settings", Feature::Dependencies }; default: THROW_HR(E_UNEXPECTED); } From dd03b85decd11af52b6d843f6c1c9223578c2ec5 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 24 Jun 2021 18:17:33 -0300 Subject: [PATCH 36/82] spellcheck exception --- .github/actions/spelling/expect.txt | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index e3ebcb83c4..b93ce7e393 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -1,5 +1,4 @@ abcd -abifind adml admx affle @@ -61,9 +60,8 @@ ci cinq CLIE cloudapp -CLSID +clsid COINIT -combaseapi COMGLB commandline Concat @@ -73,7 +71,6 @@ contoso contractversion count'th countryregion -cppwinrtprocess createmanifestmetadata cstdint ctc @@ -122,7 +119,7 @@ GES GESMBH GHS gity -Globals +globals helplib helplibrary hhx @@ -130,20 +127,15 @@ HINSTANCE hkey hlocal hre -HRESULTs +hresults htm -IApp IAttachment IConfiguration -idl idx -IFind -IGet IGlobal IHelp IHost IID -iinstall IInstalled IISOn img @@ -197,7 +189,7 @@ MBH megamorf memcpy middleware -MIDL +midl minexample minschema MMmmbbbb @@ -230,7 +222,6 @@ NX objbase ofile Packagedx -Packageend pathparts pathpaths PCs @@ -244,7 +235,6 @@ PMS positionals powertoys productcode -pseudocode pvk pvm pwabuilder @@ -288,6 +278,7 @@ SPAPI Srinivasan SRL srs +standalone startswith streambuf strtoull @@ -359,7 +350,6 @@ wsv wto wwinmain WZDNCRFJ -Xaml XPLATSTR xsi zy From 2985cd5d2b6792ad53f2e76d291d65fa597c5dda Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 28 Jun 2021 12:31:24 -0300 Subject: [PATCH 37/82] detail when adding dep --- src/AppInstallerCommonCore/Public/winget/ManifestCommon.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 565ac9646e..49153c0601 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -146,10 +146,7 @@ namespace AppInstaller::Manifest { const auto& newDependencyVersion = AppInstaller::Utility::Version(newDependency.MinVersion.value()); const auto& existingDependencyVersion = AppInstaller::Utility::Version(existingDependency->MinVersion.value()); - if (newDependencyVersion <= existingDependencyVersion) { - existingDependency->MinVersion.value() = existingDependencyVersion.ToString(); - } - else + if (newDependencyVersion > existingDependencyVersion) { existingDependency->MinVersion.value() = newDependencyVersion.ToString(); } From 021e326ea32e8b357749e60def46f7aab33a741a Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 29 Jun 2021 18:14:57 -0300 Subject: [PATCH 38/82] proto --- .../Workflows/DependenciesFlow.cpp | 81 ++++++++++++++++--- .../Workflows/DependenciesFlow.h | 6 ++ .../Workflows/InstallFlow.cpp | 10 +-- .../Workflows/InstallFlow.h | 9 +++ src/AppInstallerCLITests/WorkFlow.cpp | 33 ++++++++ 5 files changed, 120 insertions(+), 19 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 41bb4e9663..49a0931034 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -3,9 +3,14 @@ #include "pch.h" #include "DependenciesFlow.h" +#include "ManifestComparator.h" +#include "InstallFlow.h" namespace AppInstaller::CLI::Workflow { + using namespace AppInstaller::Repository; + using namespace Manifest; + void ReportDependencies::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) @@ -19,22 +24,22 @@ namespace AppInstaller::CLI::Workflow { info << Resource::StringId(m_messageId) << std::endl; - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) + if (dependencies.HasAnyOf(DependencyType::WindowsFeature)) { info << " - " << Resource::String::WindowsFeaturesDependencies << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + dependencies.ApplyToType(DependencyType::WindowsFeature, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); } - if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibrary)) + if (dependencies.HasAnyOf(DependencyType::WindowsLibrary)) { info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::WindowsLibrary, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + dependencies.ApplyToType(DependencyType::WindowsLibrary, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); } - if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) + if (dependencies.HasAnyOf(DependencyType::Package)) { info << " - " << Resource::String::PackageDependencies << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) + dependencies.ApplyToType(DependencyType::Package, [&info](Dependency dependency) { info << " " << dependency.Id; if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; @@ -42,10 +47,10 @@ namespace AppInstaller::CLI::Workflow }); } - if (dependencies.HasAnyOf(Manifest::DependencyType::External)) + if (dependencies.HasAnyOf(DependencyType::External)) { info << " - " << Resource::String::ExternalDependencies << std::endl; - dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " " << dependency.Id << std::endl; }); + dependencies.ApplyToType(DependencyType::External, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); } } } @@ -54,7 +59,7 @@ namespace AppInstaller::CLI::Workflow if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { const auto& manifest = context.Get(); - Manifest::DependencyList allDependencies; + DependencyList allDependencies; for (const auto& installer : manifest.Installers) { @@ -82,7 +87,63 @@ namespace AppInstaller::CLI::Workflow if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { // TODO make best effort to get the correct installer information, it may be better to have a record of installations and save the correct installers - context.Add(Manifest::DependencyList()); // sending empty list of dependencies for now + context.Add(DependencyList()); // sending empty list of dependencies for now + } + } + + void OpenDependencySource(Execution::Context& context) + { + // two version: have a package version or a manifest + // openDependencySource, new context data "DependencySource" + // + //TODO change this, configure the source we want + context << OpenSource; + const auto& source = context.Get(); + /*const auto& source = context.Get()->GetLatestAvailableVersion()->GetSource(); + context.Add(std::move(source));*/ + } + + void BuildPackageDependenciesGraph(Execution::Context& context) + { + + const auto& dependencies = context.Get(); + + //TODO change this, configure the source we want + context << OpenSource; + const auto& source = context.Get(); + /*const auto& source = context.Get()->GetLatestAvailableVersion()->GetSource(); + context.Add(std::move(source));*/ + + std::vector toCheck; + dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + toCheck.push_back(dependency); + }); + + std::vector toInstall; + for (const auto& dependency : toCheck) + { + // search the package to see if it exists + SearchRequest searchRequest; + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependency.Id)); + const auto& matches = source->Search(searchRequest).Matches; + + if (!matches.empty()) + { + const auto& match = matches.at(0); // What to do if there's more than one? should not happen (report to the user) + const auto& installedVersion = match.Package->GetInstalledVersion(); + if (!installedVersion) + { + // get manifest and installer, maybe use something like vector as in InstallMultiple? + //how to choose best installer, should I use SelectInstaller or not? + //toInstall.push_back(PackagesAndInstallers(installer, )); + + //match.Package->GetLatestAvailableVersion()->GetManifest().Installers.at(0).Dependencies; + } + } } + + context.Reporter.Info() << "---" << std::endl; + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index e781411d47..7e388cc470 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -38,4 +38,10 @@ namespace AppInstaller::CLI::Workflow // Inputs: None // Outputs: Dependencies void GetDependenciesInfoForUninstall(Execution::Context& context); + + // Builds the dependency graph. + // Required Args: None + // Inputs: Dependencies + // Outputs: None + void BuildPackageDependenciesGraph(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 806bcd52ac..d446078c49 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -411,18 +411,10 @@ namespace AppInstaller::CLI::Workflow Workflow::EnsureApplicableInstaller << Workflow::GetDependenciesFromInstaller << Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + Workflow::BuildPackageDependenciesGraph << Workflow::InstallPackageInstaller; } - const struct PackagesAndInstallers - { - PackagesAndInstallers(std::optional inst, - AppInstaller::CLI::Execution::PackageToInstall pkg) : Installer(inst), Package(pkg) {} - - std::optional Installer; - AppInstaller::CLI::Execution::PackageToInstall Package; - }; - void InstallMultiple(Execution::Context& context) { bool allSucceeded = true; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 30837e856c..98feca3304 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -106,4 +106,13 @@ namespace AppInstaller::CLI::Workflow // Inputs: ARPSnapshot?, Manifest, PackageVersion // Outputs: None void ReportARPChanges(Execution::Context& context); + + const struct PackagesAndInstallers + { + PackagesAndInstallers(std::optional inst, + AppInstaller::CLI::Execution::PackageToInstall pkg) : Installer(inst), Package(pkg) {} + + std::optional Installer; + AppInstaller::CLI::Execution::PackageToInstall Package; + }; } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index e767e8725b..2e42668ba9 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -237,6 +237,39 @@ namespace } }; + struct DependenciesTestSource : public TestSource + { + SearchResult Search(const SearchRequest& request) const override + { + SearchResult result; + + std::string input; + + if (request.Query) + { + input = request.Query->Value; + } + + if (input == "A.Dep.B") + { + ManifestInstaller installer; + installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); + + Manifest manifest; + manifest.Id = "A.Dep.B"; + manifest.Installers.push_back(installer); + + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + std::vector{ manifest }, + this->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); + } + } + }; + struct TestContext; struct WorkflowTaskOverride From 4e623d43bf6018a0ddc23fe55fa239a9c9350517 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 29 Jun 2021 20:19:41 -0300 Subject: [PATCH 39/82] divide identity report and installation disclaimer from actual installation --- src/AppInstallerCLICore/Commands/UpgradeCommand.cpp | 2 ++ src/AppInstallerCLICore/Workflows/InstallFlow.cpp | 11 +++++++++-- src/AppInstallerCLICore/Workflows/InstallFlow.h | 6 ++++++ src/AppInstallerCLICore/Workflows/UpdateFlow.cpp | 1 + 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 9f85569538..7e1b066995 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -150,6 +150,7 @@ namespace AppInstaller::CLI EnsureUpdateVersionApplicable << SelectInstaller << EnsureApplicableInstaller << + ReportIdentityAndInstallationDisclaimer << GetDependenciesFromInstaller << ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << InstallPackageInstaller; @@ -179,6 +180,7 @@ namespace AppInstaller::CLI } context << + ReportIdentityAndInstallationDisclaimer << GetDependenciesFromInstaller << ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << InstallPackageInstaller; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 806bcd52ac..2f6fd43126 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -388,11 +388,16 @@ namespace AppInstaller::CLI::Workflow } } - void InstallPackageInstaller(Execution::Context& context) + void ReportIdentityAndInstallationDisclaimer(Execution::Context& context) { context << Workflow::ReportManifestIdentity << - Workflow::ShowInstallationDisclaimer << + Workflow::ShowInstallationDisclaimer; + } + + void InstallPackageInstaller(Execution::Context& context) + { + context << Workflow::ReportExecutionStage(ExecutionStage::Download) << Workflow::DownloadInstaller << Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << @@ -409,6 +414,7 @@ namespace AppInstaller::CLI::Workflow context << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << + Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::GetDependenciesFromInstaller << Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; @@ -484,6 +490,7 @@ namespace AppInstaller::CLI::Workflow installContext.Add(installer); installContext << + ReportIdentityAndInstallationDisclaimer << Workflow::InstallPackageInstaller; if (installContext.IsTerminated()) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 30837e856c..ac989be7c2 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -77,6 +77,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void RemoveInstaller(Execution::Context& context); + // Reports manifest identity and shows installation disclaimer + // Required Args: None + // Inputs: Manifest + // Outputs: None + void ReportIdentityAndInstallationDisclaimer(Execution::Context& context); + // Installs a specific package installer. // Required Args: None // Inputs: Manifest, Installer diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index c363658a5a..238e724ffe 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -113,6 +113,7 @@ namespace AppInstaller::CLI::Workflow updateAllFoundUpdate = true; updateContext << + ReportIdentityAndInstallationDisclaimer << GetDependenciesFromInstaller << ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << InstallPackageInstaller; From 160242bcd11fc95f672020de954a9ddda4638885 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 6 Jul 2021 12:53:11 -0300 Subject: [PATCH 40/82] proto tests --- .../ExecutionContextData.h | 7 ++ .../Workflows/DependenciesFlow.cpp | 103 ++++++++++----- .../Workflows/DependenciesFlow.h | 10 +- .../Workflows/InstallFlow.cpp | 3 +- src/AppInstallerCLITests/WorkFlow.cpp | 119 +++++++++++++++--- 5 files changed, 194 insertions(+), 48 deletions(-) diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index e0d8b63d57..414cb47699 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -48,6 +48,7 @@ namespace AppInstaller::CLI::Execution Sources, ARPSnapshot, Dependencies, + DependencySource, Max }; @@ -191,5 +192,11 @@ namespace AppInstaller::CLI::Execution { using value_t = Manifest::DependencyList; }; + + template <> + struct DataMapping + { + using value_t = std::shared_ptr; + }; } } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 49a0931034..0b81ef5851 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -4,7 +4,6 @@ #include "pch.h" #include "DependenciesFlow.h" #include "ManifestComparator.h" -#include "InstallFlow.h" namespace AppInstaller::CLI::Workflow { @@ -93,52 +92,96 @@ namespace AppInstaller::CLI::Workflow void OpenDependencySource(Execution::Context& context) { - // two version: have a package version or a manifest - // openDependencySource, new context data "DependencySource" - // - //TODO change this, configure the source we want - context << OpenSource; - const auto& source = context.Get(); - /*const auto& source = context.Get()->GetLatestAvailableVersion()->GetSource(); - context.Add(std::move(source));*/ + // two options: have a package version or a manifest + // try to get source from package version + const auto& packageVersion = context.Get(); + + if (packageVersion) + { + // TODO open composite source: package + installed + // how to execute without progress? + //auto installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); + //auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); + auto compositeSource = packageVersion->GetSource(); + context.Add(compositeSource); + } + else + { + // TODO question to John: can/should we do nothing for local manifests? or set up something like --dependency-source + const auto& manifest = context.Get(); + } } void BuildPackageDependenciesGraph(Execution::Context& context) { - - const auto& dependencies = context.Get(); - - //TODO change this, configure the source we want - context << OpenSource; - const auto& source = context.Get(); - /*const auto& source = context.Get()->GetLatestAvailableVersion()->GetSource(); - context.Add(std::move(source));*/ + context << OpenDependencySource; + const auto& source = context.Get(); std::vector toCheck; - dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) - { - toCheck.push_back(dependency); - }); - std::vector toInstall; + // should this dictionary have Dependency as key? (to have min version) in this case Dependency should implement equal + std::map> dependencyGraph; // < package Id, dependencies > + std::map> inverseDependencyGraph; // < package Id, packages that depends on this one> + + const auto& installer = context.Get(); + if (installer) + { + dependencyGraph[installer->ProductId] = std::vector(); // ProductId is the same Id as the one used by Dependencies? + installer->Dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + toCheck.push_back(dependency); + dependencyGraph[dependency.Id] = std::vector(); + dependencyGraph[installer->ProductId].push_back(dependency); + + inverseDependencyGraph[dependency.Id] = std::vector{ installer->ProductId }; + }); + } // TODO fail otherwise + for (const auto& dependency : toCheck) { - // search the package to see if it exists + // search the package source+installed to see if the dep exists SearchRequest searchRequest; searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependency.Id)); const auto& matches = source->Search(searchRequest).Matches; if (!matches.empty()) { - const auto& match = matches.at(0); // What to do if there's more than one? should not happen (report to the user) - const auto& installedVersion = match.Package->GetInstalledVersion(); - if (!installedVersion) + const auto& match = matches.at(0); // What to do if there's more than one? TODO should not happen (report to the user) + const auto& package = match.Package; + if (!package->GetInstalledVersion()) { - // get manifest and installer, maybe use something like vector as in InstallMultiple? + const auto& packageVersion = package->GetLatestAvailableVersion(); //how to choose best installer, should I use SelectInstaller or not? - //toInstall.push_back(PackagesAndInstallers(installer, )); - - //match.Package->GetLatestAvailableVersion()->GetManifest().Installers.at(0).Dependencies; + const auto& installer = packageVersion->GetManifest().Installers.at(0); // fail if there's no installer? + const auto& dependencies = installer.Dependencies; + auto packageId = installer.ProductId; // ProductId is the same Id as the one used by Dependencies? + + // TODO save installers for later maybe? + dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + dependencyGraph[packageId].push_back(dependency); + + auto search = dependencyGraph.find(dependency.Id); + if (search == dependencyGraph.end()) // if not found + { + toCheck.push_back(dependency); + dependencyGraph[dependency.Id] = std::vector(); + + inverseDependencyGraph[dependency.Id] = std::vector{ packageId }; + } + else + { + // we only need to check for loops if the dependency already existed, right? + // should we have an inverse map? i.e., < id, packages that depend on this one > + // that can make searching for loops easier + + inverseDependencyGraph[dependency.Id].push_back(packageId); + bool hasLoop = false; + auto searchLoop = inverseDependencyGraph[dependency.Id]; + + + } + }); } } } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 7e388cc470..67eb8b70f1 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -41,7 +41,13 @@ namespace AppInstaller::CLI::Workflow // Builds the dependency graph. // Required Args: None - // Inputs: Dependencies - // Outputs: None + // Inputs: DependencySource + // Outputs: Dependencies void BuildPackageDependenciesGraph(Execution::Context& context); + + // Sets up the source used to get the dependencies. + // Required Args: None + // Inputs: PackageVersion, Manifest + // Outputs: DependencySource + void OpenDependencySource(Execution::Context& context); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index f999d450e7..1be6c27851 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -415,9 +415,8 @@ namespace AppInstaller::CLI::Workflow Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << - Workflow::GetDependenciesFromInstaller << - Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::BuildPackageDependenciesGraph << + Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 2e42668ba9..658d369e0c 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -248,25 +248,85 @@ namespace if (request.Query) { input = request.Query->Value; + } // else: default? + + ManifestInstaller installer; + Manifest manifest; + manifest.Installers.push_back(installer); + manifest.Id = input; + + /* + * Dependencies: + * "A": Depends on the test + * B: NoDeph + * C: B + * D: E + * E: D + * F: B + * G: C + * H: G, B + */ + + //-- predefined + if (input == "C") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); } - - if (input == "A.Dep.B") + if (input == "D") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "E")); + } + if (input == "E") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "D")); + } + if (input == "F") { - ManifestInstaller installer; + installer.Dependencies.Add(Dependency(DependencyType::Package, "D")); + } + if (input == "G") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "C")); + } + if (input == "H") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "G")); installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); + } - Manifest manifest; - manifest.Id = "A.Dep.B"; - manifest.Installers.push_back(installer); - - result.Matches.emplace_back( - ResultMatch( - TestPackage::Make( - std::vector{ manifest }, - this->shared_from_this() - ), - PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); + // depends on test + if (input == "StackOrderIsOk") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "C")); + } + if (input == "NeedsToInstallBFirst") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); + installer.Dependencies.Add(Dependency(DependencyType::Package, "C")); + } + if (input == "EasyToSeeLoop") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "D")); + } + if (input == "DependencyAlreadyInStackButNoLoop") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "C")); + installer.Dependencies.Add(Dependency(DependencyType::Package, "F")); + } + if (input == "PathBetweenBranchesButNoLoop") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "C")); + installer.Dependencies.Add(Dependency(DependencyType::Package, "H")); } + + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + std::vector{ manifest }, + this->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); + return result; } }; @@ -394,6 +454,14 @@ void OverrideForImportSource(TestContext& context) } }); } +void OverrideForDependencySource(TestContext& context) +{ + context.Override({ "OpenDependencySource", [](TestContext& context) + { + context.Add(std::shared_ptr{ std::make_shared() }); + } }); +} + void OverrideForUpdateInstallerMotw(TestContext& context) { context.Override({ UpdateInstallerFileMotwIfApplicable, [](TestContext&) @@ -1685,6 +1753,29 @@ TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } +TEST_CASE("InstallFlow_DependencyGraph", "[InstallFlow][workflow][dependencies]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForDependencySource(context); + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + // Verify all types of dependencies are printed + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); + REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); +} + TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") { std::ostringstream validateOutput; From 06ddcd659b7bfba161da40215217f01901beb59c Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 7 Jul 2021 18:10:26 -0300 Subject: [PATCH 41/82] testing --- .../Workflows/DependenciesFlow.cpp | 73 ++++++++++++++----- .../Workflows/DependenciesFlow.h | 3 + .../Workflows/InstallFlow.cpp | 2 +- src/AppInstallerCLITests/WorkFlow.cpp | 31 ++++++-- 4 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 0b81ef5851..94535637c8 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -108,7 +108,7 @@ namespace AppInstaller::CLI::Workflow else { // TODO question to John: can/should we do nothing for local manifests? or set up something like --dependency-source - const auto& manifest = context.Get(); + //const auto& manifest = context.Get(); } } @@ -121,7 +121,6 @@ namespace AppInstaller::CLI::Workflow // should this dictionary have Dependency as key? (to have min version) in this case Dependency should implement equal std::map> dependencyGraph; // < package Id, dependencies > - std::map> inverseDependencyGraph; // < package Id, packages that depends on this one> const auto& installer = context.Get(); if (installer) @@ -133,15 +132,14 @@ namespace AppInstaller::CLI::Workflow dependencyGraph[dependency.Id] = std::vector(); dependencyGraph[installer->ProductId].push_back(dependency); - inverseDependencyGraph[dependency.Id] = std::vector{ installer->ProductId }; }); } // TODO fail otherwise - for (const auto& dependency : toCheck) + for (const auto& dependencyNode : toCheck) { // search the package source+installed to see if the dep exists SearchRequest searchRequest; - searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependency.Id)); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependencyNode.Id)); const auto& matches = source->Search(searchRequest).Matches; if (!matches.empty()) @@ -150,43 +148,78 @@ namespace AppInstaller::CLI::Workflow const auto& package = match.Package; if (!package->GetInstalledVersion()) { - const auto& packageVersion = package->GetLatestAvailableVersion(); + const auto& packageVersion = package->GetLatestAvailableVersion(); //TODO check availability before using //how to choose best installer, should I use SelectInstaller or not? - const auto& installer = packageVersion->GetManifest().Installers.at(0); // fail if there's no installer? - const auto& dependencies = installer.Dependencies; - auto packageId = installer.ProductId; // ProductId is the same Id as the one used by Dependencies? + const auto& packageVersionManifest = packageVersion->GetManifest(); + const auto& matchInstaller = packageVersionManifest.Installers.at(0); // fail if there's no installer? + const auto& matchDependencies = matchInstaller.Dependencies; + auto matchId = matchInstaller.ProductId; // ProductId is the same Id as the one used by Dependencies? // TODO save installers for later maybe? - dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + matchDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { - dependencyGraph[packageId].push_back(dependency); + dependencyGraph[matchId].push_back(dependency); auto search = dependencyGraph.find(dependency.Id); if (search == dependencyGraph.end()) // if not found { toCheck.push_back(dependency); dependencyGraph[dependency.Id] = std::vector(); - - inverseDependencyGraph[dependency.Id] = std::vector{ packageId }; } else { // we only need to check for loops if the dependency already existed, right? // should we have an inverse map? i.e., < id, packages that depend on this one > - // that can make searching for loops easier - - inverseDependencyGraph[dependency.Id].push_back(packageId); - bool hasLoop = false; - auto searchLoop = inverseDependencyGraph[dependency.Id]; - - + if (graphHasLoop(dependencyGraph)) + { + context.Reporter.Info() << "has loop" << std::endl; + //TODO warn user and raise error + } } }); } + // TODO else: save information on dependencies already installed to inform the user? } } context.Reporter.Info() << "---" << std::endl; } + + // TODO make them iterative + // is there a better way that this to check for loops? + bool graphHasLoop(std::map> dependencyGraph) + { + for (const auto& node : dependencyGraph) { + auto visited = std::set(); + visited.insert(node.first); + if (hasLoopDFS(visited, node.first, dependencyGraph)) + { + return true; + } + } + return false; + } + + bool hasLoopDFS(std::set visited, string_t nodeId, std::map> dependencyGraph) + { + for (const auto& adjacent : dependencyGraph[nodeId]) + { + auto search = visited.find(adjacent.Id); + if (search == visited.end()) // if not found + { + visited.insert(adjacent.Id); + if (hasLoopDFS(visited, adjacent.Id, dependencyGraph)) + { + return true; + } + } + else + { + return true; + } + } + + return false; + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 67eb8b70f1..1d3ca0fb91 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -50,4 +50,7 @@ namespace AppInstaller::CLI::Workflow // Inputs: PackageVersion, Manifest // Outputs: DependencySource void OpenDependencySource(Execution::Context& context); + + bool graphHasLoop(std::map> dependencyGraph); + bool hasLoopDFS(std::set visited, AppInstaller::Manifest::string_t nodeId, std::map> dependencyGraph); } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 1be6c27851..d4fe70d361 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -416,7 +416,7 @@ namespace AppInstaller::CLI::Workflow Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::BuildPackageDependenciesGraph << - Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + //Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 658d369e0c..25112039f1 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -248,12 +248,24 @@ namespace if (request.Query) { input = request.Query->Value; - } // else: default? + } + else if (!request.Inclusions.empty()) + { + input = request.Inclusions[0].Value; + } + else if (!request.Filters.empty()) + { + input = request.Filters[0].Value; + }// else: default? - ManifestInstaller installer; - Manifest manifest; - manifest.Installers.push_back(installer); + auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_Dependencies.yaml")); manifest.Id = input; + manifest.Moniker = input; + // TODO maybe change package name on default locale for better debbugging + + auto& installer = manifest.Installers.at(0); + installer.Dependencies.Clear(); + installer.ProductId = input; /* * Dependencies: @@ -456,9 +468,14 @@ void OverrideForImportSource(TestContext& context) void OverrideForDependencySource(TestContext& context) { + context.Override({ Workflow::OpenSource, [](TestContext& context) + { + context.Add(std::make_shared()); + } }); + context.Override({ "OpenDependencySource", [](TestContext& context) { - context.Add(std::shared_ptr{ std::make_shared() }); + context.Add(std::make_shared()); } }); } @@ -1762,7 +1779,7 @@ TEST_CASE("InstallFlow_DependencyGraph", "[InstallFlow][workflow][dependencies]" OverrideForDependencySource(context); OverrideForShellExecute(context); - context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); + context.Args.AddArg(Execution::Args::Type::Query, "EasyToSeeLoop"sv); TestUserSettings settings; settings.Set({ true }); @@ -1773,7 +1790,7 @@ TEST_CASE("InstallFlow_DependencyGraph", "[InstallFlow][workflow][dependencies]" // Verify all types of dependencies are printed REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); - REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); + REQUIRE(installOutput.str().find("has loop") != std::string::npos); } TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") From e6d7b8256c9e478410995b29262c1f89ac85584c Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 8 Jul 2021 14:58:51 -0300 Subject: [PATCH 42/82] questions --- src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 94535637c8..8508e4c585 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -120,7 +120,7 @@ namespace AppInstaller::CLI::Workflow std::vector toCheck; // should this dictionary have Dependency as key? (to have min version) in this case Dependency should implement equal - std::map> dependencyGraph; // < package Id, dependencies > + std::map> dependencyGraph; // < package Id, dependencies > value should be a set instead of a vector? const auto& installer = context.Get(); if (installer) @@ -170,6 +170,7 @@ namespace AppInstaller::CLI::Workflow { // we only need to check for loops if the dependency already existed, right? // should we have an inverse map? i.e., < id, packages that depend on this one > + // or should we check for loops only once (when we have all deps in the graph) if (graphHasLoop(dependencyGraph)) { context.Reporter.Info() << "has loop" << std::endl; @@ -185,6 +186,7 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << "---" << std::endl; } + // TODO get dependency installation order from dependencyGraph (topological order) // TODO make them iterative // is there a better way that this to check for loops? @@ -208,7 +210,7 @@ namespace AppInstaller::CLI::Workflow auto search = visited.find(adjacent.Id); if (search == visited.end()) // if not found { - visited.insert(adjacent.Id); + visited.insert(adjacent.Id); //like this is ok or should we insert to a copy? if (hasLoopDFS(visited, adjacent.Id, dependencyGraph)) { return true; From 62ef1c7a7cc7c2e8f7a72a9bf6bb0f9cb35e9145 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 12 Jul 2021 13:58:27 -0300 Subject: [PATCH 43/82] test for dependency graph and fixes --- .../Workflows/DependenciesFlow.cpp | 50 ++++++++++++----- .../Workflows/DependenciesFlow.h | 2 +- src/AppInstallerCLITests/WorkFlow.cpp | 53 +++++++++++++++---- .../Public/winget/ManifestCommon.h | 6 ++- 4 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 8508e4c585..f365ee419f 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -120,7 +120,8 @@ namespace AppInstaller::CLI::Workflow std::vector toCheck; // should this dictionary have Dependency as key? (to have min version) in this case Dependency should implement equal - std::map> dependencyGraph; // < package Id, dependencies > value should be a set instead of a vector? + std::map> dependencyGraph; + // < package Id, dependencies > value should be a set instead of a vector? const auto& installer = context.Get(); if (installer) @@ -135,30 +136,47 @@ namespace AppInstaller::CLI::Workflow }); } // TODO fail otherwise - for (const auto& dependencyNode : toCheck) + std::map failedPackages; + + for (int i = 0; i < toCheck.size(); ++i) { - // search the package source+installed to see if the dep exists + const auto& dependencyNode = toCheck.at(i); + auto packageID = dependencyNode.Id; + SearchRequest searchRequest; - searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependencyNode.Id)); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, packageID)); const auto& matches = source->Search(searchRequest).Matches; if (!matches.empty()) { - const auto& match = matches.at(0); // What to do if there's more than one? TODO should not happen (report to the user) + const auto& match = matches.at(0); + if (matches.size() > 1) { + failedPackages[packageID] = "Too many matches"; //TODO localize all errors + continue; + } + const auto& package = match.Package; if (!package->GetInstalledVersion()) { - const auto& packageVersion = package->GetLatestAvailableVersion(); //TODO check availability before using - //how to choose best installer, should I use SelectInstaller or not? + const auto& packageVersion = package->GetLatestAvailableVersion(); + if (!packageVersion) { + failedPackages[packageID] = "No package version found"; //TODO localize all errors + continue; + } const auto& packageVersionManifest = packageVersion->GetManifest(); - const auto& matchInstaller = packageVersionManifest.Installers.at(0); // fail if there's no installer? + if (packageVersionManifest.Installers.empty()) { + failedPackages[packageID] = "No installers found"; //TODO localize all errors + continue; + } + //how to choose best installer, should I use SelectInstaller or not? + const auto& matchInstaller = packageVersionManifest.Installers.at(0); const auto& matchDependencies = matchInstaller.Dependencies; - auto matchId = matchInstaller.ProductId; // ProductId is the same Id as the one used by Dependencies? // TODO save installers for later maybe? matchDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { - dependencyGraph[matchId].push_back(dependency); + // TODO check dependency min version is <= latest version + dependencyGraph[packageID].push_back(dependency); auto search = dependencyGraph.find(dependency.Id); if (search == dependencyGraph.end()) // if not found @@ -181,6 +199,11 @@ namespace AppInstaller::CLI::Workflow } // TODO else: save information on dependencies already installed to inform the user? } + else + { + failedPackages[packageID] = "No matches"; //TODO localize all errors + continue; + } } context.Reporter.Info() << "---" << std::endl; @@ -203,15 +226,16 @@ namespace AppInstaller::CLI::Workflow return false; } - bool hasLoopDFS(std::set visited, string_t nodeId, std::map> dependencyGraph) + bool hasLoopDFS(std::set visited, const string_t& nodeId, std::map>& dependencyGraph) { for (const auto& adjacent : dependencyGraph[nodeId]) { auto search = visited.find(adjacent.Id); if (search == visited.end()) // if not found { - visited.insert(adjacent.Id); //like this is ok or should we insert to a copy? - if (hasLoopDFS(visited, adjacent.Id, dependencyGraph)) + auto newVisited = visited; + newVisited.insert(adjacent.Id); + if (hasLoopDFS(newVisited, adjacent.Id, dependencyGraph)) { return true; } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 1d3ca0fb91..62a125c039 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -52,5 +52,5 @@ namespace AppInstaller::CLI::Workflow void OpenDependencySource(Execution::Context& context); bool graphHasLoop(std::map> dependencyGraph); - bool hasLoopDFS(std::set visited, AppInstaller::Manifest::string_t nodeId, std::map> dependencyGraph); + bool hasLoopDFS(std::set visited, const AppInstaller::Manifest::string_t& nodeId, std::map>& dependencyGraph); } \ No newline at end of file diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 25112039f1..448975d996 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -294,7 +294,7 @@ namespace } if (input == "F") { - installer.Dependencies.Add(Dependency(DependencyType::Package, "D")); + installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); } if (input == "G") { @@ -472,11 +472,6 @@ void OverrideForDependencySource(TestContext& context) { context.Add(std::make_shared()); } }); - - context.Override({ "OpenDependencySource", [](TestContext& context) - { - context.Add(std::make_shared()); - } }); } void OverrideForUpdateInstallerMotw(TestContext& context) @@ -1770,7 +1765,7 @@ TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } -TEST_CASE("InstallFlow_DependencyGraph", "[InstallFlow][workflow][dependencies]") +TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); @@ -1788,11 +1783,51 @@ TEST_CASE("InstallFlow_DependencyGraph", "[InstallFlow][workflow][dependencies]" install.Execute(context); INFO(installOutput.str()); - // Verify all types of dependencies are printed - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("has loop") != std::string::npos); } +TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGraph]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForDependencySource(context); + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Query, "DependencyAlreadyInStackButNoLoop"sv); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + REQUIRE(installOutput.str().find("has loop") == std::string::npos); +} + +TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForDependencySource(context); + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Query, "PathBetweenBranchesButNoLoop"sv); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + REQUIRE(installOutput.str().find("has loop") == std::string::npos); +} + TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") { std::ostringstream validateOutput; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 49153c0601..a7e8134f80 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -129,6 +129,10 @@ namespace AppInstaller::Manifest Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), Id(std::move(id)), MinVersion(std::move(minVersion)) {} Dependency(DependencyType type, string_t id) : Type(std::move(type)), Id(std::move(id)) {} Dependency(DependencyType type) : Type(type) {} + + bool operator==(const Dependency& rhs) const { + return Type == rhs.Type && ICUCaseInsensitiveEquals(Id, rhs.Id); + } }; struct DependencyList @@ -184,7 +188,7 @@ namespace AppInstaller::Manifest Dependency* HasDependency(const Dependency& dependencyToSearch) { for (auto& dependency : dependencies) { - if (dependency.Type == dependencyToSearch.Type && ICUCaseInsensitiveEquals(dependency.Id, dependencyToSearch.Id)) + if (dependency == dependencyToSearch) { return &dependency; } From 0ed66ee227802f205f7e317b27c28c29e19702fc Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 12 Jul 2021 15:31:42 -0300 Subject: [PATCH 44/82] check loop logic change --- .../Workflows/DependenciesFlow.cpp | 21 ++++++++----------- .../Workflows/DependenciesFlow.h | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index f365ee419f..f84a9a9c3e 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -189,7 +189,7 @@ namespace AppInstaller::CLI::Workflow // we only need to check for loops if the dependency already existed, right? // should we have an inverse map? i.e., < id, packages that depend on this one > // or should we check for loops only once (when we have all deps in the graph) - if (graphHasLoop(dependencyGraph)) + if (graphHasLoop(dependencyGraph, installer->ProductId)) { context.Reporter.Info() << "has loop" << std::endl; //TODO warn user and raise error @@ -213,29 +213,26 @@ namespace AppInstaller::CLI::Workflow // TODO make them iterative // is there a better way that this to check for loops? - bool graphHasLoop(std::map> dependencyGraph) + bool graphHasLoop(std::map> dependencyGraph, const string_t& root) { - for (const auto& node : dependencyGraph) { - auto visited = std::set(); - visited.insert(node.first); - if (hasLoopDFS(visited, node.first, dependencyGraph)) - { - return true; - } + auto visited = std::set(); + visited.insert(root); + if (hasLoopDFS(visited, root, dependencyGraph)) + { + return true; } return false; } bool hasLoopDFS(std::set visited, const string_t& nodeId, std::map>& dependencyGraph) { + visited.insert(nodeId); for (const auto& adjacent : dependencyGraph[nodeId]) { auto search = visited.find(adjacent.Id); if (search == visited.end()) // if not found { - auto newVisited = visited; - newVisited.insert(adjacent.Id); - if (hasLoopDFS(newVisited, adjacent.Id, dependencyGraph)) + if (hasLoopDFS(visited, adjacent.Id, dependencyGraph)) { return true; } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 62a125c039..006561eb3c 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -51,6 +51,6 @@ namespace AppInstaller::CLI::Workflow // Outputs: DependencySource void OpenDependencySource(Execution::Context& context); - bool graphHasLoop(std::map> dependencyGraph); + bool graphHasLoop(std::map> dependencyGraph, const AppInstaller::Manifest::string_t& root); bool hasLoopDFS(std::set visited, const AppInstaller::Manifest::string_t& nodeId, std::map>& dependencyGraph); } \ No newline at end of file From 6eb87bf8293dce5103bbbe9b0b7ca581f15e90ac Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 12 Jul 2021 15:41:11 -0300 Subject: [PATCH 45/82] const & dep graph --- .../Workflows/DependenciesFlow.cpp | 28 ++++++++----------- .../Workflows/DependenciesFlow.h | 9 ++++-- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index f84a9a9c3e..bab358edd3 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -183,17 +183,6 @@ namespace AppInstaller::CLI::Workflow { toCheck.push_back(dependency); dependencyGraph[dependency.Id] = std::vector(); - } - else - { - // we only need to check for loops if the dependency already existed, right? - // should we have an inverse map? i.e., < id, packages that depend on this one > - // or should we check for loops only once (when we have all deps in the graph) - if (graphHasLoop(dependencyGraph, installer->ProductId)) - { - context.Reporter.Info() << "has loop" << std::endl; - //TODO warn user and raise error - } } }); } @@ -205,34 +194,39 @@ namespace AppInstaller::CLI::Workflow continue; } } + auto order = std::vector(); + if (graphHasLoop(dependencyGraph, installer->ProductId, order)) + { + context.Reporter.Info() << "has loop" << std::endl; + //TODO warn user and raise error + } context.Reporter.Info() << "---" << std::endl; } - // TODO get dependency installation order from dependencyGraph (topological order) // TODO make them iterative // is there a better way that this to check for loops? - bool graphHasLoop(std::map> dependencyGraph, const string_t& root) + bool graphHasLoop(const std::map>& dependencyGraph, const string_t& root, std::vector& order) { auto visited = std::set(); visited.insert(root); - if (hasLoopDFS(visited, root, dependencyGraph)) + if (hasLoopDFS(visited, root, dependencyGraph, order)) { return true; } return false; } - bool hasLoopDFS(std::set visited, const string_t& nodeId, std::map>& dependencyGraph) + bool hasLoopDFS(std::set visited, const string_t& nodeId, const std::map>& dependencyGraph, std::vector& order) { visited.insert(nodeId); - for (const auto& adjacent : dependencyGraph[nodeId]) + for (const auto& adjacent : dependencyGraph.at(nodeId)) { auto search = visited.find(adjacent.Id); if (search == visited.end()) // if not found { - if (hasLoopDFS(visited, adjacent.Id, dependencyGraph)) + if (hasLoopDFS(visited, adjacent.Id, dependencyGraph, order)) { return true; } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 006561eb3c..10c5dd4774 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -51,6 +51,11 @@ namespace AppInstaller::CLI::Workflow // Outputs: DependencySource void OpenDependencySource(Execution::Context& context); - bool graphHasLoop(std::map> dependencyGraph, const AppInstaller::Manifest::string_t& root); - bool hasLoopDFS(std::set visited, const AppInstaller::Manifest::string_t& nodeId, std::map>& dependencyGraph); + bool graphHasLoop(const std::map>& dependencyGraph, + const AppInstaller::Manifest::string_t& root, + std::vector& order); + bool hasLoopDFS(std::set visited, + const AppInstaller::Manifest::string_t& nodeId, + const std::map>& dependencyGraph, + std::vector& order); } \ No newline at end of file From d69b3e3f213bc551f544f0037645d8fbc8573a90 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 12 Jul 2021 16:08:53 -0300 Subject: [PATCH 46/82] with installation order --- .../Workflows/DependenciesFlow.cpp | 20 ++++++++++++++----- src/AppInstallerCLITests/WorkFlow.cpp | 3 +++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index bab358edd3..39948c9752 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -194,15 +194,20 @@ namespace AppInstaller::CLI::Workflow continue; } } - auto order = std::vector(); - if (graphHasLoop(dependencyGraph, installer->ProductId, order)) + auto info = context.Reporter.Info(); + auto installationOrder = std::vector(); + if (graphHasLoop(dependencyGraph, installer->ProductId, installationOrder)) { - context.Reporter.Info() << "has loop" << std::endl; + info << "has loop" << std::endl; //TODO warn user and raise error } - context.Reporter.Info() << "---" << std::endl; - + info << "order: "; + for (auto const& nodeId : installationOrder) + { + info << nodeId << ", "; + } + info << std::endl; } // TODO make them iterative @@ -236,6 +241,11 @@ namespace AppInstaller::CLI::Workflow return true; } } + + if (std::find(order.begin(), order.end(), nodeId) == order.end()) + { + order.push_back(nodeId); + } return false; } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 448975d996..d881d1f7cb 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1805,6 +1805,7 @@ TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGr INFO(installOutput.str()); REQUIRE(installOutput.str().find("has loop") == std::string::npos); + REQUIRE(installOutput.str().find("B, C, F, DependencyAlreadyInStackButNoLoop,") != std::string::npos); } TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph]") @@ -1826,6 +1827,8 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph INFO(installOutput.str()); REQUIRE(installOutput.str().find("has loop") == std::string::npos); + REQUIRE(installOutput.str().find("order: B, C, G, H, PathBetweenBranchesButNoLoop,") != std::string::npos); + } TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") From b67708c591d882a55c93d647468ac51bdbff818f Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 12 Jul 2021 16:13:42 -0300 Subject: [PATCH 47/82] two more tests --- src/AppInstallerCLITests/WorkFlow.cpp | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index d881d1f7cb..d011b8bfef 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1831,6 +1831,52 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph } +TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyGraph]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForDependencySource(context); + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + REQUIRE(installOutput.str().find("has loop") == std::string::npos); + REQUIRE(installOutput.str().find("order: B, C, StackOrderIsOk,") != std::string::npos); + +} + +TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForDependencySource(context); + OverrideForShellExecute(context); + + context.Args.AddArg(Execution::Args::Type::Query, "NeedsToInstallBFirst"sv); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + REQUIRE(installOutput.str().find("has loop") == std::string::npos); + REQUIRE(installOutput.str().find("order: B, C, NeedsToInstallBFirst,") != std::string::npos); + +} + TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") { std::ostringstream validateOutput; From d5f60503e2504c6f10a5139654d55812f6e3b47f Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 12 Jul 2021 17:27:01 -0300 Subject: [PATCH 48/82] change map key to Dependency (missing min version check) --- .../Workflows/DependenciesFlow.cpp | 61 ++++++++++--------- .../Workflows/DependenciesFlow.h | 14 ++--- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 39948c9752..d2137a9a42 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -119,19 +119,20 @@ namespace AppInstaller::CLI::Workflow std::vector toCheck; - // should this dictionary have Dependency as key? (to have min version) in this case Dependency should implement equal - std::map> dependencyGraph; + std::map> dependencyGraph; // < package Id, dependencies > value should be a set instead of a vector? const auto& installer = context.Get(); + Dependency rootDependency = Dependency(DependencyType::Package); if (installer) { - dependencyGraph[installer->ProductId] = std::vector(); // ProductId is the same Id as the one used by Dependencies? + rootDependency.Id = installer->ProductId; // ProductId is the same Id as the one used by Dependencies? + dependencyGraph[rootDependency] = std::vector(); installer->Dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { toCheck.push_back(dependency); - dependencyGraph[dependency.Id] = std::vector(); - dependencyGraph[installer->ProductId].push_back(dependency); + dependencyGraph[dependency] = std::vector(); + dependencyGraph[rootDependency].push_back(dependency); }); } // TODO fail otherwise @@ -140,18 +141,17 @@ namespace AppInstaller::CLI::Workflow for (int i = 0; i < toCheck.size(); ++i) { - const auto& dependencyNode = toCheck.at(i); - auto packageID = dependencyNode.Id; + auto dependencyNode = toCheck.at(i); SearchRequest searchRequest; - searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, packageID)); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependencyNode.Id)); const auto& matches = source->Search(searchRequest).Matches; if (!matches.empty()) { const auto& match = matches.at(0); if (matches.size() > 1) { - failedPackages[packageID] = "Too many matches"; //TODO localize all errors + failedPackages[dependencyNode.Id] = "Too many matches"; //TODO localize all errors continue; } @@ -160,61 +160,64 @@ namespace AppInstaller::CLI::Workflow { const auto& packageVersion = package->GetLatestAvailableVersion(); if (!packageVersion) { - failedPackages[packageID] = "No package version found"; //TODO localize all errors + failedPackages[dependencyNode.Id] = "No package version found"; //TODO localize all errors continue; } const auto& packageVersionManifest = packageVersion->GetManifest(); if (packageVersionManifest.Installers.empty()) { - failedPackages[packageID] = "No installers found"; //TODO localize all errors + failedPackages[dependencyNode.Id] = "No installers found"; //TODO localize all errors continue; } //how to choose best installer, should I use SelectInstaller or not? const auto& matchInstaller = packageVersionManifest.Installers.at(0); const auto& matchDependencies = matchInstaller.Dependencies; + // TODO check dependency min version is <= latest version + // TODO save installers for later maybe? matchDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { - // TODO check dependency min version is <= latest version - dependencyGraph[packageID].push_back(dependency); + dependencyGraph[dependencyNode].push_back(dependency); - auto search = dependencyGraph.find(dependency.Id); + auto search = dependencyGraph.find(dependency); if (search == dependencyGraph.end()) // if not found { toCheck.push_back(dependency); - dependencyGraph[dependency.Id] = std::vector(); + dependencyGraph[dependency] = std::vector(); } }); } // TODO else: save information on dependencies already installed to inform the user? + // TODO check dependency min version is <= installed version (otherwise update? -> should check for new dependencies) + } else { - failedPackages[packageID] = "No matches"; //TODO localize all errors + failedPackages[dependencyNode.Id] = "No matches"; //TODO localize all errors continue; } } auto info = context.Reporter.Info(); - auto installationOrder = std::vector(); - if (graphHasLoop(dependencyGraph, installer->ProductId, installationOrder)) + auto installationOrder = std::vector(); + if (graphHasLoop(dependencyGraph, rootDependency, installationOrder)) { info << "has loop" << std::endl; //TODO warn user and raise error } info << "order: "; - for (auto const& nodeId : installationOrder) + for (auto const& node : installationOrder) { - info << nodeId << ", "; + info << node.Id << ", "; } info << std::endl; } // TODO make them iterative // is there a better way that this to check for loops? - bool graphHasLoop(const std::map>& dependencyGraph, const string_t& root, std::vector& order) + bool graphHasLoop(const std::map>& dependencyGraph, const Dependency& root, std::vector& order) { - auto visited = std::set(); + auto visited = std::set(); visited.insert(root); if (hasLoopDFS(visited, root, dependencyGraph, order)) { @@ -223,15 +226,15 @@ namespace AppInstaller::CLI::Workflow return false; } - bool hasLoopDFS(std::set visited, const string_t& nodeId, const std::map>& dependencyGraph, std::vector& order) + bool hasLoopDFS(std::set visited, const Dependency& node, const std::map>& dependencyGraph, std::vector& order) { - visited.insert(nodeId); - for (const auto& adjacent : dependencyGraph.at(nodeId)) + visited.insert(node); + for (const auto& adjacent : dependencyGraph.at(node)) { - auto search = visited.find(adjacent.Id); + auto search = visited.find(adjacent); if (search == visited.end()) // if not found { - if (hasLoopDFS(visited, adjacent.Id, dependencyGraph, order)) + if (hasLoopDFS(visited, adjacent, dependencyGraph, order)) { return true; } @@ -242,9 +245,9 @@ namespace AppInstaller::CLI::Workflow } } - if (std::find(order.begin(), order.end(), nodeId) == order.end()) + if (std::find(order.begin(), order.end(), node) == order.end()) { - order.push_back(nodeId); + order.push_back(node); } return false; diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 10c5dd4774..abc66f7d0b 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -51,11 +51,11 @@ namespace AppInstaller::CLI::Workflow // Outputs: DependencySource void OpenDependencySource(Execution::Context& context); - bool graphHasLoop(const std::map>& dependencyGraph, - const AppInstaller::Manifest::string_t& root, - std::vector& order); - bool hasLoopDFS(std::set visited, - const AppInstaller::Manifest::string_t& nodeId, - const std::map>& dependencyGraph, - std::vector& order); + bool graphHasLoop(const std::map>& dependencyGraph, + const AppInstaller::Manifest::Dependency& root, + std::vector& order); + bool hasLoopDFS(std::set visited, + const AppInstaller::Manifest::Dependency& node, + const std::map>& dependencyGraph, + std::vector& order); } \ No newline at end of file From c737c8db398a4e4e2512e8cf64d700b8ae35ad63 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 13 Jul 2021 12:38:51 -0300 Subject: [PATCH 49/82] Dependency implements operand< --- src/AppInstallerCommonCore/Public/winget/ManifestCommon.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index a7e8134f80..4b1b6d8708 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -133,6 +133,11 @@ namespace AppInstaller::Manifest bool operator==(const Dependency& rhs) const { return Type == rhs.Type && ICUCaseInsensitiveEquals(Id, rhs.Id); } + + bool operator <(const Dependency& rhs) const + { + return Id < rhs.Id; + } }; struct DependencyList From 087dd8d04dd4c7ce058dcc9096dd23ff3e3440c6 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 13 Jul 2021 14:04:20 -0300 Subject: [PATCH 50/82] Dependency.MinVersion type is AppInstaller::Utility::Version --- .../Workflows/DependenciesFlow.cpp | 12 ++++++------ src/AppInstallerCLICore/Workflows/ShowFlow.cpp | 2 +- src/AppInstallerCLITests/WorkFlow.cpp | 1 - .../Manifest/ManifestYamlPopulator.cpp | 2 +- .../Public/winget/ManifestCommon.h | 12 +++++------- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index d2137a9a42..d8992a9f55 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -41,7 +41,7 @@ namespace AppInstaller::CLI::Workflow dependencies.ApplyToType(DependencyType::Package, [&info](Dependency dependency) { info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value().ToString() << "]"; info << std::endl; }); } @@ -122,13 +122,13 @@ namespace AppInstaller::CLI::Workflow std::map> dependencyGraph; // < package Id, dependencies > value should be a set instead of a vector? - const auto& installer = context.Get(); - Dependency rootDependency = Dependency(DependencyType::Package); - if (installer) + const auto& rootInstaller = context.Get(); + const auto& rootManifest = context.Get(); + Dependency rootDependency = Dependency(DependencyType::Package, rootManifest.Id); + if (rootInstaller) { - rootDependency.Id = installer->ProductId; // ProductId is the same Id as the one used by Dependencies? dependencyGraph[rootDependency] = std::vector(); - installer->Dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + rootInstaller->Dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { toCheck.push_back(dependency); dependencyGraph[dependency] = std::vector(); diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index e57da40893..12e967fa16 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -95,7 +95,7 @@ namespace AppInstaller::CLI::Workflow info << " - PackageDependencies: " << std::endl; dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value().ToString() << "]"; info << std::endl; }); } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index d011b8bfef..0aa6c22c37 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -265,7 +265,6 @@ namespace auto& installer = manifest.Installers.at(0); installer.Dependencies.Clear(); - installer.ProductId = input; /* * Dependencies: diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 58b6c56cdb..79b1d07a3c 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -383,7 +383,7 @@ namespace AppInstaller::Manifest result = { { "PackageIdentifier", [this](const YAML::Node& value)->ValidationErrors { m_p_packageDependency->Id = Utility::Trim(value.as()); return {}; } }, - { "MinimumVersion", [this](const YAML::Node& value)->ValidationErrors { m_p_packageDependency->MinVersion = Utility::Trim(value.as()); return {}; } }, + { "MinimumVersion", [this](const YAML::Node& value)->ValidationErrors { m_p_packageDependency->MinVersion = AppInstaller::Utility::Version(Utility::Trim(value.as())); return {}; } }, }; } diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 4b1b6d8708..b64ac42b2f 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -124,9 +124,9 @@ namespace AppInstaller::Manifest { DependencyType Type; string_t Id; - std::optional MinVersion; + std::optional MinVersion; - Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), Id(std::move(id)), MinVersion(std::move(minVersion)) {} + Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), Id(std::move(id)), MinVersion(AppInstaller::Utility::Version(minVersion)) {} Dependency(DependencyType type, string_t id) : Type(std::move(type)), Id(std::move(id)) {} Dependency(DependencyType type) : Type(type) {} @@ -153,11 +153,9 @@ namespace AppInstaller::Manifest { if (existingDependency->MinVersion) { - const auto& newDependencyVersion = AppInstaller::Utility::Version(newDependency.MinVersion.value()); - const auto& existingDependencyVersion = AppInstaller::Utility::Version(existingDependency->MinVersion.value()); - if (newDependencyVersion > existingDependencyVersion) + if (newDependency.MinVersion.value() > existingDependency->MinVersion.value()) { - existingDependency->MinVersion.value() = newDependencyVersion.ToString(); + existingDependency->MinVersion.value() = newDependency.MinVersion.value(); } } else @@ -209,7 +207,7 @@ namespace AppInstaller::Manifest if (dependency.Type == type && Utility::ICUCaseInsensitiveEquals(dependency.Id, id)) { if (dependency.MinVersion) { - if (dependency.MinVersion.value() == minVersion) + if (dependency.MinVersion.value() == AppInstaller::Utility::Version(minVersion)) { return true; } From 46d9d0b3f27c731b002c3eac9d8bc8eca0eec43a Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 13 Jul 2021 14:34:33 -0300 Subject: [PATCH 51/82] choose installer using ManifestComparator --- .../Workflows/DependenciesFlow.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index d8992a9f55..1100569651 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -168,9 +168,18 @@ namespace AppInstaller::CLI::Workflow failedPackages[dependencyNode.Id] = "No installers found"; //TODO localize all errors continue; } - //how to choose best installer, should I use SelectInstaller or not? - const auto& matchInstaller = packageVersionManifest.Installers.at(0); - const auto& matchDependencies = matchInstaller.Dependencies; + + ManifestComparator manifestComparator(context.Args, packageVersion->GetMetadata()); + const auto& matchInstaller = manifestComparator.GetPreferredInstaller(packageVersionManifest); + + if (!matchInstaller) + { + failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors + continue; + } + + //const auto& matchInstaller = packageVersionManifest.Installers.at(0); + const auto& matchDependencies = matchInstaller.value().Dependencies; // TODO check dependency min version is <= latest version From c82f2340204d3e5bde19be909fa49140d2030f3f Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 14 Jul 2021 16:17:50 -0300 Subject: [PATCH 52/82] getting composite source --- .../Workflows/DependenciesFlow.cpp | 47 ++++++++++++------- .../Workflows/InstallFlow.cpp | 2 +- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 1100569651..40648c2dc4 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -94,39 +94,41 @@ namespace AppInstaller::CLI::Workflow { // two options: have a package version or a manifest // try to get source from package version - const auto& packageVersion = context.Get(); - if (packageVersion) + //source = empty + //if PackageVersion then source = PV source + //else if Source exists then fail // if Source already open then this can't be an install -m or validate command + //else then OpenSource; source = Source + // result = CreateComposite(installed, source) + + if (context.Contains(Execution::Data::PackageVersion)) { - // TODO open composite source: package + installed - // how to execute without progress? - //auto installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); - //auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); - auto compositeSource = packageVersion->GetSource(); + const auto& packageVersion = context.Get(); + // how to execute without progress? should we? + const auto& installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); + auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); context.Add(compositeSource); } else { // TODO question to John: can/should we do nothing for local manifests? or set up something like --dependency-source - //const auto& manifest = context.Get(); + // Open source passed by parameter (from sourcename) + // openCompositeSource should work for getting installed+opened source } } void BuildPackageDependenciesGraph(Execution::Context& context) { - context << OpenDependencySource; - const auto& source = context.Get(); - - std::vector toCheck; - - std::map> dependencyGraph; - // < package Id, dependencies > value should be a set instead of a vector? - - const auto& rootInstaller = context.Get(); const auto& rootManifest = context.Get(); Dependency rootDependency = Dependency(DependencyType::Package, rootManifest.Id); + + std::vector toCheck; + std::map> dependencyGraph; //(?) value should be a set instead of a vector? + const auto& rootInstaller = context.Get(); if (rootInstaller) { + context.Add(rootInstaller->Dependencies); // to use in report + // TODO remove this ^ if we are reporting dependencies somewhere else while installing/managing them dependencyGraph[rootDependency] = std::vector(); rootInstaller->Dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { @@ -137,6 +139,15 @@ namespace AppInstaller::CLI::Workflow }); } // TODO fail otherwise + context << OpenDependencySource; + if (!context.Contains(Execution::Data::DependencySource)) + { + context.Reporter.Info() << "dependency source not found" << std::endl; //TODO localize message + return; //TODO terminate with error once we can get source for dependencies, when a manifest was passed + //AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING); // TODO create a new error code? + } + + const auto& source = context.Get(); std::map failedPackages; for (int i = 0; i < toCheck.size(); ++i) @@ -214,6 +225,8 @@ namespace AppInstaller::CLI::Workflow //TODO warn user and raise error } + // TODO raise error for failedPackages (if there's at least one) + info << "order: "; for (auto const& node : installationOrder) { diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index d4fe70d361..1be6c27851 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -416,7 +416,7 @@ namespace AppInstaller::CLI::Workflow Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::BuildPackageDependenciesGraph << - //Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; } From aebac6b9829a7d306a6f92b7ad0d25880353dc1d Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Wed, 14 Jul 2021 14:36:25 -0700 Subject: [PATCH 53/82] Drop const from the returned type of GetSource --- src/AppInstallerCLITests/TestSource.cpp | 10 ++++---- src/AppInstallerCLITests/TestSource.h | 12 +++++----- src/AppInstallerCLITests/WorkFlow.cpp | 18 +++++++------- .../Microsoft/SQLiteIndexSource.cpp | 24 +++++++++---------- .../Public/AppInstallerRepositorySearch.h | 2 +- .../Rest/RestSource.cpp | 20 ++++++++-------- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index 0a7bfce688..d7f5025513 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -30,10 +30,10 @@ namespace TestCommon } } - TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source) : + TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source) : VersionManifest(manifest), Metadata(std::move(installationMetadata)), Source(source) {} - TestPackageVersion::TestPackageVersion(const Manifest& manifest, std::weak_ptr source) : + TestPackageVersion::TestPackageVersion(const Manifest& manifest, std::weak_ptr source) : VersionManifest(manifest), Source(source) {} TestPackageVersion::LocIndString TestPackageVersion::GetProperty(PackageVersionProperty property) const @@ -96,7 +96,7 @@ namespace TestCommon return VersionManifest; } - std::shared_ptr TestPackageVersion::GetSource() const + std::shared_ptr TestPackageVersion::GetSource() const { return Source.lock(); } @@ -119,7 +119,7 @@ namespace TestCommon } } - TestPackage::TestPackage(const std::vector& available, std::weak_ptr source) + TestPackage::TestPackage(const std::vector& available, std::weak_ptr source) { for (const auto& manifest : available) { @@ -127,7 +127,7 @@ namespace TestCommon } } - TestPackage::TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available, std::weak_ptr source) : + TestPackage::TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available, std::weak_ptr source) : InstalledVersion(TestPackageVersion::Make(installed, std::move(installationMetadata), source)) { for (const auto& manifest : available) diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h index 510e6ac9f1..3f68d4b40c 100644 --- a/src/AppInstallerCLITests/TestSource.h +++ b/src/AppInstallerCLITests/TestSource.h @@ -18,8 +18,8 @@ namespace TestCommon using LocIndString = AppInstaller::Utility::LocIndString; using MetadataMap = AppInstaller::Repository::IPackageVersion::Metadata; - TestPackageVersion(const Manifest& manifest, std::weak_ptr source = {}); - TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source = {}); + TestPackageVersion(const Manifest& manifest, std::weak_ptr source = {}); + TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) @@ -30,12 +30,12 @@ namespace TestCommon LocIndString GetProperty(AppInstaller::Repository::PackageVersionProperty property) const override; std::vector GetMultiProperty(AppInstaller::Repository::PackageVersionMultiProperty property) const override; Manifest GetManifest() override; - std::shared_ptr GetSource() const override; + std::shared_ptr GetSource() const override; MetadataMap GetMetadata() const override; Manifest VersionManifest; MetadataMap Metadata; - std::weak_ptr Source; + std::weak_ptr Source; protected: static void AddFoldedIfHasValueAndNotPresent(const AppInstaller::Utility::NormalizedString& value, std::vector& target); @@ -50,10 +50,10 @@ namespace TestCommon using MetadataMap = TestPackageVersion::MetadataMap; // Create a package with only available versions using these manifests. - TestPackage(const std::vector& available, std::weak_ptr source = {}); + TestPackage(const std::vector& available, std::weak_ptr source = {}); // Create a package with an installed version, metadata, and optionally available versions. - TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available = {}, std::weak_ptr source = {}); + TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available = {}, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 2e99614cf3..0cf394d5a9 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -68,7 +68,7 @@ namespace auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( - TestPackage::Make(std::vector{ manifest }, this->shared_from_this()), + TestPackage::Make(std::vector{ manifest }, const_cast(this)->shared_from_this()), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnOne"))); } else if (input == "TestQueryReturnTwo") @@ -76,13 +76,13 @@ namespace auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( - TestPackage::Make(std::vector{ manifest }, this->shared_from_this()), + TestPackage::Make(std::vector{ manifest }, const_cast(this)->shared_from_this()), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("Manifest-Good.yaml")); result.Matches.emplace_back( ResultMatch( - TestPackage::Make(std::vector{ manifest2 }, this->shared_from_this()), + TestPackage::Make(std::vector{ manifest2 }, const_cast(this)->shared_from_this()), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); } @@ -124,7 +124,7 @@ namespace { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, }, std::vector{ manifest3, manifest2, manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } @@ -139,7 +139,7 @@ namespace manifest, TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller"))); } @@ -153,7 +153,7 @@ namespace manifest, TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "MSStore" } }, std::vector{ manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMSStoreInstaller"))); } @@ -168,7 +168,7 @@ namespace manifest2, TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } @@ -183,7 +183,7 @@ namespace manifest, TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } @@ -195,7 +195,7 @@ namespace ResultMatch( TestPackage::Make( std::vector{ manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index f24b6d2b13..3b80e4eb69 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -16,25 +16,25 @@ namespace AppInstaller::Repository::Microsoft // The base for the package objects. struct SourceReference { - SourceReference(const std::shared_ptr& source) : + SourceReference(const std::shared_ptr& source) : m_source(source) {} protected: - std::shared_ptr GetReferenceSource() const + std::shared_ptr GetReferenceSource() const { - std::shared_ptr source = m_source.lock(); + std::shared_ptr source = m_source.lock(); THROW_HR_IF(E_NOT_VALID_STATE, !source); return source; } private: - std::weak_ptr m_source; + std::weak_ptr m_source; }; // The IPackageVersion impl for SQLiteIndexSource. struct PackageVersion : public SourceReference, public IPackageVersion { - PackageVersion(const std::shared_ptr& source, SQLiteIndex::IdType manifestId) : + PackageVersion(const std::shared_ptr& source, SQLiteIndex::IdType manifestId) : SourceReference(source), m_manifestId(manifestId) {} // Inherited via IPackageVersion @@ -67,7 +67,7 @@ namespace AppInstaller::Repository::Microsoft Manifest::Manifest GetManifest() override { - std::shared_ptr source = GetReferenceSource(); + std::shared_ptr source = GetReferenceSource(); std::optional relativePathOpt = source->GetIndex().GetPropertyByManifestId(m_manifestId, PackageVersionProperty::RelativePath); THROW_HR_IF(E_NOT_SET, !relativePathOpt); @@ -82,7 +82,7 @@ namespace AppInstaller::Repository::Microsoft return GetManifestFromArgAndRelativePath(source->GetDetails().Arg, relativePathOpt.value(), manifestSHA256); } - std::shared_ptr GetSource() const override + std::shared_ptr GetSource() const override { return GetReferenceSource(); } @@ -178,7 +178,7 @@ namespace AppInstaller::Repository::Microsoft // The base for IPackage implementations here. struct PackageBase : public SourceReference { - PackageBase(const std::shared_ptr& source, SQLiteIndex::IdType idId) : + PackageBase(const std::shared_ptr& source, SQLiteIndex::IdType idId) : SourceReference(source), m_idId(idId) {} Utility::LocIndString GetProperty(PackageProperty property) const @@ -210,7 +210,7 @@ namespace AppInstaller::Repository::Microsoft protected: std::shared_ptr GetLatestVersionInternal() const { - std::shared_ptr source = GetReferenceSource(); + std::shared_ptr source = GetReferenceSource(); std::optional manifestId = source->GetIndex().GetManifestIdByKey(m_idId, {}, {}); if (manifestId) @@ -242,7 +242,7 @@ namespace AppInstaller::Repository::Microsoft std::vector GetAvailableVersionKeys() const override { - std::shared_ptr source = GetReferenceSource(); + std::shared_ptr source = GetReferenceSource(); std::vector versions = source->GetIndex().GetVersionKeysById(m_idId); std::vector result; @@ -260,7 +260,7 @@ namespace AppInstaller::Repository::Microsoft std::shared_ptr GetAvailableVersion(const PackageVersionKey& versionKey) const override { - std::shared_ptr source = GetReferenceSource(); + std::shared_ptr source = GetReferenceSource(); // Ensure that this key targets this (or any) source if (!versionKey.SourceId.empty() && versionKey.SourceId != source->GetIdentifier()) @@ -367,7 +367,7 @@ namespace AppInstaller::Repository::Microsoft auto indexResults = m_index.Search(request); SearchResult result; - std::shared_ptr sharedThis = shared_from_this(); + std::shared_ptr sharedThis = const_cast(this)->shared_from_this(); for (auto& indexResult : indexResults.Matches) { std::unique_ptr package; diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h index d61aa244e7..59337cec76 100644 --- a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h @@ -180,7 +180,7 @@ namespace AppInstaller::Repository virtual Manifest::Manifest GetManifest() = 0; // Gets the source where this package version is from. - virtual std::shared_ptr GetSource() const = 0; + virtual std::shared_ptr GetSource() const = 0; // Gets any metadata associated with this package version. // Primarily stores data on installed packages. diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index e4d6b45eea..ac94cf1b40 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -14,26 +14,26 @@ namespace AppInstaller::Repository::Rest // The source reference used by package objects. struct SourceReference { - SourceReference(const std::shared_ptr& source) : + SourceReference(const std::shared_ptr& source) : m_source(source) {} protected: - std::shared_ptr GetReferenceSource() const + std::shared_ptr GetReferenceSource() const { - std::shared_ptr source = m_source.lock(); + std::shared_ptr source = m_source.lock(); THROW_HR_IF(E_NOT_VALID_STATE, !source); return source; } private: - std::weak_ptr m_source; + std::weak_ptr m_source; }; // The IPackageVersion impl for RestSource. struct PackageVersion : public SourceReference, public IPackageVersion { PackageVersion( - const std::shared_ptr& source, IRestClient::PackageInfo packageInfo, IRestClient::VersionInfo versionInfo) + const std::shared_ptr& source, IRestClient::PackageInfo packageInfo, IRestClient::VersionInfo versionInfo) : SourceReference(source), m_packageInfo(std::move(packageInfo)), m_versionInfo(std::move(versionInfo)) {} // Inherited via IPackageVersion @@ -132,7 +132,7 @@ namespace AppInstaller::Repository::Rest return m_versionInfo.Manifest.value(); } - std::shared_ptr GetSource() const override + std::shared_ptr GetSource() const override { return GetReferenceSource(); } @@ -169,7 +169,7 @@ namespace AppInstaller::Repository::Rest // The base for IPackage implementations here. struct PackageBase : public SourceReference { - PackageBase(const std::shared_ptr& source, IRestClient::Package&& package) : + PackageBase(const std::shared_ptr& source, IRestClient::Package&& package) : SourceReference(source), m_package(std::move(package)) { // Sort the versions @@ -221,7 +221,7 @@ namespace AppInstaller::Repository::Rest std::vector GetAvailableVersionKeys() const override { - std::shared_ptr source = GetReferenceSource(); + std::shared_ptr source = GetReferenceSource(); std::vector result; for (const auto& versionInfo : m_package.Versions) @@ -240,7 +240,7 @@ namespace AppInstaller::Repository::Rest std::shared_ptr GetAvailableVersion(const PackageVersionKey& versionKey) const override { - std::shared_ptr source = GetReferenceSource(); + std::shared_ptr source = GetReferenceSource(); // Ensure that this key targets this (or any) source if (!versionKey.SourceId.empty() && versionKey.SourceId != source->GetIdentifier()) @@ -337,7 +337,7 @@ namespace AppInstaller::Repository::Rest RestClient::SearchResult results = m_restClient.Search(request); SearchResult searchResult; - std::shared_ptr sharedThis = shared_from_this(); + std::shared_ptr sharedThis = const_cast(this)->shared_from_this(); for (auto& result : results.Matches) { std::unique_ptr package = std::make_unique(sharedThis, std::move(result)); From 3bac9ab99a834956ac509a4d9f4055971328d286 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 14 Jul 2021 20:19:12 -0300 Subject: [PATCH 54/82] add check for min version --- .../Workflows/DependenciesFlow.cpp | 33 +++++++++++-------- .../Workflows/WorkflowBase.cpp | 9 +++-- .../Workflows/WorkflowBase.h | 1 + .../Public/winget/ManifestCommon.h | 7 +++- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 40648c2dc4..9959e6f977 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -104,9 +104,10 @@ namespace AppInstaller::CLI::Workflow if (context.Contains(Execution::Data::PackageVersion)) { const auto& packageVersion = context.Get(); - // how to execute without progress? should we? - const auto& installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); - auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); + //// how to execute without progress? should we? + //const auto& installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); + //auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); + auto compositeSource = packageVersion->GetSource(); context.Add(compositeSource); } else @@ -120,7 +121,7 @@ namespace AppInstaller::CLI::Workflow void BuildPackageDependenciesGraph(Execution::Context& context) { const auto& rootManifest = context.Get(); - Dependency rootDependency = Dependency(DependencyType::Package, rootManifest.Id); + Dependency rootDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); std::vector toCheck; std::map> dependencyGraph; //(?) value should be a set instead of a vector? @@ -149,6 +150,7 @@ namespace AppInstaller::CLI::Workflow const auto& source = context.Get(); std::map failedPackages; + std::vector alreadyInstalled; for (int i = 0; i < toCheck.size(); ++i) { @@ -167,33 +169,39 @@ namespace AppInstaller::CLI::Workflow } const auto& package = match.Package; - if (!package->GetInstalledVersion()) + if (package->GetInstalledVersion() && dependencyNode.IsVersionOk(package->GetInstalledVersion()->GetManifest().Version)) + { + alreadyInstalled.push_back(dependencyNode); + } + else { const auto& packageVersion = package->GetLatestAvailableVersion(); if (!packageVersion) { failedPackages[dependencyNode.Id] = "No package version found"; //TODO localize all errors continue; } + const auto& packageVersionManifest = packageVersion->GetManifest(); if (packageVersionManifest.Installers.empty()) { failedPackages[dependencyNode.Id] = "No installers found"; //TODO localize all errors continue; } - - ManifestComparator manifestComparator(context.Args, packageVersion->GetMetadata()); - const auto& matchInstaller = manifestComparator.GetPreferredInstaller(packageVersionManifest); + if (!dependencyNode.IsVersionOk(packageVersionManifest.Version)) + { + failedPackages[dependencyNode.Id] = "Minimum required version not available"; //TODO localize all errors + continue; + } + + const auto& matchInstaller = SelectInstallerFromMetadata(context, packageVersion->GetMetadata()); if (!matchInstaller) { failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors continue; } - //const auto& matchInstaller = packageVersionManifest.Installers.at(0); const auto& matchDependencies = matchInstaller.value().Dependencies; - // TODO check dependency min version is <= latest version - // TODO save installers for later maybe? matchDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { @@ -207,9 +215,6 @@ namespace AppInstaller::CLI::Workflow } }); } - // TODO else: save information on dependencies already installed to inform the user? - // TODO check dependency min version is <= installed version (otherwise update? -> should check for new dependencies) - } else { diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 5c45b865d7..f0e9ef3a86 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -675,8 +675,13 @@ namespace AppInstaller::CLI::Workflow installationMetadata = context.Get()->GetMetadata(); } - ManifestComparator manifestComparator(context.Args, installationMetadata); - context.Add(manifestComparator.GetPreferredInstaller(context.Get())); + context.Add(SelectInstallerFromMetadata(context, installationMetadata)); + } + + std::optional SelectInstallerFromMetadata(Execution::Context& context, IPackageVersion::Metadata metadata) + { + ManifestComparator manifestComparator(context.Args, metadata); + return manifestComparator.GetPreferredInstaller(context.Get()); } void EnsureRunningAsAdmin(Execution::Context& context) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index 1f47e489a6..875f045368 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -288,6 +288,7 @@ namespace AppInstaller::CLI::Workflow // Inputs: Manifest // Outputs: Installer void SelectInstaller(Execution::Context& context); + std::optional SelectInstallerFromMetadata(Execution::Context& context, AppInstaller::Repository::IPackageVersion::Metadata metadata); // Ensures that the process is running as admin. // Required Args: None diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index b64ac42b2f..eb0a1a9cc9 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -40,7 +40,7 @@ namespace AppInstaller::Manifest bool HasExtension() const; - bool HasExtension(std::string_view extension) const; +bool HasExtension(std::string_view extension) const; private: std::vector m_extensions; @@ -138,6 +138,11 @@ namespace AppInstaller::Manifest { return Id < rhs.Id; } + + bool IsVersionOk(string_t version) + { + return MinVersion <= AppInstaller::Utility::Version(version); + } }; struct DependencyList From da52d8152f8b065bef8e01f7813acfaab2ce2e8d Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 14 Jul 2021 21:07:28 -0300 Subject: [PATCH 55/82] spelling --- .../Workflows/DependenciesFlow.cpp | 24 ++++++++++++------- src/AppInstallerCLITests/WorkFlow.cpp | 4 ++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 9959e6f977..5ff26c0ba0 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -104,22 +104,24 @@ namespace AppInstaller::CLI::Workflow if (context.Contains(Execution::Data::PackageVersion)) { const auto& packageVersion = context.Get(); - //// how to execute without progress? should we? - //const auto& installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); - //auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); - auto compositeSource = packageVersion->GetSource(); + // how to execute without progress? should we? + const auto& installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); + auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); context.Add(compositeSource); } else { - // TODO question to John: can/should we do nothing for local manifests? or set up something like --dependency-source - // Open source passed by parameter (from sourcename) - // openCompositeSource should work for getting installed+opened source + // TODO set up something like --dependency-source, open source passed by parameter (from source name) + context << + Workflow::OpenSource << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); + context.Add(context.Get()); } } void BuildPackageDependenciesGraph(Execution::Context& context) { + auto info = context.Reporter.Info(); const auto& rootManifest = context.Get(); Dependency rootDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); @@ -138,7 +140,12 @@ namespace AppInstaller::CLI::Workflow dependencyGraph[rootDependency].push_back(dependency); }); - } // TODO fail otherwise + } + else + { + info << "no intaller found" << std::endl; + //TODO warn user and raise error, this should not happen as the workflow should fail before reaching this. + } context << OpenDependencySource; if (!context.Contains(Execution::Data::DependencySource)) @@ -222,7 +229,6 @@ namespace AppInstaller::CLI::Workflow continue; } } - auto info = context.Reporter.Info(); auto installationOrder = std::vector(); if (graphHasLoop(dependencyGraph, rootDependency, installationOrder)) { diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 9fac81ddce..fb31cfd983 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -261,7 +261,7 @@ namespace auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_Dependencies.yaml")); manifest.Id = input; manifest.Moniker = input; - // TODO maybe change package name on default locale for better debbugging + // TODO maybe change package name on default locale for better debugging auto& installer = manifest.Installers.at(0); installer.Dependencies.Clear(); @@ -269,7 +269,7 @@ namespace /* * Dependencies: * "A": Depends on the test - * B: NoDeph + * B: NoDependency * C: B * D: E * E: D From 0a8f06938c324a7bf9619b5e6f5aa6005adb143d Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 15 Jul 2021 15:47:54 -0300 Subject: [PATCH 56/82] add dependencies source --- src/AppInstallerCLICore/Commands/InstallCommand.cpp | 1 + src/AppInstallerCLICore/ExecutionArgs.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 565ea1a365..2b9ef484a2 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -39,6 +39,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::Override), Argument::ForType(Args::Type::InstallLocation), Argument::ForType(Args::Type::HashOverride), + Argument::ForType(Args::Type::DependenciesSource), }; } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index 6e483fb236..fb79336948 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -76,6 +76,7 @@ namespace AppInstaller::CLI::Execution Help, // Show command usage Info, // Show general info about WinGet VerboseLogs, // Increases winget logging level to verbose + DependenciesSource, // Index source to be queried against for finding dependencies // Used for demonstration purposes ExperimentalArg, From d70c55d3f691d3fd662164bb6942e7eda36e0731 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 15 Jul 2021 17:04:39 -0300 Subject: [PATCH 57/82] new parameter dependency source, open source can set up both: source and dependency source --- .../Commands/ExportCommand.cpp | 2 +- .../Commands/ListCommand.cpp | 4 +- .../Commands/SearchCommand.cpp | 4 +- .../Commands/ShowCommand.cpp | 2 +- .../Commands/UninstallCommand.cpp | 4 +- .../Commands/UpgradeCommand.cpp | 4 +- .../ExecutionContextData.h | 2 +- .../Workflows/CompletionFlow.cpp | 2 +- .../Workflows/DependenciesFlow.cpp | 35 ++++------ .../Workflows/WorkflowBase.cpp | 68 ++++++++++++++++--- .../Workflows/WorkflowBase.h | 16 ++++- 11 files changed, 98 insertions(+), 45 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/ExportCommand.cpp b/src/AppInstallerCLICore/Commands/ExportCommand.cpp index 15d0923281..b3e6ff7806 100644 --- a/src/AppInstallerCLICore/Commands/ExportCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ExportCommand.cpp @@ -55,7 +55,7 @@ namespace AppInstaller::CLI { context << Workflow::ReportExecutionStage(Workflow::ExecutionStage::Discovery) << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed) << Workflow::SearchSourceForMany << Workflow::EnsureMatchesFromSearchResult(true) << diff --git a/src/AppInstallerCLICore/Commands/ListCommand.cpp b/src/AppInstallerCLICore/Commands/ListCommand.cpp index 287d75934d..0650a8d869 100644 --- a/src/AppInstallerCLICore/Commands/ListCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ListCommand.cpp @@ -38,7 +38,7 @@ namespace AppInstaller::CLI void ListCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { context << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) @@ -69,7 +69,7 @@ namespace AppInstaller::CLI void ListCommand::ExecuteInternal(Execution::Context& context) const { context << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed) << Workflow::SearchSourceForMany << Workflow::EnsureMatchesFromSearchResult(true) << diff --git a/src/AppInstallerCLICore/Commands/SearchCommand.cpp b/src/AppInstallerCLICore/Commands/SearchCommand.cpp index a20c6ec5e0..e688638d65 100644 --- a/src/AppInstallerCLICore/Commands/SearchCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SearchCommand.cpp @@ -42,7 +42,7 @@ namespace AppInstaller::CLI { case Execution::Args::Type::Query: context << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForManyCompletion << Workflow::CompleteWithMatchedField; @@ -67,7 +67,7 @@ namespace AppInstaller::CLI void SearchCommand::ExecuteInternal(Context& context) const { context << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::SearchSourceForMany << Workflow::EnsureMatchesFromSearchResult(false) << Workflow::ReportSearchResult; diff --git a/src/AppInstallerCLICore/Commands/ShowCommand.cpp b/src/AppInstallerCLICore/Commands/ShowCommand.cpp index b6aa015b54..e0d9a900d5 100644 --- a/src/AppInstallerCLICore/Commands/ShowCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ShowCommand.cpp @@ -61,7 +61,7 @@ namespace AppInstaller::CLI else { context << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::SearchSourceForSingle << Workflow::EnsureOneMatchFromSearchResult(false) << Workflow::ReportPackageIdentity << diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index 2f9fd4f925..fc7cb2bf44 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -54,7 +54,7 @@ namespace AppInstaller::CLI } context << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) @@ -104,7 +104,7 @@ namespace AppInstaller::CLI // open the sources where to search for the package context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); // find the uninstaller diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index 7e1b066995..eb281e4b4f 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -68,7 +68,7 @@ namespace AppInstaller::CLI } context << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) @@ -120,7 +120,7 @@ namespace AppInstaller::CLI context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << - Workflow::OpenSource << + Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); if (ShouldListUpgrade(context)) diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index 414cb47699..f1c0e63ad1 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -196,7 +196,7 @@ namespace AppInstaller::CLI::Execution template <> struct DataMapping { - using value_t = std::shared_ptr; + using value_t = std::shared_ptr; }; } } diff --git a/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp b/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp index 747960c010..a1e9cf8db2 100644 --- a/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp @@ -115,7 +115,7 @@ namespace AppInstaller::CLI::Workflow case Execution::Args::Type::Version: case Execution::Args::Type::Channel: context << - Workflow::OpenSource; + Workflow::OpenSource(); break; } diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 5ff26c0ba0..8dc031d6f1 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -92,30 +92,18 @@ namespace AppInstaller::CLI::Workflow void OpenDependencySource(Execution::Context& context) { - // two options: have a package version or a manifest - // try to get source from package version - - //source = empty - //if PackageVersion then source = PV source - //else if Source exists then fail // if Source already open then this can't be an install -m or validate command - //else then OpenSource; source = Source - // result = CreateComposite(installed, source) - if (context.Contains(Execution::Data::PackageVersion)) { const auto& packageVersion = context.Get(); - // how to execute without progress? should we? - const auto& installedSource = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenPredefinedSource, PredefinedSource::Installed, std::placeholders::_1), true); - auto compositeSource = Repository::CreateCompositeSource(installedSource, packageVersion->GetSource()); - context.Add(compositeSource); + context.Add(packageVersion->GetSource()); + context << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); } else - { - // TODO set up something like --dependency-source, open source passed by parameter (from source name) + { // install from manifest requires --dependency-source to be set context << - Workflow::OpenSource << - Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); - context.Add(context.Get()); + Workflow::OpenSource(true) << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); } } @@ -143,8 +131,15 @@ namespace AppInstaller::CLI::Workflow } else { - info << "no intaller found" << std::endl; - //TODO warn user and raise error, this should not happen as the workflow should fail before reaching this. + info << "no installer found" << std::endl; + //TODO warn user and raise error, this should not happen as the workflow should fail before reaching here. + } + + if (toCheck.empty()) + { + // nothing to do, there's no need to set up dependency source either. + // TODO add information to the logger + return; } context << OpenDependencySource; diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index f0e9ef3a86..eb26d0b519 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -141,12 +141,22 @@ namespace AppInstaller::CLI::Workflow m_func(context); } - void OpenSource(Execution::Context& context) + void OpenSource::operator()(Execution::Context& context) const { std::string_view sourceName; - if (context.Args.Contains(Execution::Args::Type::Source)) + if (m_forDependencies) { - sourceName = context.Args.GetArg(Execution::Args::Type::Source); + if (m_forDependencies && context.Args.Contains(Execution::Args::Type::DependenciesSource)) + { + sourceName = context.Args.GetArg(Execution::Args::Type::DependenciesSource); + } + } + else + { + if (context.Args.Contains(Execution::Args::Type::Source)) + { + sourceName = context.Args.GetArg(Execution::Args::Type::Source); + } } auto source = OpenNamedSource(context, sourceName); @@ -155,7 +165,14 @@ namespace AppInstaller::CLI::Workflow return; } - context.Add(std::move(source)); + if (m_forDependencies) + { + context.Add(std::move(source)); + } + else + { + context.Add(std::move(source)); + } } void OpenNamedSourceForSources::operator()(Execution::Context& context) const @@ -192,22 +209,53 @@ namespace AppInstaller::CLI::Workflow // A well known predefined source should return a value. THROW_HR_IF(E_UNEXPECTED, !source); - context.Add(std::move(source)); + if (m_forDependencies) + { + context.Add(std::move(source)); + } + else + { + context.Add(std::move(source)); + } } void OpenCompositeSource::operator()(Execution::Context& context) const { // Get the already open source for use as the available. - std::shared_ptr availableSource = context.Get(); + std::shared_ptr availableSource; + if (m_forDependencies) + { + availableSource = context.Get(); + } + else + { + availableSource = context.Get(); + } // Open the predefined source. - context << OpenPredefinedSource(m_predefinedSource); + context << OpenPredefinedSource(m_predefinedSource, m_forDependencies); // Create the composite source from the two. - std::shared_ptr compositeSource = Repository::CreateCompositeSource(context.Get(), availableSource); + std::shared_ptr source; + if (m_forDependencies) + { + source = context.Get(); + } + else + { + source = context.Get(); + } + std::shared_ptr compositeSource = Repository::CreateCompositeSource(source, availableSource); // Overwrite the source with the composite. - context.Add(std::move(compositeSource)); + if (m_forDependencies) + { + context.Add(std::move(compositeSource)); + } + else + { + context.Add(std::move(compositeSource)); + } } void SearchSourceForMany(Execution::Context& context) @@ -658,7 +706,7 @@ namespace AppInstaller::CLI::Workflow else { context << - OpenSource << + OpenSource() << SearchSourceForSingle << EnsureOneMatchFromSearchResult(false) << GetManifestFromPackage; diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index 875f045368..b12c0910d9 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -60,7 +60,15 @@ namespace AppInstaller::CLI::Workflow // Required Args: None // Inputs: None // Outputs: Source - void OpenSource(Execution::Context& context); + struct OpenSource : public WorkflowTask + { + OpenSource(bool forDependencies = false) : WorkflowTask("OpenSource"), m_forDependencies(forDependencies) {} + + void operator()(Execution::Context& context) const override; + + private: + bool m_forDependencies; + }; // Creates a source object for a source specified by name, and adds it to the list of open sources. // Required Args: None @@ -82,12 +90,13 @@ namespace AppInstaller::CLI::Workflow // Outputs: Source struct OpenPredefinedSource : public WorkflowTask { - OpenPredefinedSource(Repository::PredefinedSource source) : WorkflowTask("OpenPredefinedSource"), m_predefinedSource(source) {} + OpenPredefinedSource(Repository::PredefinedSource source, bool forDependencies = false) : WorkflowTask("OpenPredefinedSource"), m_predefinedSource(source), m_forDependencies(forDependencies) {} void operator()(Execution::Context& context) const override; private: Repository::PredefinedSource m_predefinedSource; + bool m_forDependencies; }; // Creates a composite source from the given predefined source and the existing source. @@ -96,12 +105,13 @@ namespace AppInstaller::CLI::Workflow // Outputs: Source struct OpenCompositeSource : public WorkflowTask { - OpenCompositeSource(Repository::PredefinedSource source) : WorkflowTask("OpenCompositeSource"), m_predefinedSource(source) {} + OpenCompositeSource(Repository::PredefinedSource source, bool forDependencies = false) : WorkflowTask("OpenCompositeSource"), m_predefinedSource(source), m_forDependencies(forDependencies) {} void operator()(Execution::Context& context) const override; private: Repository::PredefinedSource m_predefinedSource; + bool m_forDependencies; }; // Performs a search on the source. From 2e6f29f3a16135cff27edf87d4fbbc7b37142247 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 15 Jul 2021 17:07:51 -0300 Subject: [PATCH 58/82] spelling --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index b93ce7e393..773c58701d 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -32,6 +32,7 @@ badbit Baz Beigi bfd +BFirst bght bitmask bkup From 9d39e48d996e8a9113cd6ccef78c04186f17e5c8 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 16 Jul 2021 15:09:32 -0300 Subject: [PATCH 59/82] DependencyGraph struct, manages only node/adjacent addition, loop check --- .../Workflows/DependenciesFlow.cpp | 82 ++++------------- .../Workflows/DependenciesFlow.h | 87 +++++++++++++++++-- 2 files changed, 96 insertions(+), 73 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 8dc031d6f1..70c5fdcd7d 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -114,19 +114,17 @@ namespace AppInstaller::CLI::Workflow Dependency rootDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); std::vector toCheck; - std::map> dependencyGraph; //(?) value should be a set instead of a vector? + DependencyGraph dependencyGraph(rootDependency); //(?) value should be a set instead of a vector? const auto& rootInstaller = context.Get(); if (rootInstaller) { context.Add(rootInstaller->Dependencies); // to use in report // TODO remove this ^ if we are reporting dependencies somewhere else while installing/managing them - dependencyGraph[rootDependency] = std::vector(); rootInstaller->Dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { toCheck.push_back(dependency); - dependencyGraph[dependency] = std::vector(); - dependencyGraph[rootDependency].push_back(dependency); - + dependencyGraph.AddNode(dependency); + dependencyGraph.AddAdjacent(rootDependency, dependency); }); } else @@ -145,9 +143,8 @@ namespace AppInstaller::CLI::Workflow context << OpenDependencySource; if (!context.Contains(Execution::Data::DependencySource)) { - context.Reporter.Info() << "dependency source not found" << std::endl; //TODO localize message - return; //TODO terminate with error once we can get source for dependencies, when a manifest was passed - //AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING); // TODO create a new error code? + info << "dependency source not found" << std::endl; //TODO localize message + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING); // TODO create a new error code? } const auto& source = context.Get(); @@ -177,25 +174,25 @@ namespace AppInstaller::CLI::Workflow } else { - const auto& packageVersion = package->GetLatestAvailableVersion(); - if (!packageVersion) { + const auto& packageLatestVersion = package->GetLatestAvailableVersion(); + if (!packageLatestVersion) { failedPackages[dependencyNode.Id] = "No package version found"; //TODO localize all errors continue; } - const auto& packageVersionManifest = packageVersion->GetManifest(); - if (packageVersionManifest.Installers.empty()) { + const auto& packageLatestVersionManifest = packageLatestVersion->GetManifest(); + if (packageLatestVersionManifest.Installers.empty()) { failedPackages[dependencyNode.Id] = "No installers found"; //TODO localize all errors continue; } - if (!dependencyNode.IsVersionOk(packageVersionManifest.Version)) + if (!dependencyNode.IsVersionOk(packageLatestVersionManifest.Version)) { failedPackages[dependencyNode.Id] = "Minimum required version not available"; //TODO localize all errors continue; } - const auto& matchInstaller = SelectInstallerFromMetadata(context, packageVersion->GetMetadata()); + const auto& matchInstaller = SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()); if (!matchInstaller) { failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors @@ -207,13 +204,12 @@ namespace AppInstaller::CLI::Workflow // TODO save installers for later maybe? matchDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { - dependencyGraph[dependencyNode].push_back(dependency); + dependencyGraph.AddAdjacent(dependencyNode, dependency); - auto search = dependencyGraph.find(dependency); - if (search == dependencyGraph.end()) // if not found + if (!dependencyGraph.HasNode(dependency)) { toCheck.push_back(dependency); - dependencyGraph[dependency] = std::vector(); + dependencyGraph.AddNode(dependency); } }); } @@ -224,8 +220,7 @@ namespace AppInstaller::CLI::Workflow continue; } } - auto installationOrder = std::vector(); - if (graphHasLoop(dependencyGraph, rootDependency, installationOrder)) + if (dependencyGraph.HasLoop()) { info << "has loop" << std::endl; //TODO warn user and raise error @@ -233,51 +228,6 @@ namespace AppInstaller::CLI::Workflow // TODO raise error for failedPackages (if there's at least one) - info << "order: "; - for (auto const& node : installationOrder) - { - info << node.Id << ", "; - } - info << std::endl; - } - - // TODO make them iterative - // is there a better way that this to check for loops? - bool graphHasLoop(const std::map>& dependencyGraph, const Dependency& root, std::vector& order) - { - auto visited = std::set(); - visited.insert(root); - if (hasLoopDFS(visited, root, dependencyGraph, order)) - { - return true; - } - return false; - } - - bool hasLoopDFS(std::set visited, const Dependency& node, const std::map>& dependencyGraph, std::vector& order) - { - visited.insert(node); - for (const auto& adjacent : dependencyGraph.at(node)) - { - auto search = visited.find(adjacent); - if (search == visited.end()) // if not found - { - if (hasLoopDFS(visited, adjacent, dependencyGraph, order)) - { - return true; - } - } - else - { - return true; - } - } - - if (std::find(order.begin(), order.end(), node) == order.end()) - { - order.push_back(node); - } - - return false; + dependencyGraph.PrintOrder(info); } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index abc66f7d0b..1a0c4137a4 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -51,11 +51,84 @@ namespace AppInstaller::CLI::Workflow // Outputs: DependencySource void OpenDependencySource(Execution::Context& context); - bool graphHasLoop(const std::map>& dependencyGraph, - const AppInstaller::Manifest::Dependency& root, - std::vector& order); - bool hasLoopDFS(std::set visited, - const AppInstaller::Manifest::Dependency& node, - const std::map>& dependencyGraph, - std::vector& order); + struct DependencyGraph + { + DependencyGraph(AppInstaller::Manifest::Dependency root) : m_root(root) + { + adjacents[m_root] = std::vector(); + + } + + void AddNode(AppInstaller::Manifest::Dependency node) + { + adjacents[node] = std::vector(); + + } + + void AddAdjacent(AppInstaller::Manifest::Dependency node, AppInstaller::Manifest::Dependency adjacent) + { + adjacents[node].push_back(adjacent); + } + + bool HasNode(AppInstaller::Manifest::Dependency dependency) + { + auto search = adjacents.find(dependency); + return search == adjacents.end(); + } + + // TODO make HasLoop and HasLoopDFS iterative + bool HasLoop() + { + auto visited = std::set(); + visited.insert(m_root); + if (HasLoopDFS(visited, m_root)) + { + return true; + } + return false; + } + + //-- only for debugging + void PrintOrder(AppInstaller::CLI::Execution::OutputStream info) + { + info << "order: "; + for (auto const& node : installationOrder) + { + info << node.Id << ", "; + } + info << std::endl; + } + + private: + bool HasLoopDFS(std::set visited, const AppInstaller::Manifest::Dependency& node) + { + visited.insert(node); + for (const auto& adjacent : adjacents.at(node)) + { + auto search = visited.find(adjacent); + if (search == visited.end()) // if not found + { + if (HasLoopDFS(visited, adjacent)) + { + return true; + } + } + else + { + return true; + } + } + + if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) + { + installationOrder.push_back(node); + } + + return false; + } + + AppInstaller::Manifest::Dependency m_root; + std::map> adjacents; + std::vector installationOrder; + }; } \ No newline at end of file From 00489c6f0d345a59d4a66a3ae1d64e2feb0daa49 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 16 Jul 2021 15:09:49 -0300 Subject: [PATCH 60/82] OpenSource() missing --- src/AppInstallerCLITests/WorkFlow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index fb31cfd983..1c1dbcb9f3 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -434,7 +434,7 @@ namespace void OverrideForOpenSource(TestContext& context) { - context.Override({ Workflow::OpenSource, [](TestContext& context) + context.Override({ Workflow::OpenSource(), [](TestContext& context) { context.Add(std::make_shared()); } }); @@ -442,7 +442,7 @@ void OverrideForOpenSource(TestContext& context) void OverrideForCompositeInstalledSource(TestContext& context) { - context.Override({ Workflow::OpenSource, [](TestContext&) + context.Override({ Workflow::OpenSource(), [](TestContext&) { } }); @@ -467,7 +467,7 @@ void OverrideForImportSource(TestContext& context) void OverrideForDependencySource(TestContext& context) { - context.Override({ Workflow::OpenSource, [](TestContext& context) + context.Override({ Workflow::OpenSource(), [](TestContext& context) { context.Add(std::make_shared()); } }); From 572c2bf2a5b361a0bd4e70c6b6a0963bfad0afb7 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 16 Jul 2021 15:13:55 -0300 Subject: [PATCH 61/82] add adjacents to spellchecker --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 773c58701d..1544ff27b2 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -1,4 +1,5 @@ abcd +adjacents adml admx affle From 70978eebf75fe5b88487bb5adcd74a642a7cf4c3 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 19 Jul 2021 15:52:45 -0300 Subject: [PATCH 62/82] graph logic is ok, installer selection and test source settings needs to be fixed --- .../Workflows/DependenciesFlow.cpp | 28 +++++++++++-------- .../Workflows/DependenciesFlow.h | 6 ++-- src/AppInstallerCLITests/WorkFlow.cpp | 14 +++++----- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 70c5fdcd7d..23f1966039 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -96,8 +96,8 @@ namespace AppInstaller::CLI::Workflow { const auto& packageVersion = context.Get(); context.Add(packageVersion->GetSource()); - context << - Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); + /*context << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true);*/ } else { // install from manifest requires --dependency-source to be set @@ -192,14 +192,18 @@ namespace AppInstaller::CLI::Workflow continue; } - const auto& matchInstaller = SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()); - if (!matchInstaller) - { - failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors - continue; - } - - const auto& matchDependencies = matchInstaller.value().Dependencies; + // TODO FIX THIS, have a better way to pick installer (other than the first one) + // the problem is SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()) uses context data so it ends up returning + // the installer for the root package being installed. + //const auto& matchInstaller = SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()); + //if (!matchInstaller) + //{ + // failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors + // continue; + //} + + //const auto& matchDependencies = matchInstaller.value().Dependencies; + const auto& matchDependencies = packageLatestVersionManifest.Installers.at(0).Dependencies; // TODO save installers for later maybe? matchDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) @@ -223,7 +227,9 @@ namespace AppInstaller::CLI::Workflow if (dependencyGraph.HasLoop()) { info << "has loop" << std::endl; - //TODO warn user and raise error + Logging::Log().Write(Logging::Channel::CLI, Logging::Level::Warning, "Dependency loop found"); //TODO localization + //TODO warn user but try to install either way + return; } // TODO raise error for failedPackages (if there's at least one) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 1a0c4137a4..036c1d14bf 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -73,14 +73,14 @@ namespace AppInstaller::CLI::Workflow bool HasNode(AppInstaller::Manifest::Dependency dependency) { auto search = adjacents.find(dependency); - return search == adjacents.end(); + return search != adjacents.end(); } // TODO make HasLoop and HasLoopDFS iterative bool HasLoop() { - auto visited = std::set(); - visited.insert(m_root); + installationOrder.clear(); + std::set visited; if (HasLoopDFS(visited, m_root)) { return true; diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 1c1dbcb9f3..af4b505a88 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -216,7 +216,7 @@ namespace { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, }, std::vector{ manifest2, manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller.Dependencies"))); } @@ -228,7 +228,7 @@ namespace ResultMatch( TestPackage::Make( std::vector{ manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller.WFDep"))); } @@ -334,7 +334,7 @@ namespace ResultMatch( TestPackage::Make( std::vector{ manifest }, - this->shared_from_this() + const_cast(this)->shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); return result; @@ -434,7 +434,7 @@ namespace void OverrideForOpenSource(TestContext& context) { - context.Override({ Workflow::OpenSource(), [](TestContext& context) + context.Override({ "OpenSource", [](TestContext& context) { context.Add(std::make_shared()); } }); @@ -442,7 +442,7 @@ void OverrideForOpenSource(TestContext& context) void OverrideForCompositeInstalledSource(TestContext& context) { - context.Override({ Workflow::OpenSource(), [](TestContext&) + context.Override({ "OpenSource", [](TestContext&) { } }); @@ -467,7 +467,7 @@ void OverrideForImportSource(TestContext& context) void OverrideForDependencySource(TestContext& context) { - context.Override({ Workflow::OpenSource(), [](TestContext& context) + context.Override({ "OpenSource", [](TestContext& context) { context.Add(std::make_shared()); } }); @@ -1804,7 +1804,7 @@ TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGr INFO(installOutput.str()); REQUIRE(installOutput.str().find("has loop") == std::string::npos); - REQUIRE(installOutput.str().find("B, C, F, DependencyAlreadyInStackButNoLoop,") != std::string::npos); + REQUIRE(installOutput.str().find("order: B, C, F, DependencyAlreadyInStackButNoLoop,") != std::string::npos); } TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph]") From 743ca96f37010590f1004eed175f2f0d309af8bf Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 19 Jul 2021 16:55:00 -0300 Subject: [PATCH 63/82] dependency-source argument --- src/AppInstallerCLICore/Argument.cpp | 2 ++ src/AppInstallerCLICore/Commands/InstallCommand.cpp | 2 +- src/AppInstallerCLICore/ExecutionArgs.h | 2 +- src/AppInstallerCLICore/Resources.h | 1 + src/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 4 ++-- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 4 ++++ 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index fb672e57a2..28fd6e0301 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -31,6 +31,8 @@ namespace AppInstaller::CLI return Argument{ "command", NoAlias, Args::Type::Command, Resource::String::CommandArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Source: return Argument{ "source", 's', Args::Type::Source, Resource::String::SourceArgumentDescription, ArgumentType::Standard }; + case Args::Type::DependencySource: + return Argument{ "dependency-source", NoAlias, Args::Type::DependencySource, Resource::String::DependencySourceArgumentDescription, ArgumentType::Standard }; case Args::Type::Count: return Argument{ "count", 'n', Args::Type::Count, Resource::String::CountArgumentDescription, ArgumentType::Standard }; case Args::Type::Exact: diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 2b9ef484a2..0f7222406a 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -39,7 +39,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::Override), Argument::ForType(Args::Type::InstallLocation), Argument::ForType(Args::Type::HashOverride), - Argument::ForType(Args::Type::DependenciesSource), + Argument::ForType(Args::Type::DependencySource), }; } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index fb79336948..ad7794f00c 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -76,7 +76,7 @@ namespace AppInstaller::CLI::Execution Help, // Show command usage Info, // Show general info about WinGet VerboseLogs, // Increases winget logging level to verbose - DependenciesSource, // Index source to be queried against for finding dependencies + DependencySource, // Index source to be queried against for finding dependencies // Used for demonstration purposes ExperimentalArg, diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index fc992bc902..ed4208b70f 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -191,6 +191,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(SourceAddCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceArgArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(DependencySourceArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceExportCommandLongDescription); diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index eb26d0b519..bd34327981 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -146,9 +146,9 @@ namespace AppInstaller::CLI::Workflow std::string_view sourceName; if (m_forDependencies) { - if (m_forDependencies && context.Args.Contains(Execution::Args::Type::DependenciesSource)) + if (m_forDependencies && context.Args.Contains(Execution::Args::Type::DependencySource)) { - sourceName = context.Args.GetArg(Execution::Args::Type::DependenciesSource); + sourceName = context.Args.GetArg(Execution::Args::Type::DependencySource); } } else diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 9e00e20f10..e10ff60bcb 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -949,4 +949,8 @@ Configuration is disabled due to Group Policy. Windows Libraries + + Find package depedencies using the specified source + For getting package type dependencies when installing from a local manifest + \ No newline at end of file From edde7c0976f3f0cf219541937aa8633cc296ae46 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 19 Jul 2021 18:34:02 -0300 Subject: [PATCH 64/82] typo --- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index e10ff60bcb..b5588a4ae6 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -950,7 +950,7 @@ Configuration is disabled due to Group Policy. Windows Libraries - Find package depedencies using the specified source + Find package dependencies using the specified source For getting package type dependencies when installing from a local manifest \ No newline at end of file From 25f30decb0d3717421de2c198ac673120eeca292 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 21 Jul 2021 12:33:38 -0300 Subject: [PATCH 65/82] DependencyGraph receives function to search for dependencies, auto-builds the graph --- .../Workflows/DependenciesFlow.cpp | 158 ++++++++---------- .../Workflows/DependenciesFlow.h | 66 +++++++- .../Public/winget/ManifestCommon.h | 25 +-- 3 files changed, 140 insertions(+), 109 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 23f1966039..c3c53cdae1 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -111,32 +111,16 @@ namespace AppInstaller::CLI::Workflow { auto info = context.Reporter.Info(); const auto& rootManifest = context.Get(); - Dependency rootDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); + Dependency rootAsDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); - std::vector toCheck; - DependencyGraph dependencyGraph(rootDependency); //(?) value should be a set instead of a vector? - const auto& rootInstaller = context.Get(); - if (rootInstaller) - { - context.Add(rootInstaller->Dependencies); // to use in report - // TODO remove this ^ if we are reporting dependencies somewhere else while installing/managing them - rootInstaller->Dependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) - { - toCheck.push_back(dependency); - dependencyGraph.AddNode(dependency); - dependencyGraph.AddAdjacent(rootDependency, dependency); - }); - } - else - { - info << "no installer found" << std::endl; - //TODO warn user and raise error, this should not happen as the workflow should fail before reaching here. - } - - if (toCheck.empty()) + const auto& rootDependencies = context.Get()->Dependencies; + // installer should exist, otherwise previous workflows should have failed + context.Add(rootDependencies); // to use in report + // TODO remove this ^ if we are reporting dependencies somewhere else while installing/managing them + + if (rootDependencies.Empty()) { - // nothing to do, there's no need to set up dependency source either. - // TODO add information to the logger + // If there's no dependencies there's nothing to do aside of logging the outcome return; } @@ -148,82 +132,76 @@ namespace AppInstaller::CLI::Workflow } const auto& source = context.Get(); - std::map failedPackages; - std::vector alreadyInstalled; - for (int i = 0; i < toCheck.size(); ++i) - { - auto dependencyNode = toCheck.at(i); + DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, + [&](Dependency node) { + auto info = context.Reporter.Info(); - SearchRequest searchRequest; - searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependencyNode.Id)); - const auto& matches = source->Search(searchRequest).Matches; + SearchRequest searchRequest; + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, node.Id)); + //TODO add min version filter to search request ? + const auto& matches = source->Search(searchRequest).Matches; - if (!matches.empty()) - { - const auto& match = matches.at(0); - if (matches.size() > 1) { - failedPackages[dependencyNode.Id] = "Too many matches"; //TODO localize all errors - continue; - } - - const auto& package = match.Package; - if (package->GetInstalledVersion() && dependencyNode.IsVersionOk(package->GetInstalledVersion()->GetManifest().Version)) - { - alreadyInstalled.push_back(dependencyNode); - } - else + if (!matches.empty()) { - const auto& packageLatestVersion = package->GetLatestAvailableVersion(); - if (!packageLatestVersion) { - failedPackages[dependencyNode.Id] = "No package version found"; //TODO localize all errors - continue; - } - - const auto& packageLatestVersionManifest = packageLatestVersion->GetManifest(); - if (packageLatestVersionManifest.Installers.empty()) { - failedPackages[dependencyNode.Id] = "No installers found"; //TODO localize all errors - continue; + if (matches.size() > 1) { + info << "Too many matches"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors } + const auto& match = matches.at(0); - if (!dependencyNode.IsVersionOk(packageLatestVersionManifest.Version)) + const auto& package = match.Package; + if (package->GetInstalledVersion() && node.IsVersionOk(package->GetInstalledVersion()->GetManifest().Version)) { - failedPackages[dependencyNode.Id] = "Minimum required version not available"; //TODO localize all errors - continue; + return DependencyList(); //return empty dependency list, as we won't keep searching for dependencies for installed packages + //TODO we should have this information on the graph, to avoid trying to install it later } - - // TODO FIX THIS, have a better way to pick installer (other than the first one) - // the problem is SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()) uses context data so it ends up returning - // the installer for the root package being installed. - //const auto& matchInstaller = SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()); - //if (!matchInstaller) - //{ - // failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors - // continue; - //} - - //const auto& matchDependencies = matchInstaller.value().Dependencies; - const auto& matchDependencies = packageLatestVersionManifest.Installers.at(0).Dependencies; - - // TODO save installers for later maybe? - matchDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + else + { + const auto& packageLatestVersion = package->GetLatestAvailableVersion(); + if (!packageLatestVersion) { + info << "No package version found"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + + const auto& packageLatestVersionManifest = packageLatestVersion->GetManifest(); + if (packageLatestVersionManifest.Installers.empty()) { + info << "No installers found"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + + if (!node.IsVersionOk(packageLatestVersionManifest.Version)) { - dependencyGraph.AddAdjacent(dependencyNode, dependency); - - if (!dependencyGraph.HasNode(dependency)) - { - toCheck.push_back(dependency); - dependencyGraph.AddNode(dependency); - } - }); + info << "Minimum required version not available"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + + // TODO FIX THIS, have a better way to pick installer (other than the first one) + // the problem is SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()) uses context data so it ends up returning + // the installer for the root package being installed. + //const auto& matchInstaller = SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()); + //if (!matchInstaller) + //{ + // failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors + // continue; + //} + // TODO save installers for later maybe? + + //const auto& matchDependencies = matchInstaller.value().Dependencies; + const auto& matchDependencies = packageLatestVersionManifest.Installers.at(0).Dependencies; + + return matchDependencies; + } } - } - else - { - failedPackages[dependencyNode.Id] = "No matches"; //TODO localize all errors - continue; - } - } + else + { + info << "No matches"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + }); + + dependencyGraph.BuildGraph(); // maybe it's better if it already does it on the constructor? + if (dependencyGraph.HasLoop()) { info << "has loop" << std::endl; diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 036c1d14bf..91a62281ab 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -53,10 +53,46 @@ namespace AppInstaller::CLI::Workflow struct DependencyGraph { - DependencyGraph(AppInstaller::Manifest::Dependency root) : m_root(root) + DependencyGraph(AppInstaller::Manifest::Dependency root, + AppInstaller::Manifest::DependencyList rootDependencies, + std::function infoFunction) : m_root(root), getDependencies(infoFunction) { adjacents[m_root] = std::vector(); + toCheck = std::vector(); + rootDependencies.ApplyToType(AppInstaller::Manifest::DependencyType::Package, [&](AppInstaller::Manifest::Dependency dependency) + { + toCheck.push_back(dependency); + AddNode(dependency); + AddAdjacent(root, dependency); + }); + } + + void BuildGraph() + { + if (toCheck.empty()) + { + return; + } + + for (int i = 0; i < toCheck.size(); ++i) + { + auto node = toCheck.at(i); + + const auto& nodeDependencies = getDependencies(node); //TODO add error stream so we can report back + + nodeDependencies.ApplyToType(AppInstaller::Manifest::DependencyType::Package, [&](AppInstaller::Manifest::Dependency dependency) + { + if (!HasNode(dependency)) + { + toCheck.push_back(dependency); + AddNode(dependency); + } + + AddAdjacent(node, dependency); + }); + } + CheckForLoopsAndGetOrder(); } void AddNode(AppInstaller::Manifest::Dependency node) @@ -76,16 +112,22 @@ namespace AppInstaller::CLI::Workflow return search != adjacents.end(); } - // TODO make HasLoop and HasLoopDFS iterative bool HasLoop() { - installationOrder.clear(); + return hasLoop; + } + + // TODO make CheckForLoops and HasLoopDFS iterative + void CheckForLoopsAndGetOrder() + { + installationOrder = std::vector(); std::set visited; - if (HasLoopDFS(visited, m_root)) - { - return true; - } - return false; + hasLoop = HasLoopDFS(visited, m_root); + } + + std::vector GetInstallationOrder() + { + return installationOrder; } //-- only for debugging @@ -128,7 +170,13 @@ namespace AppInstaller::CLI::Workflow } AppInstaller::Manifest::Dependency m_root; - std::map> adjacents; + std::map> adjacents; //(?) value should be a set instead of a vector? + std::function getDependencies; + + bool hasLoop; std::vector installationOrder; + + std::vector toCheck; + std::map failedPackages; }; } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index eb0a1a9cc9..99c218c74b 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -204,6 +204,21 @@ bool HasExtension(std::string_view extension) const; return nullptr; } + void ApplyToType(DependencyType type, std::function func) const + { + for (const auto& dependency : dependencies) + { + if (dependency.Type == type) func(dependency); + } + } + + bool Empty() const + { + return dependencies.empty(); + } + + void Clear() { dependencies.clear(); } + // for testing purposes bool HasExactDependency(DependencyType type, string_t id, string_t minVersion = "") { @@ -230,16 +245,6 @@ bool HasExtension(std::string_view extension) const; return dependencies.size(); } - void ApplyToType(DependencyType type, std::function func) const - { - for (const auto& dependency : dependencies) - { - if (dependency.Type == type) func(dependency); - } - } - - void Clear() { dependencies.clear(); } - private: std::vector dependencies; }; From ef3e6a0b0a1a09a621cd945a67dc97d9c617a231 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 22 Jul 2021 13:00:02 -0300 Subject: [PATCH 66/82] dependency table, create with id --- .../Workflows/DependenciesFlow.h | 2 +- .../Public/winget/ManifestCommon.h | 8 +++++++ .../AppInstallerRepositoryCore.vcxproj | 1 + ...AppInstallerRepositoryCore.vcxproj.filters | 3 +++ .../Schema/1_3/DependenciesTable.cpp | 0 .../Microsoft/Schema/1_3/DependenciesTable.h | 22 +++++++++++++++++++ .../Microsoft/Schema/1_3/Interface_1_3.cpp | 17 ++++++++++++++ 7 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/DependenciesTable.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/DependenciesTable.h diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 91a62281ab..279efbcece 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -117,7 +117,6 @@ namespace AppInstaller::CLI::Workflow return hasLoop; } - // TODO make CheckForLoops and HasLoopDFS iterative void CheckForLoopsAndGetOrder() { installationOrder = std::vector(); @@ -142,6 +141,7 @@ namespace AppInstaller::CLI::Workflow } private: + // TODO make HasLoopDFS iterative bool HasLoopDFS(std::set visited, const AppInstaller::Manifest::Dependency& node) { visited.insert(node); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 99c218c74b..dbf0359c5c 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -212,6 +212,14 @@ bool HasExtension(std::string_view extension) const; } } + void ApplyToAll(std::function func) const + { + for (const auto& dependency : dependencies) + { + func(dependency); + } + } + bool Empty() const { return dependencies.empty(); diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index de08205e68..4807c40d57 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -225,6 +225,7 @@ + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 11971b5953..ab6ac072a0 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -216,6 +216,9 @@ Microsoft\Schema\1_3 + + Microsoft\Schema\1_3 + diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/DependenciesTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/DependenciesTable.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/DependenciesTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/DependenciesTable.h new file mode 100644 index 0000000000..74f1234538 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/DependenciesTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToManyTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_3 +{ + namespace details + { + using namespace std::string_view_literals; + + struct DependenciesTableInfo + { + inline static constexpr std::string_view TableName() { return "dependencies"sv; } + inline static constexpr std::string_view ValueName() { return "id"sv; } + }; + } + + // The table for Dependencies. + using DependenciesTable = V1_0::OneToManyTable; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp index 68f258ff5e..16bb32d2a1 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp @@ -8,9 +8,22 @@ #include "Microsoft/Schema/1_3/HashVirtualTable.h" +#include "Microsoft/Schema/1_3/DependenciesTable.h" + namespace AppInstaller::Repository::Microsoft::Schema::V1_3 { + std::vector> GetDependencies(const Manifest::Manifest& manifest) + { + std::vector> manifestDependencies; + + manifest.DefaultInstallerInfo.Dependencies.ApplyToAll([&](AppInstaller::Manifest::Dependency dependency) + { + manifestDependencies.push_back(dependency.Id); + }); + return manifestDependencies; + } + Interface::Interface(Utility::NormalizationVersion normVersion) : V1_2::Interface(normVersion) { } @@ -28,6 +41,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_3 V1_0::ManifestTable::AddColumn(connection, { HashVirtualTable::ValueName(), HashVirtualTable::SQLiteType() }); + DependenciesTable::Create(connection); + savepoint.Commit(); } @@ -44,6 +59,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_3 V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, manifest.StreamSha256); } + DependenciesTable::EnsureExistsAndInsert(connection, GetDependencies(manifest), manifestId); + savepoint.Commit(); return manifestId; From e371668988b98c2e0d836d838481248423d37adb Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 23 Jul 2021 13:55:34 -0300 Subject: [PATCH 67/82] dependency graph logic moved to manifest common --- .../Workflows/DependenciesFlow.cpp | 9 +- .../Workflows/DependenciesFlow.h | 129 ------------------ .../Public/winget/ManifestCommon.h | 119 ++++++++++++++++ 3 files changed, 127 insertions(+), 130 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index c3c53cdae1..a432258bb3 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -212,6 +212,13 @@ namespace AppInstaller::CLI::Workflow // TODO raise error for failedPackages (if there's at least one) - dependencyGraph.PrintOrder(info); + //-- only for debugging + const auto& installationOrder = dependencyGraph.GetInstallationOrder(); + info << "order: "; + for (auto const& node : installationOrder) + { + info << node.Id << ", "; + } + info << std::endl; } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 279efbcece..67eb8b70f1 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -50,133 +50,4 @@ namespace AppInstaller::CLI::Workflow // Inputs: PackageVersion, Manifest // Outputs: DependencySource void OpenDependencySource(Execution::Context& context); - - struct DependencyGraph - { - DependencyGraph(AppInstaller::Manifest::Dependency root, - AppInstaller::Manifest::DependencyList rootDependencies, - std::function infoFunction) : m_root(root), getDependencies(infoFunction) - { - adjacents[m_root] = std::vector(); - toCheck = std::vector(); - rootDependencies.ApplyToType(AppInstaller::Manifest::DependencyType::Package, [&](AppInstaller::Manifest::Dependency dependency) - { - toCheck.push_back(dependency); - AddNode(dependency); - AddAdjacent(root, dependency); - }); - } - - void BuildGraph() - { - if (toCheck.empty()) - { - return; - } - - for (int i = 0; i < toCheck.size(); ++i) - { - auto node = toCheck.at(i); - - const auto& nodeDependencies = getDependencies(node); //TODO add error stream so we can report back - - nodeDependencies.ApplyToType(AppInstaller::Manifest::DependencyType::Package, [&](AppInstaller::Manifest::Dependency dependency) - { - if (!HasNode(dependency)) - { - toCheck.push_back(dependency); - AddNode(dependency); - } - - AddAdjacent(node, dependency); - }); - } - - CheckForLoopsAndGetOrder(); - } - - void AddNode(AppInstaller::Manifest::Dependency node) - { - adjacents[node] = std::vector(); - - } - - void AddAdjacent(AppInstaller::Manifest::Dependency node, AppInstaller::Manifest::Dependency adjacent) - { - adjacents[node].push_back(adjacent); - } - - bool HasNode(AppInstaller::Manifest::Dependency dependency) - { - auto search = adjacents.find(dependency); - return search != adjacents.end(); - } - - bool HasLoop() - { - return hasLoop; - } - - void CheckForLoopsAndGetOrder() - { - installationOrder = std::vector(); - std::set visited; - hasLoop = HasLoopDFS(visited, m_root); - } - - std::vector GetInstallationOrder() - { - return installationOrder; - } - - //-- only for debugging - void PrintOrder(AppInstaller::CLI::Execution::OutputStream info) - { - info << "order: "; - for (auto const& node : installationOrder) - { - info << node.Id << ", "; - } - info << std::endl; - } - - private: - // TODO make HasLoopDFS iterative - bool HasLoopDFS(std::set visited, const AppInstaller::Manifest::Dependency& node) - { - visited.insert(node); - for (const auto& adjacent : adjacents.at(node)) - { - auto search = visited.find(adjacent); - if (search == visited.end()) // if not found - { - if (HasLoopDFS(visited, adjacent)) - { - return true; - } - } - else - { - return true; - } - } - - if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) - { - installationOrder.push_back(node); - } - - return false; - } - - AppInstaller::Manifest::Dependency m_root; - std::map> adjacents; //(?) value should be a set instead of a vector? - std::function getDependencies; - - bool hasLoop; - std::vector installationOrder; - - std::vector toCheck; - std::map failedPackages; - }; } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index dbf0359c5c..c726c19285 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace AppInstaller::Manifest @@ -257,6 +258,124 @@ bool HasExtension(std::string_view extension) const; std::vector dependencies; }; + struct DependencyGraph + { + DependencyGraph(Dependency root, DependencyList rootDependencies, + std::function infoFunction) : m_root(root), getDependencies(infoFunction) + { + adjacents[m_root] = std::vector(); + toCheck = std::vector(); + rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + toCheck.push_back(dependency); + AddNode(dependency); + AddAdjacent(root, dependency); + }); + } + + void BuildGraph() + { + if (toCheck.empty()) + { + return; + } + + for (int i = 0; i < toCheck.size(); ++i) + { + auto node = toCheck.at(i); + + const auto& nodeDependencies = getDependencies(node); + //TODO add error stream so we can report back + + nodeDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + if (!HasNode(dependency)) + { + toCheck.push_back(dependency); + AddNode(dependency); + } + + AddAdjacent(node, dependency); + }); + } + + CheckForLoopsAndGetOrder(); + } + + void AddNode(Dependency node) + { + adjacents[node] = std::vector(); + + } + + void AddAdjacent(Dependency node, Dependency adjacent) + { + adjacents[node].push_back(adjacent); + } + + bool HasNode(Dependency dependency) + { + auto search = adjacents.find(dependency); + return search != adjacents.end(); + } + + bool HasLoop() + { + return hasLoop; + } + + void CheckForLoopsAndGetOrder() + { + installationOrder = std::vector(); + std::set visited; + hasLoop = HasLoopDFS(visited, m_root); + } + + std::vector GetInstallationOrder() + { + return installationOrder; + } + + private: + // TODO make this function iterative + bool HasLoopDFS(std::set visited, const Dependency& node) + { + visited.insert(node); + for (const auto& adjacent : adjacents.at(node)) + { + auto search = visited.find(adjacent); + if (search == visited.end()) // if not found + { + if (HasLoopDFS(visited, adjacent)) + { + return true; + } + } + else + { + return true; + } + } + + if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) + { + installationOrder.push_back(node); + } + + return false; + } + + Dependency m_root; + std::map> adjacents; //(?) value should be a set instead of a vector? + std::function getDependencies; + + bool hasLoop; + std::vector installationOrder; + + std::vector toCheck; + std::map failedPackages; + }; + InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); UpdateBehaviorEnum ConvertToUpdateBehaviorEnum(const std::string& in); From e6c00d62b3055ee63a0125904bb949219198f670 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Mon, 26 Jul 2021 18:16:06 -0300 Subject: [PATCH 68/82] refactor --- .../Workflows/DependenciesFlow.cpp | 57 ++++++++++++------- .../Workflows/InstallFlow.cpp | 21 +++++-- .../Workflows/InstallFlow.h | 4 ++ src/AppInstallerCLITests/WorkFlow.cpp | 4 ++ .../Public/winget/ManifestCommon.h | 16 +++++- 5 files changed, 73 insertions(+), 29 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index a432258bb3..1917a5d931 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -3,8 +3,10 @@ #include "pch.h" #include "DependenciesFlow.h" +#include "InstallFlow.h" #include "ManifestComparator.h" + namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::Repository; @@ -107,6 +109,7 @@ namespace AppInstaller::CLI::Workflow } } + void BuildPackageDependenciesGraph(Execution::Context& context) { auto info = context.Reporter.Info(); @@ -128,10 +131,11 @@ namespace AppInstaller::CLI::Workflow if (!context.Contains(Execution::Data::DependencySource)) { info << "dependency source not found" << std::endl; //TODO localize message - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING); // TODO create a new error code? + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); // TODO create specific error code } const auto& source = context.Get(); + std::map dependenciesInstallers; DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, [&](Dependency node) { @@ -155,42 +159,40 @@ namespace AppInstaller::CLI::Workflow { return DependencyList(); //return empty dependency list, as we won't keep searching for dependencies for installed packages //TODO we should have this information on the graph, to avoid trying to install it later + // TODO if it's already installed we need to upgrade it } else { - const auto& packageLatestVersion = package->GetLatestAvailableVersion(); - if (!packageLatestVersion) { + const auto& latestVersion = package->GetLatestAvailableVersion(); + if (!latestVersion) { info << "No package version found"; //TODO localize all errors return DependencyList(); //return empty dependency list, TODO change this to actually manage errors } - const auto& packageLatestVersionManifest = packageLatestVersion->GetManifest(); - if (packageLatestVersionManifest.Installers.empty()) { + const auto& manifest = latestVersion->GetManifest(); + if (manifest.Installers.empty()) { info << "No installers found"; //TODO localize all errors return DependencyList(); //return empty dependency list, TODO change this to actually manage errors } - if (!node.IsVersionOk(packageLatestVersionManifest.Version)) + if (!node.IsVersionOk(manifest.Version)) { info << "Minimum required version not available"; //TODO localize all errors return DependencyList(); //return empty dependency list, TODO change this to actually manage errors } // TODO FIX THIS, have a better way to pick installer (other than the first one) + const auto* installer = &manifest.Installers.at(0); // the problem is SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()) uses context data so it ends up returning // the installer for the root package being installed. - //const auto& matchInstaller = SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()); - //if (!matchInstaller) - //{ - // failedPackages[dependencyNode.Id] = "No installer found"; //TODO localize all errors - // continue; - //} - // TODO save installers for later maybe? - - //const auto& matchDependencies = matchInstaller.value().Dependencies; - const auto& matchDependencies = packageLatestVersionManifest.Installers.at(0).Dependencies; - - return matchDependencies; + //const auto& installer = SelectInstallerFromMetadata(context, latestVersion->GetMetadata()); + + const auto& nodeDependencies = installer->Dependencies; + + //auto packageDescription = AppInstaller::CLI::PackageCollection::Package(manifest.Id, manifest.Version, manifest.Channel); + // create package description too be able to use it for installer + //dependenciesInstallers[node.Id] = PackagesAndInstallers(installer, latestVersion, manifest.Version, manifest.Channel); + return nodeDependencies; } } else @@ -212,13 +214,24 @@ namespace AppInstaller::CLI::Workflow // TODO raise error for failedPackages (if there's at least one) - //-- only for debugging const auto& installationOrder = dependencyGraph.GetInstallationOrder(); - info << "order: "; + + + std::vector installers; + + info << "order: "; //-- only for debugging for (auto const& node : installationOrder) { - info << node.Id << ", "; + info << node.Id << ", "; //-- only for debugging + installers.push_back(dependenciesInstallers.find(node.Id)->second); + } + info << std::endl; //-- only for debugging + + bool allSucceeded = InstallPackages(context, installers); + if (!allSucceeded) + { + context.Reporter.Error() << "error installing dependencies" << std::endl; //TODO localize error + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); // TODO create specific error code } - info << std::endl; } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 1be6c27851..9dffc8d0d0 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -466,6 +466,19 @@ namespace AppInstaller::CLI::Workflow context << Workflow::ReportDependencies(Resource::String::ImportCommandReportDependencies); } + allSucceeded &= InstallPackages(context, installers); + + if (!allSucceeded) + { + context.Reporter.Error() << Resource::String::ImportInstallFailed << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); + } + } + + bool InstallPackages(Execution::Context& context, std::vector installers) + { + bool allSucceeded = true; + for (auto packageAndInstaller : installers) { auto package = packageAndInstaller.Package; @@ -490,18 +503,14 @@ namespace AppInstaller::CLI::Workflow { // This means that the subcontext being terminated is due to an overall abort context.Reporter.Info() << Resource::String::Cancelled << std::endl; - return; + return false; } allSucceeded = false; } } - if (!allSucceeded) - { - context.Reporter.Error() << Resource::String::ImportInstallFailed << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); - } + return allSucceeded; } void SnapshotARPEntries(Execution::Context& context) try diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index e21cdee97a..10e1e15943 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -121,4 +121,8 @@ namespace AppInstaller::CLI::Workflow std::optional Installer; AppInstaller::CLI::Execution::PackageToInstall Package; }; + + // Installs packages and returns if all succeeded or not + bool InstallPackages(Execution::Context& context, std::vector installers); + } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index af4b505a88..1071471523 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -330,6 +330,10 @@ namespace installer.Dependencies.Add(Dependency(DependencyType::Package, "H")); } + //TODO: + // test for installed packages (or the ones that need upgrade) + // test for different min Version of dependencies + result.Matches.emplace_back( ResultMatch( TestPackage::Make( diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index c726c19285..098b9da152 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -260,6 +260,7 @@ bool HasExtension(std::string_view extension) const; struct DependencyGraph { + // this constructor was intented for use during installation flow (we already have installer dependencies and there's no need to search the source again) DependencyGraph(Dependency root, DependencyList rootDependencies, std::function infoFunction) : m_root(root), getDependencies(infoFunction) { @@ -273,6 +274,20 @@ bool HasExtension(std::string_view extension) const; }); } + DependencyGraph(Dependency root, std::function infoFunction) : m_root(root), getDependencies(infoFunction) + { + adjacents[m_root] = std::vector(); + toCheck = std::vector(); + + DependencyList rootDependencies = getDependencies(root); + rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + toCheck.push_back(dependency); + AddNode(dependency); + AddAdjacent(root, dependency); + }); + } + void BuildGraph() { if (toCheck.empty()) @@ -305,7 +320,6 @@ bool HasExtension(std::string_view extension) const; void AddNode(Dependency node) { adjacents[node] = std::vector(); - } void AddAdjacent(Dependency node, Dependency adjacent) From 7b4238a98d3f0cec28b36aff3ebe81e7b8f857fd Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Tue, 27 Jul 2021 16:41:00 -0300 Subject: [PATCH 69/82] break InstallMultiple workflow into two --- .../Commands/ImportCommand.cpp | 2 +- .../ExecutionContextData.h | 14 ++++ .../Workflows/DependenciesFlow.cpp | 68 ++++++++++--------- .../Workflows/InstallFlow.cpp | 41 ++++++----- .../Workflows/InstallFlow.h | 21 ++---- .../Workflows/WorkflowBase.cpp | 4 +- 6 files changed, 82 insertions(+), 68 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/ImportCommand.cpp b/src/AppInstallerCLICore/Commands/ImportCommand.cpp index 7d7c9c2a19..1f6922c5c2 100644 --- a/src/AppInstallerCLICore/Commands/ImportCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ImportCommand.cpp @@ -47,6 +47,6 @@ namespace AppInstaller::CLI Workflow::OpenPredefinedSource(Repository::PredefinedSource::Installed) << Workflow::SearchPackagesForImport << Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) << - Workflow::InstallMultiple; + Workflow::SelectInstallerMultiple; } } diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index f1c0e63ad1..78e554d405 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -44,6 +44,7 @@ namespace AppInstaller::CLI::Execution PackageCollection, // On import: A collection of specific package versions to install PackagesToInstall, + InstallersToInstall, // On import: Sources for the imported packages Sources, ARPSnapshot, @@ -58,6 +59,13 @@ namespace AppInstaller::CLI::Execution PackageCollection::Package PackageRequest; }; + struct InstallerToInstall + { + std::shared_ptr PackageVersion; + Manifest::ManifestInstaller Installer; + bool IsUpdate = false; + }; + namespace details { template @@ -173,6 +181,12 @@ namespace AppInstaller::CLI::Execution { using value_t = std::vector; }; + + template <> + struct DataMapping + { + using value_t = std::vector; + }; template <> struct DataMapping diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 1917a5d931..31cd0487f2 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -3,8 +3,8 @@ #include "pch.h" #include "DependenciesFlow.h" -#include "InstallFlow.h" #include "ManifestComparator.h" +#include "InstallFlow.h" namespace AppInstaller::CLI::Workflow @@ -98,8 +98,8 @@ namespace AppInstaller::CLI::Workflow { const auto& packageVersion = context.Get(); context.Add(packageVersion->GetSource()); - /*context << - Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true);*/ + context << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); } else { // install from manifest requires --dependency-source to be set @@ -114,12 +114,13 @@ namespace AppInstaller::CLI::Workflow { auto info = context.Reporter.Info(); const auto& rootManifest = context.Get(); + Dependency rootAsDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); - const auto& rootDependencies = context.Get()->Dependencies; - // installer should exist, otherwise previous workflows should have failed - context.Add(rootDependencies); // to use in report - // TODO remove this ^ if we are reporting dependencies somewhere else while installing/managing them + const auto& rootInstaller = context.Get(); + const auto& rootDependencies = rootInstaller->Dependencies; + + context.Add(rootDependencies); //information needed to report dependencies if (rootDependencies.Empty()) { @@ -135,7 +136,10 @@ namespace AppInstaller::CLI::Workflow } const auto& source = context.Get(); - std::map dependenciesInstallers; + std::map idToInstallerMap; + + //idToInstallerMap[rootManifest.Id] = {rootVersion, rootInstaller.value(), false}; + // Question: where do I get the root version from? DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, [&](Dependency node) { @@ -158,8 +162,6 @@ namespace AppInstaller::CLI::Workflow if (package->GetInstalledVersion() && node.IsVersionOk(package->GetInstalledVersion()->GetManifest().Version)) { return DependencyList(); //return empty dependency list, as we won't keep searching for dependencies for installed packages - //TODO we should have this information on the graph, to avoid trying to install it later - // TODO if it's already installed we need to upgrade it } else { @@ -171,7 +173,7 @@ namespace AppInstaller::CLI::Workflow const auto& manifest = latestVersion->GetManifest(); if (manifest.Installers.empty()) { - info << "No installers found"; //TODO localize all errors + info << "No installers found"; //TODO localize all errors return DependencyList(); //return empty dependency list, TODO change this to actually manage errors } @@ -181,17 +183,21 @@ namespace AppInstaller::CLI::Workflow return DependencyList(); //return empty dependency list, TODO change this to actually manage errors } - // TODO FIX THIS, have a better way to pick installer (other than the first one) - const auto* installer = &manifest.Installers.at(0); - // the problem is SelectInstallerFromMetadata(context, packageLatestVersion->GetMetadata()) uses context data so it ends up returning - // the installer for the root package being installed. - //const auto& installer = SelectInstallerFromMetadata(context, latestVersion->GetMetadata()); + std::optional installer; + bool isUpdate = false; + if (package->GetInstalledVersion()) + { + installer = SelectInstallerFromMetadata(context, package->GetInstalledVersion()->GetMetadata()); + isUpdate = true; + } + else + { + installer = SelectInstallerFromMetadata(context, {}); + } const auto& nodeDependencies = installer->Dependencies; - //auto packageDescription = AppInstaller::CLI::PackageCollection::Package(manifest.Id, manifest.Version, manifest.Channel); - // create package description too be able to use it for installer - //dependenciesInstallers[node.Id] = PackagesAndInstallers(installer, latestVersion, manifest.Version, manifest.Channel); + idToInstallerMap[node.Id] = {latestVersion, installer.value(), isUpdate}; return nodeDependencies; } } @@ -209,29 +215,29 @@ namespace AppInstaller::CLI::Workflow info << "has loop" << std::endl; Logging::Log().Write(Logging::Channel::CLI, Logging::Level::Warning, "Dependency loop found"); //TODO localization //TODO warn user but try to install either way - return; } - // TODO raise error for failedPackages (if there's at least one) + // TODO raise error for failed packages? (if there's at least one) const auto& installationOrder = dependencyGraph.GetInstallationOrder(); - - std::vector installers; + std::vector installers; info << "order: "; //-- only for debugging for (auto const& node : installationOrder) { info << node.Id << ", "; //-- only for debugging - installers.push_back(dependenciesInstallers.find(node.Id)->second); + + auto itr = idToInstallerMap.find(node.Id); + // if the package was already installed (with a useful version) there will be no installer for it on the map. + if (itr != idToInstallerMap.end()) + { + installers.push_back(itr->second); + } } info << std::endl; //-- only for debugging - - bool allSucceeded = InstallPackages(context, installers); - if (!allSucceeded) - { - context.Reporter.Error() << "error installing dependencies" << std::endl; //TODO localize error - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); // TODO create specific error code - } + + context.Add(installers); + context << InstallMultiple; } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 9dffc8d0d0..625a9499d7 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -7,7 +7,7 @@ #include "ShellExecuteInstallerHandler.h" #include "MSStoreInstallerHandler.h" #include "WorkflowBase.h" -#include "Workflows/DependenciesFlow.h" +#include "DependenciesFlow.h" namespace AppInstaller::CLI::Workflow { @@ -416,15 +416,15 @@ namespace AppInstaller::CLI::Workflow Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::BuildPackageDependenciesGraph << - Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies)<< Workflow::InstallPackageInstaller; } - void InstallMultiple(Execution::Context& context) + void SelectInstallerMultiple(Execution::Context& context) { bool allSucceeded = true; DependencyList allDependencies; - std::vector installers; + std::vector installers; for (auto package : context.Get()) { @@ -452,11 +452,11 @@ namespace AppInstaller::CLI::Workflow } const auto& installer = installContext.Get(); - installers.push_back(PackagesAndInstallers(installer, package)); + installers.push_back({package.PackageVersion, installer.value(), false}); if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - if (installer) allDependencies.Add(installer->Dependencies); + allDependencies.Add(installer->Dependencies); } } @@ -465,32 +465,27 @@ namespace AppInstaller::CLI::Workflow context.Add(allDependencies); context << Workflow::ReportDependencies(Resource::String::ImportCommandReportDependencies); } - - allSucceeded &= InstallPackages(context, installers); - - if (!allSucceeded) - { - context.Reporter.Error() << Resource::String::ImportInstallFailed << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); - } + context.Add(installers); + context << InstallMultiple; } - bool InstallPackages(Execution::Context& context, std::vector installers) + void InstallMultiple(Execution::Context& context) { bool allSucceeded = true; + const auto& installers = context.Get(); + for (auto packageAndInstaller : installers) { - auto package = packageAndInstaller.Package; + auto packageVersion = packageAndInstaller.PackageVersion; auto installer = packageAndInstaller.Installer; auto installContextPtr = context.Clone(); Execution::Context& installContext = *installContextPtr; // set data needed for installing - installContext.Add(package.PackageVersion); - installContext.Add(package.PackageVersion->GetManifest()); - installContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(package.PackageRequest.Scope)); + installContext.Add(packageVersion); + installContext.Add(packageVersion->GetManifest()); installContext.Add(installer); installContext << @@ -503,14 +498,18 @@ namespace AppInstaller::CLI::Workflow { // This means that the subcontext being terminated is due to an overall abort context.Reporter.Info() << Resource::String::Cancelled << std::endl; - return false; + return; } allSucceeded = false; } } - return allSucceeded; + if (!allSucceeded) + { + context.Reporter.Error() << Resource::String::ImportInstallFailed << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); + } } void SnapshotARPEntries(Execution::Context& context) try diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 10e1e15943..157568689c 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -95,9 +95,15 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void InstallPackageVersion(Execution::Context& context); + // Selects the installer for each of the packages to install. + // Required Args: None + // Inputs: PackagesToInstall + // Outputs: InstallersToInstall + void SelectInstallerMultiple(Execution::Context& context); + // Installs multiple packages. // Required Args: None - // Inputs: Manifests + // Inputs: InstallersToInstall // Outputs: None void InstallMultiple(Execution::Context& context); @@ -112,17 +118,4 @@ namespace AppInstaller::CLI::Workflow // Inputs: ARPSnapshot?, Manifest, PackageVersion // Outputs: None void ReportARPChanges(Execution::Context& context); - - const struct PackagesAndInstallers - { - PackagesAndInstallers(std::optional inst, - AppInstaller::CLI::Execution::PackageToInstall pkg) : Installer(inst), Package(pkg) {} - - std::optional Installer; - AppInstaller::CLI::Execution::PackageToInstall Package; - }; - - // Installs packages and returns if all succeeded or not - bool InstallPackages(Execution::Context& context, std::vector installers); - } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index bd34327981..12fe687a7d 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -237,15 +237,17 @@ namespace AppInstaller::CLI::Workflow // Create the composite source from the two. std::shared_ptr source; + std::shared_ptr compositeSource; if (m_forDependencies) { source = context.Get(); + compositeSource = Repository::CreateCompositeSource(source, availableSource, CompositeSearchBehavior::AvailablePackages); } else { source = context.Get(); + compositeSource = Repository::CreateCompositeSource(source, availableSource); } - std::shared_ptr compositeSource = Repository::CreateCompositeSource(source, availableSource); // Overwrite the source with the composite. if (m_forDependencies) From a05f384b5585b722e19b790beac380b349bf8276 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 28 Jul 2021 11:41:05 -0300 Subject: [PATCH 70/82] SelectInstallerFromMetadata does not receive context --- .../Workflows/DependenciesFlow.cpp | 19 +++++++++++-------- .../Workflows/DependenciesFlow.h | 2 +- .../Workflows/InstallFlow.cpp | 4 ++-- .../Workflows/WorkflowBase.cpp | 8 ++++---- .../Workflows/WorkflowBase.h | 2 +- .../Public/winget/ManifestCommon.h | 18 +++++++++--------- 6 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 31cd0487f2..ba497b3929 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -110,7 +110,7 @@ namespace AppInstaller::CLI::Workflow } - void BuildPackageDependenciesGraph(Execution::Context& context) + void ManagePackageDependencies(Execution::Context& context) { auto info = context.Reporter.Info(); const auto& rootManifest = context.Get(); @@ -138,9 +138,6 @@ namespace AppInstaller::CLI::Workflow const auto& source = context.Get(); std::map idToInstallerMap; - //idToInstallerMap[rootManifest.Id] = {rootVersion, rootInstaller.value(), false}; - // Question: where do I get the root version from? - DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, [&](Dependency node) { auto info = context.Reporter.Info(); @@ -187,12 +184,12 @@ namespace AppInstaller::CLI::Workflow bool isUpdate = false; if (package->GetInstalledVersion()) { - installer = SelectInstallerFromMetadata(context, package->GetInstalledVersion()->GetMetadata()); + installer = SelectInstallerFromMetadata(context.Args, manifest, package->GetInstalledVersion()->GetMetadata()); isUpdate = true; } else { - installer = SelectInstallerFromMetadata(context, {}); + installer = SelectInstallerFromMetadata(context.Args, manifest, {}); } const auto& nodeDependencies = installer->Dependencies; @@ -214,7 +211,8 @@ namespace AppInstaller::CLI::Workflow { info << "has loop" << std::endl; Logging::Log().Write(Logging::Channel::CLI, Logging::Level::Warning, "Dependency loop found"); //TODO localization - //TODO warn user but try to install either way + //TODO warn user but try to install either way (right now packages are only added to installation order if there's not a loop) + return; } // TODO raise error for failed packages? (if there's at least one) @@ -229,7 +227,9 @@ namespace AppInstaller::CLI::Workflow info << node.Id << ", "; //-- only for debugging auto itr = idToInstallerMap.find(node.Id); - // if the package was already installed (with a useful version) there will be no installer for it on the map. + // if the package was already installed (with a useful version) + // or is the root + // then there will be no installer for it on the map. if (itr != idToInstallerMap.end()) { installers.push_back(itr->second); @@ -237,7 +237,10 @@ namespace AppInstaller::CLI::Workflow } info << std::endl; //-- only for debugging + // Install dependencies in the correct order context.Add(installers); context << InstallMultiple; + + // Install the root (continue) } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index 67eb8b70f1..b97ab50995 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -43,7 +43,7 @@ namespace AppInstaller::CLI::Workflow // Required Args: None // Inputs: DependencySource // Outputs: Dependencies - void BuildPackageDependenciesGraph(Execution::Context& context); + void ManagePackageDependencies(Execution::Context& context); // Sets up the source used to get the dependencies. // Required Args: None diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 625a9499d7..0cfd4302c4 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -415,8 +415,8 @@ namespace AppInstaller::CLI::Workflow Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << - Workflow::BuildPackageDependenciesGraph << - Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies)<< + Workflow::ManagePackageDependencies << + Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 12fe687a7d..f4109ad169 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -725,13 +725,13 @@ namespace AppInstaller::CLI::Workflow installationMetadata = context.Get()->GetMetadata(); } - context.Add(SelectInstallerFromMetadata(context, installationMetadata)); + context.Add(SelectInstallerFromMetadata(context.Args, context.Get(), installationMetadata)); } - std::optional SelectInstallerFromMetadata(Execution::Context& context, IPackageVersion::Metadata metadata) + std::optional SelectInstallerFromMetadata(Execution::Args args, AppInstaller::Manifest::Manifest manifest, AppInstaller::Repository::IPackageVersion::Metadata metadata) { - ManifestComparator manifestComparator(context.Args, metadata); - return manifestComparator.GetPreferredInstaller(context.Get()); + ManifestComparator manifestComparator(args, metadata); + return manifestComparator.GetPreferredInstaller(manifest); } void EnsureRunningAsAdmin(Execution::Context& context) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index b12c0910d9..1faf376664 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -298,7 +298,7 @@ namespace AppInstaller::CLI::Workflow // Inputs: Manifest // Outputs: Installer void SelectInstaller(Execution::Context& context); - std::optional SelectInstallerFromMetadata(Execution::Context& context, AppInstaller::Repository::IPackageVersion::Metadata metadata); + std::optional SelectInstallerFromMetadata(Execution::Args args, AppInstaller::Manifest::Manifest manifest, AppInstaller::Repository::IPackageVersion::Metadata metadata); // Ensures that the process is running as admin. // Required Args: None diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 098b9da152..b6147acc2a 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -261,8 +261,8 @@ bool HasExtension(std::string_view extension) const; struct DependencyGraph { // this constructor was intented for use during installation flow (we already have installer dependencies and there's no need to search the source again) - DependencyGraph(Dependency root, DependencyList rootDependencies, - std::function infoFunction) : m_root(root), getDependencies(infoFunction) + DependencyGraph(const Dependency& root, const DependencyList& rootDependencies, + std::function infoFunction) : m_root(root), getDependencies(infoFunction) { adjacents[m_root] = std::vector(); toCheck = std::vector(); @@ -274,12 +274,12 @@ bool HasExtension(std::string_view extension) const; }); } - DependencyGraph(Dependency root, std::function infoFunction) : m_root(root), getDependencies(infoFunction) + DependencyGraph(const Dependency& root, std::function infoFunction) : m_root(root), getDependencies(infoFunction) { adjacents[m_root] = std::vector(); toCheck = std::vector(); - DependencyList rootDependencies = getDependencies(root); + const DependencyList& rootDependencies = getDependencies(root); rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { toCheck.push_back(dependency); @@ -317,17 +317,17 @@ bool HasExtension(std::string_view extension) const; CheckForLoopsAndGetOrder(); } - void AddNode(Dependency node) + void AddNode(const Dependency& node) { adjacents[node] = std::vector(); } - void AddAdjacent(Dependency node, Dependency adjacent) + void AddAdjacent(const Dependency& node,const Dependency& adjacent) { adjacents[node].push_back(adjacent); } - bool HasNode(Dependency dependency) + bool HasNode(const Dependency& dependency) { auto search = adjacents.find(dependency); return search != adjacents.end(); @@ -379,9 +379,9 @@ bool HasExtension(std::string_view extension) const; return false; } - Dependency m_root; + const Dependency& m_root; std::map> adjacents; //(?) value should be a set instead of a vector? - std::function getDependencies; + std::function getDependencies; bool hasLoop; std::vector installationOrder; From 4a1aec8c8b66177c78226c462c5cf65d0fd62f2e Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 28 Jul 2021 14:02:22 -0300 Subject: [PATCH 71/82] leaving workflow to work as in report dependencies, with the addition of managing package dep --- .../Workflows/DependenciesFlow.cpp | 8 ++------ src/AppInstallerCLICore/Workflows/InstallFlow.cpp | 1 + src/AppInstallerCLITests/WorkFlow.cpp | 10 +++++----- .../Public/winget/ManifestCommon.h | 11 ++++++----- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index ba497b3929..22bcb0b28f 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -120,8 +120,6 @@ namespace AppInstaller::CLI::Workflow const auto& rootInstaller = context.Get(); const auto& rootDependencies = rootInstaller->Dependencies; - context.Add(rootDependencies); //information needed to report dependencies - if (rootDependencies.Empty()) { // If there's no dependencies there's nothing to do aside of logging the outcome @@ -211,8 +209,7 @@ namespace AppInstaller::CLI::Workflow { info << "has loop" << std::endl; Logging::Log().Write(Logging::Channel::CLI, Logging::Level::Warning, "Dependency loop found"); //TODO localization - //TODO warn user but try to install either way (right now packages are only added to installation order if there's not a loop) - return; + //warn user but try to install either way } // TODO raise error for failed packages? (if there's at least one) @@ -227,8 +224,7 @@ namespace AppInstaller::CLI::Workflow info << node.Id << ", "; //-- only for debugging auto itr = idToInstallerMap.find(node.Id); - // if the package was already installed (with a useful version) - // or is the root + // if the package was already installed (with a useful version) or is the root // then there will be no installer for it on the map. if (itr != idToInstallerMap.end()) { diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 0cfd4302c4..13b1425119 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -415,6 +415,7 @@ namespace AppInstaller::CLI::Workflow Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << + Workflow::GetDependenciesFromInstaller << Workflow::ManagePackageDependencies << Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 1071471523..f6961a7536 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1768,7 +1768,7 @@ TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } -TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph]") +TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); @@ -1789,7 +1789,7 @@ TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph]") REQUIRE(installOutput.str().find("has loop") != std::string::npos); } -TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGraph]") +TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); @@ -1811,7 +1811,7 @@ TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGr REQUIRE(installOutput.str().find("order: B, C, F, DependencyAlreadyInStackButNoLoop,") != std::string::npos); } -TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph]") +TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); @@ -1834,7 +1834,7 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph } -TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyGraph]") +TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); @@ -1857,7 +1857,7 @@ TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyG } -TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph]") +TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index b6147acc2a..3173c444f2 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -354,6 +354,12 @@ bool HasExtension(std::string_view extension) const; // TODO make this function iterative bool HasLoopDFS(std::set visited, const Dependency& node) { + // Adding before checking for loops, to have an order even if a loop is present + if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) + { + installationOrder.push_back(node); + } + visited.insert(node); for (const auto& adjacent : adjacents.at(node)) { @@ -371,11 +377,6 @@ bool HasExtension(std::string_view extension) const; } } - if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) - { - installationOrder.push_back(node); - } - return false; } From 29bc5fde4f208221bc2c9f1c1091d24e9748b4fa Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Wed, 28 Jul 2021 15:47:11 -0300 Subject: [PATCH 72/82] move dependencies to context data --- .../Workflows/DependenciesFlow.cpp | 2 +- .../Workflows/InstallFlow.cpp | 2 +- .../Public/winget/ManifestCommon.h | 6 +- .../DependenciesFlow.cpp | 242 ++++++ .../ManifestCommon.h | 421 ++++++++++ .../InstallFlow.cpp | 731 ++++++++++++++++++ 6 files changed, 1399 insertions(+), 5 deletions(-) create mode 100644 src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp create mode 100644 src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h create mode 100644 src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 22bcb0b28f..070672eb61 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -67,7 +67,7 @@ namespace AppInstaller::CLI::Workflow allDependencies.Add(installer.Dependencies); } - context.Add(allDependencies); + context.Add(std::move(allDependencies)); } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 13b1425119..15f462ab07 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -463,7 +463,7 @@ namespace AppInstaller::CLI::Workflow if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { - context.Add(allDependencies); + context.Add(std::move(allDependencies)); context << Workflow::ReportDependencies(Resource::String::ImportCommandReportDependencies); } context.Add(installers); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 3173c444f2..83bcde2eca 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -128,7 +128,7 @@ bool HasExtension(std::string_view extension) const; std::optional MinVersion; Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), Id(std::move(id)), MinVersion(AppInstaller::Utility::Version(minVersion)) {} - Dependency(DependencyType type, string_t id) : Type(std::move(type)), Id(std::move(id)) {} + Dependency(DependencyType type, string_t id) : Type(type), Id(std::move(id)) {} Dependency(DependencyType type) : Type(type) {} bool operator==(const Dependency& rhs) const { @@ -152,7 +152,7 @@ bool HasExtension(std::string_view extension) const; void Add(const Dependency& newDependency) { - Dependency* existingDependency = this->HasDependency(newDependency); + Dependency* existingDependency = HasDependency(newDependency); if (existingDependency != NULL) { if (newDependency.MinVersion) @@ -180,7 +180,7 @@ bool HasExtension(std::string_view extension) const; { for (const auto& dependency : otherDependencyList.dependencies) { - this->Add(dependency); + Add(dependency); } } diff --git a/src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp b/src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp new file mode 100644 index 0000000000..22bcb0b28f --- /dev/null +++ b/src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp @@ -0,0 +1,242 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "DependenciesFlow.h" +#include "ManifestComparator.h" +#include "InstallFlow.h" + + +namespace AppInstaller::CLI::Workflow +{ + using namespace AppInstaller::Repository; + using namespace Manifest; + + void ReportDependencies::operator()(Execution::Context& context) const + { + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + return; + } + auto info = context.Reporter.Info(); + + const auto& dependencies = context.Get(); + if (dependencies.HasAny()) + { + info << Resource::StringId(m_messageId) << std::endl; + + if (dependencies.HasAnyOf(DependencyType::WindowsFeature)) + { + info << " - " << Resource::String::WindowsFeaturesDependencies << std::endl; + dependencies.ApplyToType(DependencyType::WindowsFeature, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } + + if (dependencies.HasAnyOf(DependencyType::WindowsLibrary)) + { + info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; + dependencies.ApplyToType(DependencyType::WindowsLibrary, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } + + if (dependencies.HasAnyOf(DependencyType::Package)) + { + info << " - " << Resource::String::PackageDependencies << std::endl; + dependencies.ApplyToType(DependencyType::Package, [&info](Dependency dependency) + { + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value().ToString() << "]"; + info << std::endl; + }); + } + + if (dependencies.HasAnyOf(DependencyType::External)) + { + info << " - " << Resource::String::ExternalDependencies << std::endl; + dependencies.ApplyToType(DependencyType::External, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } + } + } + + void GetInstallersDependenciesFromManifest(Execution::Context& context) { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + const auto& manifest = context.Get(); + DependencyList allDependencies; + + for (const auto& installer : manifest.Installers) + { + allDependencies.Add(installer.Dependencies); + } + + context.Add(allDependencies); + } + } + + void GetDependenciesFromInstaller(Execution::Context& context) + { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + const auto& installer = context.Get(); + if (installer) + { + context.Add(installer->Dependencies); + } + } + } + + void GetDependenciesInfoForUninstall(Execution::Context& context) + { + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + // TODO make best effort to get the correct installer information, it may be better to have a record of installations and save the correct installers + context.Add(DependencyList()); // sending empty list of dependencies for now + } + } + + void OpenDependencySource(Execution::Context& context) + { + if (context.Contains(Execution::Data::PackageVersion)) + { + const auto& packageVersion = context.Get(); + context.Add(packageVersion->GetSource()); + context << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); + } + else + { // install from manifest requires --dependency-source to be set + context << + Workflow::OpenSource(true) << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); + } + } + + + void ManagePackageDependencies(Execution::Context& context) + { + auto info = context.Reporter.Info(); + const auto& rootManifest = context.Get(); + + Dependency rootAsDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); + + const auto& rootInstaller = context.Get(); + const auto& rootDependencies = rootInstaller->Dependencies; + + if (rootDependencies.Empty()) + { + // If there's no dependencies there's nothing to do aside of logging the outcome + return; + } + + context << OpenDependencySource; + if (!context.Contains(Execution::Data::DependencySource)) + { + info << "dependency source not found" << std::endl; //TODO localize message + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); // TODO create specific error code + } + + const auto& source = context.Get(); + std::map idToInstallerMap; + + DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, + [&](Dependency node) { + auto info = context.Reporter.Info(); + + SearchRequest searchRequest; + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, node.Id)); + //TODO add min version filter to search request ? + const auto& matches = source->Search(searchRequest).Matches; + + if (!matches.empty()) + { + if (matches.size() > 1) { + info << "Too many matches"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + const auto& match = matches.at(0); + + const auto& package = match.Package; + if (package->GetInstalledVersion() && node.IsVersionOk(package->GetInstalledVersion()->GetManifest().Version)) + { + return DependencyList(); //return empty dependency list, as we won't keep searching for dependencies for installed packages + } + else + { + const auto& latestVersion = package->GetLatestAvailableVersion(); + if (!latestVersion) { + info << "No package version found"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + + const auto& manifest = latestVersion->GetManifest(); + if (manifest.Installers.empty()) { + info << "No installers found"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + + if (!node.IsVersionOk(manifest.Version)) + { + info << "Minimum required version not available"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + + std::optional installer; + bool isUpdate = false; + if (package->GetInstalledVersion()) + { + installer = SelectInstallerFromMetadata(context.Args, manifest, package->GetInstalledVersion()->GetMetadata()); + isUpdate = true; + } + else + { + installer = SelectInstallerFromMetadata(context.Args, manifest, {}); + } + + const auto& nodeDependencies = installer->Dependencies; + + idToInstallerMap[node.Id] = {latestVersion, installer.value(), isUpdate}; + return nodeDependencies; + } + } + else + { + info << "No matches"; //TODO localize all errors + return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + } + }); + + dependencyGraph.BuildGraph(); // maybe it's better if it already does it on the constructor? + + if (dependencyGraph.HasLoop()) + { + info << "has loop" << std::endl; + Logging::Log().Write(Logging::Channel::CLI, Logging::Level::Warning, "Dependency loop found"); //TODO localization + //warn user but try to install either way + } + + // TODO raise error for failed packages? (if there's at least one) + + const auto& installationOrder = dependencyGraph.GetInstallationOrder(); + + std::vector installers; + + info << "order: "; //-- only for debugging + for (auto const& node : installationOrder) + { + info << node.Id << ", "; //-- only for debugging + + auto itr = idToInstallerMap.find(node.Id); + // if the package was already installed (with a useful version) or is the root + // then there will be no installer for it on the map. + if (itr != idToInstallerMap.end()) + { + installers.push_back(itr->second); + } + } + info << std::endl; //-- only for debugging + + // Install dependencies in the correct order + context.Add(installers); + context << InstallMultiple; + + // Install the root (continue) + } +} \ No newline at end of file diff --git a/src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h b/src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h new file mode 100644 index 0000000000..3173c444f2 --- /dev/null +++ b/src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h @@ -0,0 +1,421 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include +#include +#include +#include +#include +#include + +namespace AppInstaller::Manifest +{ + using string_t = Utility::NormalizedString; + using namespace std::string_view_literals; + + // The maximum supported major version known about by this code. + constexpr uint64_t s_MaxSupportedMajorVersion = 1; + + // The default manifest version assigned to manifests without a ManifestVersion field. + constexpr std::string_view s_DefaultManifestVersion = "0.1.0"sv; + + // V1 manifest version for GA + constexpr std::string_view s_ManifestVersionV1 = "1.0.0"sv; + + // The manifest extension for the MS Store + constexpr std::string_view s_MSStoreExtension = "msstore"sv; + + // ManifestVer is inherited from Utility::Version and is a more restricted version. + // ManifestVer is used to specify the version of app manifest itself. + // ManifestVer is a 3 part version in the format of [0-65535].[0-65535].[0-65535] + // and optionally a following tag in the format of -[SomeString] for experimental purpose. + struct ManifestVer : public Utility::Version + { + ManifestVer() = default; + + ManifestVer(std::string_view version); + + uint64_t Major() const { return m_parts.size() > 0 ? m_parts[0].Integer : 0; } + uint64_t Minor() const { return m_parts.size() > 1 ? m_parts[1].Integer : 0; } + uint64_t Patch() const { return m_parts.size() > 2 ? m_parts[2].Integer : 0; } + + bool HasExtension() const; + +bool HasExtension(std::string_view extension) const; + + private: + std::vector m_extensions; + }; + + enum class InstallerTypeEnum + { + Unknown, + Inno, + Wix, + Msi, + Nullsoft, + Zip, + Msix, + Exe, + Burn, + MSStore, + }; + + enum class UpdateBehaviorEnum + { + Unknown, + Install, + UninstallPrevious, + }; + + enum class InstallerSwitchType + { + Custom, + Silent, + SilentWithProgress, + Interactive, + Language, + Log, + InstallLocation, + Update, + }; + + enum class ScopeEnum + { + Unknown, + User, + Machine, + }; + + enum class InstallModeEnum + { + Unknown, + Interactive, + Silent, + SilentWithProgress, + }; + + enum class PlatformEnum + { + Unknown, + Universal, + Desktop, + }; + + enum class ManifestTypeEnum + { + Singleton, + Version, + Installer, + DefaultLocale, + Locale, + Merged, + Preview, + }; + + enum class DependencyType + { + WindowsFeature, + WindowsLibrary, + Package, + External + }; + + struct Dependency + { + DependencyType Type; + string_t Id; + std::optional MinVersion; + + Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), Id(std::move(id)), MinVersion(AppInstaller::Utility::Version(minVersion)) {} + Dependency(DependencyType type, string_t id) : Type(std::move(type)), Id(std::move(id)) {} + Dependency(DependencyType type) : Type(type) {} + + bool operator==(const Dependency& rhs) const { + return Type == rhs.Type && ICUCaseInsensitiveEquals(Id, rhs.Id); + } + + bool operator <(const Dependency& rhs) const + { + return Id < rhs.Id; + } + + bool IsVersionOk(string_t version) + { + return MinVersion <= AppInstaller::Utility::Version(version); + } + }; + + struct DependencyList + { + DependencyList() = default; + + void Add(const Dependency& newDependency) + { + Dependency* existingDependency = this->HasDependency(newDependency); + + if (existingDependency != NULL) { + if (newDependency.MinVersion) + { + if (existingDependency->MinVersion) + { + if (newDependency.MinVersion.value() > existingDependency->MinVersion.value()) + { + existingDependency->MinVersion.value() = newDependency.MinVersion.value(); + } + } + else + { + existingDependency->MinVersion.value() = newDependency.MinVersion.value(); + } + } + } + else + { + dependencies.push_back(newDependency); + } + } + + void Add(const DependencyList& otherDependencyList) + { + for (const auto& dependency : otherDependencyList.dependencies) + { + this->Add(dependency); + } + } + + bool HasAny() const { return !dependencies.empty(); } + bool HasAnyOf(DependencyType type) const + { + for (const auto& dependency : dependencies) + { + if (dependency.Type == type) return true; + }; + return false; + } + + Dependency* HasDependency(const Dependency& dependencyToSearch) + { + for (auto& dependency : dependencies) { + if (dependency == dependencyToSearch) + { + return &dependency; + } + } + return nullptr; + } + + void ApplyToType(DependencyType type, std::function func) const + { + for (const auto& dependency : dependencies) + { + if (dependency.Type == type) func(dependency); + } + } + + void ApplyToAll(std::function func) const + { + for (const auto& dependency : dependencies) + { + func(dependency); + } + } + + bool Empty() const + { + return dependencies.empty(); + } + + void Clear() { dependencies.clear(); } + + // for testing purposes + bool HasExactDependency(DependencyType type, string_t id, string_t minVersion = "") + { + for (const auto& dependency : dependencies) + { + if (dependency.Type == type && Utility::ICUCaseInsensitiveEquals(dependency.Id, id)) + { + if (dependency.MinVersion) { + if (dependency.MinVersion.value() == AppInstaller::Utility::Version(minVersion)) + { + return true; + } + } + else { + return true; + } + } + } + return false; + } + + size_t Size() + { + return dependencies.size(); + } + + private: + std::vector dependencies; + }; + + struct DependencyGraph + { + // this constructor was intented for use during installation flow (we already have installer dependencies and there's no need to search the source again) + DependencyGraph(const Dependency& root, const DependencyList& rootDependencies, + std::function infoFunction) : m_root(root), getDependencies(infoFunction) + { + adjacents[m_root] = std::vector(); + toCheck = std::vector(); + rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + toCheck.push_back(dependency); + AddNode(dependency); + AddAdjacent(root, dependency); + }); + } + + DependencyGraph(const Dependency& root, std::function infoFunction) : m_root(root), getDependencies(infoFunction) + { + adjacents[m_root] = std::vector(); + toCheck = std::vector(); + + const DependencyList& rootDependencies = getDependencies(root); + rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + toCheck.push_back(dependency); + AddNode(dependency); + AddAdjacent(root, dependency); + }); + } + + void BuildGraph() + { + if (toCheck.empty()) + { + return; + } + + for (int i = 0; i < toCheck.size(); ++i) + { + auto node = toCheck.at(i); + + const auto& nodeDependencies = getDependencies(node); + //TODO add error stream so we can report back + + nodeDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) + { + if (!HasNode(dependency)) + { + toCheck.push_back(dependency); + AddNode(dependency); + } + + AddAdjacent(node, dependency); + }); + } + + CheckForLoopsAndGetOrder(); + } + + void AddNode(const Dependency& node) + { + adjacents[node] = std::vector(); + } + + void AddAdjacent(const Dependency& node,const Dependency& adjacent) + { + adjacents[node].push_back(adjacent); + } + + bool HasNode(const Dependency& dependency) + { + auto search = adjacents.find(dependency); + return search != adjacents.end(); + } + + bool HasLoop() + { + return hasLoop; + } + + void CheckForLoopsAndGetOrder() + { + installationOrder = std::vector(); + std::set visited; + hasLoop = HasLoopDFS(visited, m_root); + } + + std::vector GetInstallationOrder() + { + return installationOrder; + } + + private: + // TODO make this function iterative + bool HasLoopDFS(std::set visited, const Dependency& node) + { + // Adding before checking for loops, to have an order even if a loop is present + if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) + { + installationOrder.push_back(node); + } + + visited.insert(node); + for (const auto& adjacent : adjacents.at(node)) + { + auto search = visited.find(adjacent); + if (search == visited.end()) // if not found + { + if (HasLoopDFS(visited, adjacent)) + { + return true; + } + } + else + { + return true; + } + } + + return false; + } + + const Dependency& m_root; + std::map> adjacents; //(?) value should be a set instead of a vector? + std::function getDependencies; + + bool hasLoop; + std::vector installationOrder; + + std::vector toCheck; + std::map failedPackages; + }; + + InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); + + UpdateBehaviorEnum ConvertToUpdateBehaviorEnum(const std::string& in); + + ScopeEnum ConvertToScopeEnum(std::string_view in); + + InstallModeEnum ConvertToInstallModeEnum(const std::string& in); + + PlatformEnum ConvertToPlatformEnum(const std::string& in); + + ManifestTypeEnum ConvertToManifestTypeEnum(const std::string& in); + + std::string_view InstallerTypeToString(InstallerTypeEnum installerType); + + std::string_view ScopeToString(ScopeEnum scope); + + // Gets a value indicating whether the given installer type uses the PackageFamilyName system reference. + bool DoesInstallerTypeUsePackageFamilyName(InstallerTypeEnum installerType); + + // Gets a value indicating whether the given installer type uses the ProductCode system reference. + bool DoesInstallerTypeUseProductCode(InstallerTypeEnum installerType); + + // Checks whether 2 installer types are compatible. E.g. inno and exe are update compatible + bool IsInstallerTypeCompatible(InstallerTypeEnum type1, InstallerTypeEnum type2); + + // Get a list of default switches for known installer types + std::map GetDefaultKnownSwitches(InstallerTypeEnum installerType); +} \ No newline at end of file diff --git a/src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp b/src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp new file mode 100644 index 0000000000..13b1425119 --- /dev/null +++ b/src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp @@ -0,0 +1,731 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "InstallFlow.h" +#include "UninstallFlow.h" +#include "Resources.h" +#include "ShellExecuteInstallerHandler.h" +#include "MSStoreInstallerHandler.h" +#include "WorkflowBase.h" +#include "DependenciesFlow.h" + +namespace AppInstaller::CLI::Workflow +{ + using namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl; + using namespace winrt::Windows::Foundation; + using namespace winrt::Windows::Foundation::Collections; + using namespace winrt::Windows::Management::Deployment; + using namespace AppInstaller::Utility; + using namespace AppInstaller::Manifest; + using namespace AppInstaller::Repository; + + namespace + { + bool MightWriteToARP(InstallerTypeEnum type) + { + switch (type) + { + case InstallerTypeEnum::Exe: + case InstallerTypeEnum::Burn: + case InstallerTypeEnum::Inno: + case InstallerTypeEnum::Msi: + case InstallerTypeEnum::Nullsoft: + case InstallerTypeEnum::Wix: + return true; + default: + return false; + } + } + } + + void EnsureApplicableInstaller(Execution::Context& context) + { + const auto& installer = context.Get(); + + if (!installer.has_value()) + { + context.Reporter.Error() << Resource::String::NoApplicableInstallers << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER); + } + } + + void ShowInstallationDisclaimer(Execution::Context& context) + { + auto installerType = context.Get().value().InstallerType; + + if (installerType == InstallerTypeEnum::MSStore) + { + context.Reporter.Info() << Resource::String::InstallationDisclaimerMSStore << std::endl; + } + else + { + context.Reporter.Info() << + Resource::String::InstallationDisclaimer1 << std::endl << + Resource::String::InstallationDisclaimer2 << std::endl; + } + } + + void DownloadInstaller(Execution::Context& context) + { + const auto& installer = context.Get().value(); + + switch (installer.InstallerType) + { + case InstallerTypeEnum::Exe: + case InstallerTypeEnum::Burn: + case InstallerTypeEnum::Inno: + case InstallerTypeEnum::Msi: + case InstallerTypeEnum::Nullsoft: + case InstallerTypeEnum::Wix: + context << DownloadInstallerFile << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; + break; + case InstallerTypeEnum::Msix: + if (installer.SignatureSha256.empty()) + { + context << DownloadInstallerFile << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; + } + else + { + // Signature hash provided. No download needed. Just verify signature hash. + context << GetMsixSignatureHash << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; + } + break; + case InstallerTypeEnum::MSStore: + // Nothing to do here + break; + default: + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + } + + void DownloadInstallerFile(Execution::Context& context) + { + const auto& manifest = context.Get(); + const auto& installer = context.Get().value(); + + std::filesystem::path tempInstallerPath = Runtime::GetPathTo(Runtime::PathName::Temp); + tempInstallerPath /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); + + Utility::DownloadInfo downloadInfo{}; + downloadInfo.DisplayName = Resource::GetFixedString(Resource::FixedString::ProductName); + // Use the SHA256 hash of the installer as the identifier for the download + downloadInfo.ContentId = SHA256::ConvertToString(installer.Sha256); + + AICLI_LOG(CLI, Info, << "Generated temp download path: " << tempInstallerPath); + + context.Reporter.Info() << "Downloading " << Execution::UrlEmphasis << installer.Url << std::endl; + + std::optional> hash; + + const int MaxRetryCount = 2; + for (int retryCount = 0; retryCount < MaxRetryCount; ++retryCount) + { + bool success = false; + try + { + hash = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, + installer.Url, + tempInstallerPath, + Utility::DownloadType::Installer, + std::placeholders::_1, + true, + downloadInfo)); + + success = true; + } + catch (...) + { + if (retryCount < MaxRetryCount - 1) + { + AICLI_LOG(CLI, Info, << "Failed to download, waiting a bit and retry. Url: " << installer.Url); + Sleep(500); + } + else + { + throw; + } + } + + if (success) + { + break; + } + } + + if (!hash) + { + context.Reporter.Info() << "Package download canceled." << std::endl; + AICLI_TERMINATE_CONTEXT(E_ABORT); + } + + context.Add(std::make_pair(installer.Sha256, hash.value())); + context.Add(std::move(tempInstallerPath)); + } + + void GetMsixSignatureHash(Execution::Context& context) + { + // We use this when the server won't support streaming install to swap to download. + bool downloadInstead = false; + + try + { + const auto& installer = context.Get().value(); + + Msix::MsixInfo msixInfo(installer.Url); + auto signature = msixInfo.GetSignature(); + + auto signatureHash = SHA256::ComputeHash(signature.data(), static_cast(signature.size())); + + context.Add(std::make_pair(installer.SignatureSha256, signatureHash)); + } + catch (const winrt::hresult_error& e) + { + if (static_cast(e.code()) == HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED) || + HRESULT_FACILITY(e.code()) == FACILITY_HTTP) + { + // Failed to get signature hash through HttpStream, use download + downloadInstead = true; + } + else + { + throw; + } + } + + if (downloadInstead) + { + context << DownloadInstallerFile; + } + } + + void VerifyInstallerHash(Execution::Context& context) + { + const auto& hashPair = context.Get(); + + if (!std::equal( + hashPair.first.begin(), + hashPair.first.end(), + hashPair.second.begin())) + { + bool overrideHashMismatch = context.Args.Contains(Execution::Args::Type::HashOverride); + + const auto& manifest = context.Get(); + Logging::Telemetry().LogInstallerHashMismatch(manifest.Id, manifest.Version, manifest.Channel, hashPair.first, hashPair.second, overrideHashMismatch); + + // If running as admin, do not allow the user to override the hash failure. + if (Runtime::IsRunningAsAdmin()) + { + context.Reporter.Error() << Resource::String::InstallerHashMismatchAdminBlock << std::endl; + } + else if (overrideHashMismatch) + { + context.Reporter.Warn() << Resource::String::InstallerHashMismatchOverridden << std::endl; + return; + } + else if (Settings::GroupPolicies().IsEnabled(Settings::TogglePolicy::Policy::HashOverride)) + { + context.Reporter.Error() << Resource::String::InstallerHashMismatchOverrideRequired << std::endl; + } + else + { + context.Reporter.Error() << Resource::String::InstallerHashMismatchError << std::endl; + } + + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH); + } + else + { + AICLI_LOG(CLI, Info, << "Installer hash verified"); + context.Reporter.Info() << Resource::String::InstallerHashVerified << std::endl; + + context.SetFlags(Execution::ContextFlag::InstallerHashMatched); + + if (context.Contains(Execution::Data::PackageVersion) && + context.Get()->GetSource() != nullptr && + WI_IsFlagSet(context.Get()->GetSource()->GetDetails().TrustLevel, SourceTrustLevel::Trusted)) + { + context.SetFlags(Execution::ContextFlag::InstallerTrusted); + } + } + } + + void UpdateInstallerFileMotwIfApplicable(Execution::Context& context) + { + if (context.Contains(Execution::Data::InstallerPath)) + { + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerTrusted)) + { + Utility::ApplyMotwIfApplicable(context.Get(), URLZONE_TRUSTED); + } + else if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerHashMatched)) + { + const auto& installer = context.Get(); + HRESULT hr = Utility::ApplyMotwUsingIAttachmentExecuteIfApplicable(context.Get(), installer.value().Url); + + // Not using SUCCEEDED(hr) to check since there are cases file is missing after a successful scan + if (hr != S_OK) + { + switch (hr) + { + case INET_E_SECURITY_PROBLEM: + context.Reporter.Error() << Resource::String::InstallerBlockedByPolicy << std::endl; + break; + case E_FAIL: + context.Reporter.Error() << Resource::String::InstallerFailedVirusScan << std::endl; + break; + default: + context.Reporter.Error() << Resource::String::InstallerFailedSecurityCheck << std::endl; + } + + AICLI_LOG(Fail, Error, << "Installer failed security check. Url: " << installer.value().Url << " Result: " << WINGET_OSTREAM_FORMAT_HRESULT(hr)); + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED); + } + } + } + } + + void ExecuteInstaller(Execution::Context& context) + { + const auto& installer = context.Get().value(); + + bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); + + switch (installer.InstallerType) + { + case InstallerTypeEnum::Exe: + case InstallerTypeEnum::Burn: + case InstallerTypeEnum::Inno: + case InstallerTypeEnum::Msi: + case InstallerTypeEnum::Nullsoft: + case InstallerTypeEnum::Wix: + if (isUpdate && installer.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious) + { + context << + GetUninstallInfo << + ExecuteUninstaller; + context.ClearFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); + } + context << ShellExecuteInstall; + break; + case InstallerTypeEnum::Msix: + context << MsixInstall; + break; + case InstallerTypeEnum::MSStore: + context << + EnsureFeatureEnabled(Settings::ExperimentalFeature::Feature::ExperimentalMSStore) << + EnsureStorePolicySatisfied << + (isUpdate ? MSStoreUpdate : MSStoreInstall); + break; + default: + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + } + + void ShellExecuteInstall(Execution::Context& context) + { + context << + GetInstallerArgs << + RenameDownloadedInstaller << + ShellExecuteInstallImpl; + } + + void MsixInstall(Execution::Context& context) + { + Uri uri = nullptr; + if (context.Contains(Execution::Data::InstallerPath)) + { + uri = Uri(context.Get().c_str()); + } + else + { + uri = Uri(Utility::ConvertToUTF16(context.Get()->Url)); + } + + context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; + + try + { + DeploymentOptions deploymentOptions = + DeploymentOptions::ForceApplicationShutdown | + DeploymentOptions::ForceTargetApplicationShutdown; + + context.Reporter.ExecuteWithProgress(std::bind(Deployment::AddPackage, uri, deploymentOptions, + WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerTrusted), std::placeholders::_1)); + } + catch (const wil::ResultException& re) + { + const auto& manifest = context.Get(); + Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, "MSIX", re.GetErrorCode()); + + context.Reporter.Error() << GetUserPresentableMessage(re) << std::endl; + AICLI_TERMINATE_CONTEXT(re.GetErrorCode()); + } + + context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + } + + void RemoveInstaller(Execution::Context& context) + { + // Path may not be present if installed from a URL for MSIX + if (context.Contains(Execution::Data::InstallerPath)) + { + const auto& path = context.Get(); + AICLI_LOG(CLI, Info, << "Removing installer: " << path); + + try + { + // best effort + std::filesystem::remove(path); + } + catch (const std::exception& e) + { + AICLI_LOG(CLI, Warning, << "Failed to remove installer file after execution. Reason: " << e.what()); + } + catch (...) + { + AICLI_LOG(CLI, Warning, << "Failed to remove installer file after execution. Reason unknown."); + } + } + } + + void ReportIdentityAndInstallationDisclaimer(Execution::Context& context) + { + context << + Workflow::ReportManifestIdentity << + Workflow::ShowInstallationDisclaimer; + } + + void InstallPackageInstaller(Execution::Context& context) + { + context << + Workflow::ReportExecutionStage(ExecutionStage::Download) << + Workflow::DownloadInstaller << + Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << + Workflow::SnapshotARPEntries << + Workflow::ReportExecutionStage(ExecutionStage::Execution) << + Workflow::ExecuteInstaller << + Workflow::ReportExecutionStage(ExecutionStage::PostExecution) << + Workflow::ReportARPChanges << + Workflow::RemoveInstaller; + } + + void InstallPackageVersion(Execution::Context& context) + { + context << + Workflow::SelectInstaller << + Workflow::EnsureApplicableInstaller << + Workflow::ReportIdentityAndInstallationDisclaimer << + Workflow::GetDependenciesFromInstaller << + Workflow::ManagePackageDependencies << + Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + Workflow::InstallPackageInstaller; + } + + void SelectInstallerMultiple(Execution::Context& context) + { + bool allSucceeded = true; + DependencyList allDependencies; + std::vector installers; + + for (auto package : context.Get()) + { + Logging::SubExecutionTelemetryScope subExecution; + + // We want to do best effort to install all packages regardless of previous failures + auto installContextPtr = context.Clone(); + Execution::Context& installContext = *installContextPtr; + + // Extract the data needed for installing + installContext.Add(package.PackageVersion); + installContext.Add(package.PackageVersion->GetManifest()); + + // TODO: In the future, it would be better to not have to convert back and forth from a string + installContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(package.PackageRequest.Scope)); + + installContext << + Workflow::SelectInstaller << + Workflow::EnsureApplicableInstaller; + + if (installContext.IsTerminated()) + { + allSucceeded = false; + continue; + } + + const auto& installer = installContext.Get(); + installers.push_back({package.PackageVersion, installer.value(), false}); + + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + allDependencies.Add(installer->Dependencies); + } + } + + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + context.Add(allDependencies); + context << Workflow::ReportDependencies(Resource::String::ImportCommandReportDependencies); + } + context.Add(installers); + context << InstallMultiple; + } + + void InstallMultiple(Execution::Context& context) + { + bool allSucceeded = true; + + const auto& installers = context.Get(); + + for (auto packageAndInstaller : installers) + { + auto packageVersion = packageAndInstaller.PackageVersion; + auto installer = packageAndInstaller.Installer; + + auto installContextPtr = context.Clone(); + Execution::Context& installContext = *installContextPtr; + + // set data needed for installing + installContext.Add(packageVersion); + installContext.Add(packageVersion->GetManifest()); + installContext.Add(installer); + + installContext << + ReportIdentityAndInstallationDisclaimer << + Workflow::InstallPackageInstaller; + + if (installContext.IsTerminated()) + { + if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) + { + // This means that the subcontext being terminated is due to an overall abort + context.Reporter.Info() << Resource::String::Cancelled << std::endl; + return; + } + + allSucceeded = false; + } + } + + if (!allSucceeded) + { + context.Reporter.Error() << Resource::String::ImportInstallFailed << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); + } + } + + void SnapshotARPEntries(Execution::Context& context) try + { + // Ensure that installer type might actually write to ARP, otherwise this is a waste of time + auto installer = context.Get(); + + if (installer && MightWriteToARP(installer->InstallerType)) + { + std::shared_ptr arpSource = context.Reporter.ExecuteWithProgress( + [](IProgressCallback& progress) + { + return Repository::OpenPredefinedSource(PredefinedSource::ARP, progress); + }, true); + + std::vector> entries; + + for (const auto& entry : arpSource->Search({}).Matches) + { + auto installed = entry.Package->GetInstalledVersion(); + if (installed) + { + entries.emplace_back(std::make_tuple( + entry.Package->GetProperty(PackageProperty::Id), + installed->GetProperty(PackageVersionProperty::Version), + installed->GetProperty(PackageVersionProperty::Channel))); + } + } + + std::sort(entries.begin(), entries.end()); + + context.Add(std::move(entries)); + } + } + CATCH_LOG() + + void ReportARPChanges(Execution::Context& context) try + { + if (context.Contains(Execution::Data::ARPSnapshot)) + { + const auto& entries = context.Get(); + + // Open it again to get the (potentially) changed ARP entries + std::shared_ptr arpSource = context.Reporter.ExecuteWithProgress( + [](IProgressCallback& progress) + { + return Repository::OpenPredefinedSource(PredefinedSource::ARP, progress); + }, true); + + std::vector changes; + + for (auto& entry : arpSource->Search({}).Matches) + { + auto installed = entry.Package->GetInstalledVersion(); + + if (installed) + { + auto entryKey = std::make_tuple( + entry.Package->GetProperty(PackageProperty::Id), + installed->GetProperty(PackageVersionProperty::Version), + installed->GetProperty(PackageVersionProperty::Channel)); + + auto itr = std::lower_bound(entries.begin(), entries.end(), entryKey); + if (itr == entries.end() || *itr != entryKey) + { + changes.emplace_back(std::move(entry)); + } + } + } + + // Also attempt to find the entry based on the manifest data + const auto& manifest = context.Get(); + + SearchRequest nameAndPublisherRequest; + + // The default localization must contain the name or we cannot do this lookup + if (manifest.DefaultLocalization.Contains(Localization::PackageName)) + { + AppInstaller::Manifest::Manifest::string_t defaultName = manifest.DefaultLocalization.Get(); + AppInstaller::Manifest::Manifest::string_t defaultPublisher; + if (manifest.DefaultLocalization.Contains(Localization::Publisher)) + { + defaultPublisher = manifest.DefaultLocalization.Get(); + } + + nameAndPublisherRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, defaultName, defaultPublisher)); + + for (const auto& loc : manifest.Localizations) + { + if (loc.Contains(Localization::PackageName) || loc.Contains(Localization::Publisher)) + { + nameAndPublisherRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, + loc.Contains(Localization::PackageName) ? loc.Get() : defaultName, + loc.Contains(Localization::Publisher) ? loc.Get() : defaultPublisher)); + } + } + } + + std::vector productCodes; + for (const auto& installer : manifest.Installers) + { + if (!installer.ProductCode.empty()) + { + if (std::find(productCodes.begin(), productCodes.end(), installer.ProductCode) == productCodes.end()) + { + nameAndPublisherRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, installer.ProductCode)); + productCodes.emplace_back(installer.ProductCode); + } + } + } + + SearchResult findByManifest; + + // Don't execute this search if it would just find everything + if (!nameAndPublisherRequest.IsForEverything()) + { + findByManifest = arpSource->Search(nameAndPublisherRequest); + } + + // Cross reference the changes with the search results + std::vector> packagesInBoth; + + for (const auto& change : changes) + { + for (const auto& byManifest : findByManifest.Matches) + { + if (change.Package->IsSame(byManifest.Package.get())) + { + packagesInBoth.emplace_back(change.Package); + break; + } + } + } + + // We now have all of the package changes; time to report them. + // The set of cases we could have for changes to ARP: + // 0 packages :: No changes were detected to ARP, which could mean that the installer + // did not write an entry. It could also be a forced reinstall. + // 1 package :: Golden path; this should be what we installed. + // 2+ packages :: We need to determine which package actually matches the one that we + // were installing. + // + // The set of cases we could have for finding packages based on the manifest: + // 0 packages :: The manifest data does not match the ARP information. + // 1 package :: Golden path; this should be what we installed. + // 2+ packages :: The data in the manifest is either too broad or we have + // a problem with our name normalization. + // + // ARP Package changes + // 0 1 N + // +------------------+--------------------+--------------------+ + // M | | | | + // a | Package does not | Manifest data does | Manifest data does | + // n 0 | write to ARP | not match ARP | not match ARP | + // i | Log this fact | Log for fixup | Log for fixup | + // f | | | | + // e +------------------+--------------------+--------------------+ + // s | | | | + // t | Reinstall of | Golden Path! | Treat manifest as | + // 1 | existing version | (assuming match) | main if common | + // r | | | | + // e +------------------+--------------------+--------------------+ + // s | | | | + // u | Not expected | Treat ARP as main | Not expected | + // l N | Log this for | | Log this for | + // t | investigation | | investigation | + // s | | | | + // +------------------+--------------------+--------------------+ + + // Find the package that we are going to log + std::shared_ptr toLog; + + // If no changes found, only log if a single matching package was found by the manifest + if (changes.empty() && findByManifest.Matches.size() == 1) + { + toLog = findByManifest.Matches[0].Package->GetInstalledVersion(); + } + // If only a single ARP entry was changed, always log that + else if (changes.size() == 1) + { + toLog = changes[0].Package->GetInstalledVersion(); + } + // Finally, if there is only a single common package, log that one + else if (packagesInBoth.size() == 1) + { + toLog = packagesInBoth[0]->GetInstalledVersion(); + } + + IPackageVersion::Metadata toLogMetadata; + if (toLog) + { + toLogMetadata = toLog->GetMetadata(); + } + + // We can only get the source identifier from an active source + std::string sourceIdentifier; + if (context.Contains(Execution::Data::PackageVersion)) + { + sourceIdentifier = context.Get()->GetProperty(PackageVersionProperty::SourceIdentifier); + } + + Logging::Telemetry().LogSuccessfulInstallARPChange( + sourceIdentifier, + manifest.Id, + manifest.Version, + manifest.Channel, + changes.size(), + findByManifest.Matches.size(), + packagesInBoth.size(), + toLog ? static_cast(toLog->GetProperty(PackageVersionProperty::Name)) : "", + toLog ? static_cast(toLog->GetProperty(PackageVersionProperty::Version)) : "", + toLog ? static_cast(toLogMetadata[PackageVersionMetadata::Publisher]) : "", + toLog ? static_cast(toLogMetadata[PackageVersionMetadata::InstalledLocale]) : "" + ); + } + } + CATCH_LOG() +} From 48fbb52f96b72242a6d4dd4fd53911a04841843e Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 29 Jul 2021 17:58:53 -0300 Subject: [PATCH 73/82] dependency graph + installation, all tests passed --- .../Workflows/WorkflowBase.cpp | 2 +- src/AppInstallerCLITests/WorkFlow.cpp | 35 +++++++++++++++---- .../Public/winget/ManifestCommon.h | 20 ++++++----- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index f4109ad169..b07bdd2e3a 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -146,7 +146,7 @@ namespace AppInstaller::CLI::Workflow std::string_view sourceName; if (m_forDependencies) { - if (m_forDependencies && context.Args.Contains(Execution::Args::Type::DependencySource)) + if (context.Args.Contains(Execution::Args::Type::DependencySource)) { sourceName = context.Args.GetArg(Execution::Args::Type::DependencySource); } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index f6961a7536..a37f0c84f7 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -469,12 +470,25 @@ void OverrideForImportSource(TestContext& context) } }); } -void OverrideForDependencySource(TestContext& context) +void OverrideOpenSourceForDependencies(TestContext& context) { context.Override({ "OpenSource", [](TestContext& context) { context.Add(std::make_shared()); } }); + + context.Override({ Workflow::OpenDependencySource, [](TestContext& context) + { + context.Add(std::make_shared()); + } }); +} + +void OverrideDependencySource(TestContext& context) +{ + context.Override({ Workflow::OpenDependencySource, [](TestContext& context) + { + context.Add(std::make_shared()); + } }); } void OverrideForUpdateInstallerMotw(TestContext& context) @@ -1753,6 +1767,7 @@ TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); + OverrideDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); @@ -1774,7 +1789,7 @@ TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph][depe std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideForDependencySource(context); + OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "EasyToSeeLoop"sv); @@ -1795,7 +1810,7 @@ TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGr std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideForDependencySource(context); + OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "DependencyAlreadyInStackButNoLoop"sv); @@ -1817,7 +1832,7 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideForDependencySource(context); + OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "PathBetweenBranchesButNoLoop"sv); @@ -1840,7 +1855,7 @@ TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyG std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideForDependencySource(context); + OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); @@ -1863,7 +1878,7 @@ TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][de std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideForDependencySource(context); + OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, "NeedsToInstallBFirst"sv); @@ -1911,6 +1926,7 @@ TEST_CASE("DependenciesMultideclaration_InstallerDependenciesPreference", "[depe std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); + OverrideDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesMultideclaration.yaml").GetPath().u8string()); @@ -1935,6 +1951,7 @@ TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); + OverrideDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesOnRoot.yaml").GetPath().u8string()); @@ -1948,4 +1965,8 @@ TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies // Verify root dependencies are shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("PreviewIISOnRoot") != std::string::npos); -} \ No newline at end of file +} +// TODO +// add dependencies for installer tests to DependenciesTestSource (or a new one) +// add tests for min version dependency solving +// add tests that check for correct installation of dependencies (not only the order) \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 83bcde2eca..8d940fb59b 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -354,11 +354,7 @@ bool HasExtension(std::string_view extension) const; // TODO make this function iterative bool HasLoopDFS(std::set visited, const Dependency& node) { - // Adding before checking for loops, to have an order even if a loop is present - if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) - { - installationOrder.push_back(node); - } + bool loop = false; visited.insert(node); for (const auto& adjacent : adjacents.at(node)) @@ -368,16 +364,24 @@ bool HasExtension(std::string_view extension) const; { if (HasLoopDFS(visited, adjacent)) { - return true; + loop = true; + // didn't break the loop to have a complete order at the end (even if a loop exists) } } else { - return true; + loop = true; + // didn't break the loop to have a complete order at the end (even if a loop exists) } } - return false; + // Adding to have an order even if a loop is present + if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) + { + installationOrder.push_back(node); + } + + return loop; } const Dependency& m_root; From 43099edd818b19ce07bdc92824b167ac1b5ed4b1 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 29 Jul 2021 18:42:00 -0300 Subject: [PATCH 74/82] information detail, delete temp folder --- .../Workflows/DependenciesFlow.cpp | 2 + .../Workflows/InstallFlow.cpp | 2 +- src/AppInstallerCLITests/WorkFlow.cpp | 1 + .../DependenciesFlow.cpp | 242 ------ .../ManifestCommon.h | 421 ---------- .../InstallFlow.cpp | 731 ------------------ 6 files changed, 4 insertions(+), 1395 deletions(-) delete mode 100644 src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp delete mode 100644 src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h delete mode 100644 src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 070672eb61..6785415356 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -126,6 +126,8 @@ namespace AppInstaller::CLI::Workflow return; } + info << "Installing dependencies:" << std::endl; //TODO localize message + context << OpenDependencySource; if (!context.Contains(Execution::Data::DependencySource)) { diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 15f462ab07..6248ff72fa 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -416,8 +416,8 @@ namespace AppInstaller::CLI::Workflow Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::GetDependenciesFromInstaller << - Workflow::ManagePackageDependencies << Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + Workflow::ManagePackageDependencies << Workflow::InstallPackageInstaller; } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index a37f0c84f7..4d61222ee6 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -265,6 +265,7 @@ namespace // TODO maybe change package name on default locale for better debugging auto& installer = manifest.Installers.at(0); + installer.ProductId = input; installer.Dependencies.Clear(); /* diff --git a/src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp b/src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp deleted file mode 100644 index 22bcb0b28f..0000000000 --- a/src/enc_temp_folder/1c45dac4c6c8f36dc14a122fd6f7e/DependenciesFlow.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "DependenciesFlow.h" -#include "ManifestComparator.h" -#include "InstallFlow.h" - - -namespace AppInstaller::CLI::Workflow -{ - using namespace AppInstaller::Repository; - using namespace Manifest; - - void ReportDependencies::operator()(Execution::Context& context) const - { - if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - return; - } - auto info = context.Reporter.Info(); - - const auto& dependencies = context.Get(); - if (dependencies.HasAny()) - { - info << Resource::StringId(m_messageId) << std::endl; - - if (dependencies.HasAnyOf(DependencyType::WindowsFeature)) - { - info << " - " << Resource::String::WindowsFeaturesDependencies << std::endl; - dependencies.ApplyToType(DependencyType::WindowsFeature, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); - } - - if (dependencies.HasAnyOf(DependencyType::WindowsLibrary)) - { - info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; - dependencies.ApplyToType(DependencyType::WindowsLibrary, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); - } - - if (dependencies.HasAnyOf(DependencyType::Package)) - { - info << " - " << Resource::String::PackageDependencies << std::endl; - dependencies.ApplyToType(DependencyType::Package, [&info](Dependency dependency) - { - info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value().ToString() << "]"; - info << std::endl; - }); - } - - if (dependencies.HasAnyOf(DependencyType::External)) - { - info << " - " << Resource::String::ExternalDependencies << std::endl; - dependencies.ApplyToType(DependencyType::External, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); - } - } - } - - void GetInstallersDependenciesFromManifest(Execution::Context& context) { - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - const auto& manifest = context.Get(); - DependencyList allDependencies; - - for (const auto& installer : manifest.Installers) - { - allDependencies.Add(installer.Dependencies); - } - - context.Add(allDependencies); - } - } - - void GetDependenciesFromInstaller(Execution::Context& context) - { - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - const auto& installer = context.Get(); - if (installer) - { - context.Add(installer->Dependencies); - } - } - } - - void GetDependenciesInfoForUninstall(Execution::Context& context) - { - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - // TODO make best effort to get the correct installer information, it may be better to have a record of installations and save the correct installers - context.Add(DependencyList()); // sending empty list of dependencies for now - } - } - - void OpenDependencySource(Execution::Context& context) - { - if (context.Contains(Execution::Data::PackageVersion)) - { - const auto& packageVersion = context.Get(); - context.Add(packageVersion->GetSource()); - context << - Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); - } - else - { // install from manifest requires --dependency-source to be set - context << - Workflow::OpenSource(true) << - Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); - } - } - - - void ManagePackageDependencies(Execution::Context& context) - { - auto info = context.Reporter.Info(); - const auto& rootManifest = context.Get(); - - Dependency rootAsDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); - - const auto& rootInstaller = context.Get(); - const auto& rootDependencies = rootInstaller->Dependencies; - - if (rootDependencies.Empty()) - { - // If there's no dependencies there's nothing to do aside of logging the outcome - return; - } - - context << OpenDependencySource; - if (!context.Contains(Execution::Data::DependencySource)) - { - info << "dependency source not found" << std::endl; //TODO localize message - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); // TODO create specific error code - } - - const auto& source = context.Get(); - std::map idToInstallerMap; - - DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, - [&](Dependency node) { - auto info = context.Reporter.Info(); - - SearchRequest searchRequest; - searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, node.Id)); - //TODO add min version filter to search request ? - const auto& matches = source->Search(searchRequest).Matches; - - if (!matches.empty()) - { - if (matches.size() > 1) { - info << "Too many matches"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - const auto& match = matches.at(0); - - const auto& package = match.Package; - if (package->GetInstalledVersion() && node.IsVersionOk(package->GetInstalledVersion()->GetManifest().Version)) - { - return DependencyList(); //return empty dependency list, as we won't keep searching for dependencies for installed packages - } - else - { - const auto& latestVersion = package->GetLatestAvailableVersion(); - if (!latestVersion) { - info << "No package version found"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - - const auto& manifest = latestVersion->GetManifest(); - if (manifest.Installers.empty()) { - info << "No installers found"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - - if (!node.IsVersionOk(manifest.Version)) - { - info << "Minimum required version not available"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - - std::optional installer; - bool isUpdate = false; - if (package->GetInstalledVersion()) - { - installer = SelectInstallerFromMetadata(context.Args, manifest, package->GetInstalledVersion()->GetMetadata()); - isUpdate = true; - } - else - { - installer = SelectInstallerFromMetadata(context.Args, manifest, {}); - } - - const auto& nodeDependencies = installer->Dependencies; - - idToInstallerMap[node.Id] = {latestVersion, installer.value(), isUpdate}; - return nodeDependencies; - } - } - else - { - info << "No matches"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - }); - - dependencyGraph.BuildGraph(); // maybe it's better if it already does it on the constructor? - - if (dependencyGraph.HasLoop()) - { - info << "has loop" << std::endl; - Logging::Log().Write(Logging::Channel::CLI, Logging::Level::Warning, "Dependency loop found"); //TODO localization - //warn user but try to install either way - } - - // TODO raise error for failed packages? (if there's at least one) - - const auto& installationOrder = dependencyGraph.GetInstallationOrder(); - - std::vector installers; - - info << "order: "; //-- only for debugging - for (auto const& node : installationOrder) - { - info << node.Id << ", "; //-- only for debugging - - auto itr = idToInstallerMap.find(node.Id); - // if the package was already installed (with a useful version) or is the root - // then there will be no installer for it on the map. - if (itr != idToInstallerMap.end()) - { - installers.push_back(itr->second); - } - } - info << std::endl; //-- only for debugging - - // Install dependencies in the correct order - context.Add(installers); - context << InstallMultiple; - - // Install the root (continue) - } -} \ No newline at end of file diff --git a/src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h b/src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h deleted file mode 100644 index 3173c444f2..0000000000 --- a/src/enc_temp_folder/473be0fecfa66ed67c7cbecefcb3030/ManifestCommon.h +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include -#include -#include -#include -#include -#include - -namespace AppInstaller::Manifest -{ - using string_t = Utility::NormalizedString; - using namespace std::string_view_literals; - - // The maximum supported major version known about by this code. - constexpr uint64_t s_MaxSupportedMajorVersion = 1; - - // The default manifest version assigned to manifests without a ManifestVersion field. - constexpr std::string_view s_DefaultManifestVersion = "0.1.0"sv; - - // V1 manifest version for GA - constexpr std::string_view s_ManifestVersionV1 = "1.0.0"sv; - - // The manifest extension for the MS Store - constexpr std::string_view s_MSStoreExtension = "msstore"sv; - - // ManifestVer is inherited from Utility::Version and is a more restricted version. - // ManifestVer is used to specify the version of app manifest itself. - // ManifestVer is a 3 part version in the format of [0-65535].[0-65535].[0-65535] - // and optionally a following tag in the format of -[SomeString] for experimental purpose. - struct ManifestVer : public Utility::Version - { - ManifestVer() = default; - - ManifestVer(std::string_view version); - - uint64_t Major() const { return m_parts.size() > 0 ? m_parts[0].Integer : 0; } - uint64_t Minor() const { return m_parts.size() > 1 ? m_parts[1].Integer : 0; } - uint64_t Patch() const { return m_parts.size() > 2 ? m_parts[2].Integer : 0; } - - bool HasExtension() const; - -bool HasExtension(std::string_view extension) const; - - private: - std::vector m_extensions; - }; - - enum class InstallerTypeEnum - { - Unknown, - Inno, - Wix, - Msi, - Nullsoft, - Zip, - Msix, - Exe, - Burn, - MSStore, - }; - - enum class UpdateBehaviorEnum - { - Unknown, - Install, - UninstallPrevious, - }; - - enum class InstallerSwitchType - { - Custom, - Silent, - SilentWithProgress, - Interactive, - Language, - Log, - InstallLocation, - Update, - }; - - enum class ScopeEnum - { - Unknown, - User, - Machine, - }; - - enum class InstallModeEnum - { - Unknown, - Interactive, - Silent, - SilentWithProgress, - }; - - enum class PlatformEnum - { - Unknown, - Universal, - Desktop, - }; - - enum class ManifestTypeEnum - { - Singleton, - Version, - Installer, - DefaultLocale, - Locale, - Merged, - Preview, - }; - - enum class DependencyType - { - WindowsFeature, - WindowsLibrary, - Package, - External - }; - - struct Dependency - { - DependencyType Type; - string_t Id; - std::optional MinVersion; - - Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), Id(std::move(id)), MinVersion(AppInstaller::Utility::Version(minVersion)) {} - Dependency(DependencyType type, string_t id) : Type(std::move(type)), Id(std::move(id)) {} - Dependency(DependencyType type) : Type(type) {} - - bool operator==(const Dependency& rhs) const { - return Type == rhs.Type && ICUCaseInsensitiveEquals(Id, rhs.Id); - } - - bool operator <(const Dependency& rhs) const - { - return Id < rhs.Id; - } - - bool IsVersionOk(string_t version) - { - return MinVersion <= AppInstaller::Utility::Version(version); - } - }; - - struct DependencyList - { - DependencyList() = default; - - void Add(const Dependency& newDependency) - { - Dependency* existingDependency = this->HasDependency(newDependency); - - if (existingDependency != NULL) { - if (newDependency.MinVersion) - { - if (existingDependency->MinVersion) - { - if (newDependency.MinVersion.value() > existingDependency->MinVersion.value()) - { - existingDependency->MinVersion.value() = newDependency.MinVersion.value(); - } - } - else - { - existingDependency->MinVersion.value() = newDependency.MinVersion.value(); - } - } - } - else - { - dependencies.push_back(newDependency); - } - } - - void Add(const DependencyList& otherDependencyList) - { - for (const auto& dependency : otherDependencyList.dependencies) - { - this->Add(dependency); - } - } - - bool HasAny() const { return !dependencies.empty(); } - bool HasAnyOf(DependencyType type) const - { - for (const auto& dependency : dependencies) - { - if (dependency.Type == type) return true; - }; - return false; - } - - Dependency* HasDependency(const Dependency& dependencyToSearch) - { - for (auto& dependency : dependencies) { - if (dependency == dependencyToSearch) - { - return &dependency; - } - } - return nullptr; - } - - void ApplyToType(DependencyType type, std::function func) const - { - for (const auto& dependency : dependencies) - { - if (dependency.Type == type) func(dependency); - } - } - - void ApplyToAll(std::function func) const - { - for (const auto& dependency : dependencies) - { - func(dependency); - } - } - - bool Empty() const - { - return dependencies.empty(); - } - - void Clear() { dependencies.clear(); } - - // for testing purposes - bool HasExactDependency(DependencyType type, string_t id, string_t minVersion = "") - { - for (const auto& dependency : dependencies) - { - if (dependency.Type == type && Utility::ICUCaseInsensitiveEquals(dependency.Id, id)) - { - if (dependency.MinVersion) { - if (dependency.MinVersion.value() == AppInstaller::Utility::Version(minVersion)) - { - return true; - } - } - else { - return true; - } - } - } - return false; - } - - size_t Size() - { - return dependencies.size(); - } - - private: - std::vector dependencies; - }; - - struct DependencyGraph - { - // this constructor was intented for use during installation flow (we already have installer dependencies and there's no need to search the source again) - DependencyGraph(const Dependency& root, const DependencyList& rootDependencies, - std::function infoFunction) : m_root(root), getDependencies(infoFunction) - { - adjacents[m_root] = std::vector(); - toCheck = std::vector(); - rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) - { - toCheck.push_back(dependency); - AddNode(dependency); - AddAdjacent(root, dependency); - }); - } - - DependencyGraph(const Dependency& root, std::function infoFunction) : m_root(root), getDependencies(infoFunction) - { - adjacents[m_root] = std::vector(); - toCheck = std::vector(); - - const DependencyList& rootDependencies = getDependencies(root); - rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) - { - toCheck.push_back(dependency); - AddNode(dependency); - AddAdjacent(root, dependency); - }); - } - - void BuildGraph() - { - if (toCheck.empty()) - { - return; - } - - for (int i = 0; i < toCheck.size(); ++i) - { - auto node = toCheck.at(i); - - const auto& nodeDependencies = getDependencies(node); - //TODO add error stream so we can report back - - nodeDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) - { - if (!HasNode(dependency)) - { - toCheck.push_back(dependency); - AddNode(dependency); - } - - AddAdjacent(node, dependency); - }); - } - - CheckForLoopsAndGetOrder(); - } - - void AddNode(const Dependency& node) - { - adjacents[node] = std::vector(); - } - - void AddAdjacent(const Dependency& node,const Dependency& adjacent) - { - adjacents[node].push_back(adjacent); - } - - bool HasNode(const Dependency& dependency) - { - auto search = adjacents.find(dependency); - return search != adjacents.end(); - } - - bool HasLoop() - { - return hasLoop; - } - - void CheckForLoopsAndGetOrder() - { - installationOrder = std::vector(); - std::set visited; - hasLoop = HasLoopDFS(visited, m_root); - } - - std::vector GetInstallationOrder() - { - return installationOrder; - } - - private: - // TODO make this function iterative - bool HasLoopDFS(std::set visited, const Dependency& node) - { - // Adding before checking for loops, to have an order even if a loop is present - if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) - { - installationOrder.push_back(node); - } - - visited.insert(node); - for (const auto& adjacent : adjacents.at(node)) - { - auto search = visited.find(adjacent); - if (search == visited.end()) // if not found - { - if (HasLoopDFS(visited, adjacent)) - { - return true; - } - } - else - { - return true; - } - } - - return false; - } - - const Dependency& m_root; - std::map> adjacents; //(?) value should be a set instead of a vector? - std::function getDependencies; - - bool hasLoop; - std::vector installationOrder; - - std::vector toCheck; - std::map failedPackages; - }; - - InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); - - UpdateBehaviorEnum ConvertToUpdateBehaviorEnum(const std::string& in); - - ScopeEnum ConvertToScopeEnum(std::string_view in); - - InstallModeEnum ConvertToInstallModeEnum(const std::string& in); - - PlatformEnum ConvertToPlatformEnum(const std::string& in); - - ManifestTypeEnum ConvertToManifestTypeEnum(const std::string& in); - - std::string_view InstallerTypeToString(InstallerTypeEnum installerType); - - std::string_view ScopeToString(ScopeEnum scope); - - // Gets a value indicating whether the given installer type uses the PackageFamilyName system reference. - bool DoesInstallerTypeUsePackageFamilyName(InstallerTypeEnum installerType); - - // Gets a value indicating whether the given installer type uses the ProductCode system reference. - bool DoesInstallerTypeUseProductCode(InstallerTypeEnum installerType); - - // Checks whether 2 installer types are compatible. E.g. inno and exe are update compatible - bool IsInstallerTypeCompatible(InstallerTypeEnum type1, InstallerTypeEnum type2); - - // Get a list of default switches for known installer types - std::map GetDefaultKnownSwitches(InstallerTypeEnum installerType); -} \ No newline at end of file diff --git a/src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp b/src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp deleted file mode 100644 index 13b1425119..0000000000 --- a/src/enc_temp_folder/e3c44113fafe4b1a4257475e59e387fc/InstallFlow.cpp +++ /dev/null @@ -1,731 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "InstallFlow.h" -#include "UninstallFlow.h" -#include "Resources.h" -#include "ShellExecuteInstallerHandler.h" -#include "MSStoreInstallerHandler.h" -#include "WorkflowBase.h" -#include "DependenciesFlow.h" - -namespace AppInstaller::CLI::Workflow -{ - using namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl; - using namespace winrt::Windows::Foundation; - using namespace winrt::Windows::Foundation::Collections; - using namespace winrt::Windows::Management::Deployment; - using namespace AppInstaller::Utility; - using namespace AppInstaller::Manifest; - using namespace AppInstaller::Repository; - - namespace - { - bool MightWriteToARP(InstallerTypeEnum type) - { - switch (type) - { - case InstallerTypeEnum::Exe: - case InstallerTypeEnum::Burn: - case InstallerTypeEnum::Inno: - case InstallerTypeEnum::Msi: - case InstallerTypeEnum::Nullsoft: - case InstallerTypeEnum::Wix: - return true; - default: - return false; - } - } - } - - void EnsureApplicableInstaller(Execution::Context& context) - { - const auto& installer = context.Get(); - - if (!installer.has_value()) - { - context.Reporter.Error() << Resource::String::NoApplicableInstallers << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER); - } - } - - void ShowInstallationDisclaimer(Execution::Context& context) - { - auto installerType = context.Get().value().InstallerType; - - if (installerType == InstallerTypeEnum::MSStore) - { - context.Reporter.Info() << Resource::String::InstallationDisclaimerMSStore << std::endl; - } - else - { - context.Reporter.Info() << - Resource::String::InstallationDisclaimer1 << std::endl << - Resource::String::InstallationDisclaimer2 << std::endl; - } - } - - void DownloadInstaller(Execution::Context& context) - { - const auto& installer = context.Get().value(); - - switch (installer.InstallerType) - { - case InstallerTypeEnum::Exe: - case InstallerTypeEnum::Burn: - case InstallerTypeEnum::Inno: - case InstallerTypeEnum::Msi: - case InstallerTypeEnum::Nullsoft: - case InstallerTypeEnum::Wix: - context << DownloadInstallerFile << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; - break; - case InstallerTypeEnum::Msix: - if (installer.SignatureSha256.empty()) - { - context << DownloadInstallerFile << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; - } - else - { - // Signature hash provided. No download needed. Just verify signature hash. - context << GetMsixSignatureHash << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; - } - break; - case InstallerTypeEnum::MSStore: - // Nothing to do here - break; - default: - THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); - } - } - - void DownloadInstallerFile(Execution::Context& context) - { - const auto& manifest = context.Get(); - const auto& installer = context.Get().value(); - - std::filesystem::path tempInstallerPath = Runtime::GetPathTo(Runtime::PathName::Temp); - tempInstallerPath /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); - - Utility::DownloadInfo downloadInfo{}; - downloadInfo.DisplayName = Resource::GetFixedString(Resource::FixedString::ProductName); - // Use the SHA256 hash of the installer as the identifier for the download - downloadInfo.ContentId = SHA256::ConvertToString(installer.Sha256); - - AICLI_LOG(CLI, Info, << "Generated temp download path: " << tempInstallerPath); - - context.Reporter.Info() << "Downloading " << Execution::UrlEmphasis << installer.Url << std::endl; - - std::optional> hash; - - const int MaxRetryCount = 2; - for (int retryCount = 0; retryCount < MaxRetryCount; ++retryCount) - { - bool success = false; - try - { - hash = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, - installer.Url, - tempInstallerPath, - Utility::DownloadType::Installer, - std::placeholders::_1, - true, - downloadInfo)); - - success = true; - } - catch (...) - { - if (retryCount < MaxRetryCount - 1) - { - AICLI_LOG(CLI, Info, << "Failed to download, waiting a bit and retry. Url: " << installer.Url); - Sleep(500); - } - else - { - throw; - } - } - - if (success) - { - break; - } - } - - if (!hash) - { - context.Reporter.Info() << "Package download canceled." << std::endl; - AICLI_TERMINATE_CONTEXT(E_ABORT); - } - - context.Add(std::make_pair(installer.Sha256, hash.value())); - context.Add(std::move(tempInstallerPath)); - } - - void GetMsixSignatureHash(Execution::Context& context) - { - // We use this when the server won't support streaming install to swap to download. - bool downloadInstead = false; - - try - { - const auto& installer = context.Get().value(); - - Msix::MsixInfo msixInfo(installer.Url); - auto signature = msixInfo.GetSignature(); - - auto signatureHash = SHA256::ComputeHash(signature.data(), static_cast(signature.size())); - - context.Add(std::make_pair(installer.SignatureSha256, signatureHash)); - } - catch (const winrt::hresult_error& e) - { - if (static_cast(e.code()) == HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED) || - HRESULT_FACILITY(e.code()) == FACILITY_HTTP) - { - // Failed to get signature hash through HttpStream, use download - downloadInstead = true; - } - else - { - throw; - } - } - - if (downloadInstead) - { - context << DownloadInstallerFile; - } - } - - void VerifyInstallerHash(Execution::Context& context) - { - const auto& hashPair = context.Get(); - - if (!std::equal( - hashPair.first.begin(), - hashPair.first.end(), - hashPair.second.begin())) - { - bool overrideHashMismatch = context.Args.Contains(Execution::Args::Type::HashOverride); - - const auto& manifest = context.Get(); - Logging::Telemetry().LogInstallerHashMismatch(manifest.Id, manifest.Version, manifest.Channel, hashPair.first, hashPair.second, overrideHashMismatch); - - // If running as admin, do not allow the user to override the hash failure. - if (Runtime::IsRunningAsAdmin()) - { - context.Reporter.Error() << Resource::String::InstallerHashMismatchAdminBlock << std::endl; - } - else if (overrideHashMismatch) - { - context.Reporter.Warn() << Resource::String::InstallerHashMismatchOverridden << std::endl; - return; - } - else if (Settings::GroupPolicies().IsEnabled(Settings::TogglePolicy::Policy::HashOverride)) - { - context.Reporter.Error() << Resource::String::InstallerHashMismatchOverrideRequired << std::endl; - } - else - { - context.Reporter.Error() << Resource::String::InstallerHashMismatchError << std::endl; - } - - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH); - } - else - { - AICLI_LOG(CLI, Info, << "Installer hash verified"); - context.Reporter.Info() << Resource::String::InstallerHashVerified << std::endl; - - context.SetFlags(Execution::ContextFlag::InstallerHashMatched); - - if (context.Contains(Execution::Data::PackageVersion) && - context.Get()->GetSource() != nullptr && - WI_IsFlagSet(context.Get()->GetSource()->GetDetails().TrustLevel, SourceTrustLevel::Trusted)) - { - context.SetFlags(Execution::ContextFlag::InstallerTrusted); - } - } - } - - void UpdateInstallerFileMotwIfApplicable(Execution::Context& context) - { - if (context.Contains(Execution::Data::InstallerPath)) - { - if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerTrusted)) - { - Utility::ApplyMotwIfApplicable(context.Get(), URLZONE_TRUSTED); - } - else if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerHashMatched)) - { - const auto& installer = context.Get(); - HRESULT hr = Utility::ApplyMotwUsingIAttachmentExecuteIfApplicable(context.Get(), installer.value().Url); - - // Not using SUCCEEDED(hr) to check since there are cases file is missing after a successful scan - if (hr != S_OK) - { - switch (hr) - { - case INET_E_SECURITY_PROBLEM: - context.Reporter.Error() << Resource::String::InstallerBlockedByPolicy << std::endl; - break; - case E_FAIL: - context.Reporter.Error() << Resource::String::InstallerFailedVirusScan << std::endl; - break; - default: - context.Reporter.Error() << Resource::String::InstallerFailedSecurityCheck << std::endl; - } - - AICLI_LOG(Fail, Error, << "Installer failed security check. Url: " << installer.value().Url << " Result: " << WINGET_OSTREAM_FORMAT_HRESULT(hr)); - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED); - } - } - } - } - - void ExecuteInstaller(Execution::Context& context) - { - const auto& installer = context.Get().value(); - - bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); - - switch (installer.InstallerType) - { - case InstallerTypeEnum::Exe: - case InstallerTypeEnum::Burn: - case InstallerTypeEnum::Inno: - case InstallerTypeEnum::Msi: - case InstallerTypeEnum::Nullsoft: - case InstallerTypeEnum::Wix: - if (isUpdate && installer.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious) - { - context << - GetUninstallInfo << - ExecuteUninstaller; - context.ClearFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); - } - context << ShellExecuteInstall; - break; - case InstallerTypeEnum::Msix: - context << MsixInstall; - break; - case InstallerTypeEnum::MSStore: - context << - EnsureFeatureEnabled(Settings::ExperimentalFeature::Feature::ExperimentalMSStore) << - EnsureStorePolicySatisfied << - (isUpdate ? MSStoreUpdate : MSStoreInstall); - break; - default: - THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); - } - } - - void ShellExecuteInstall(Execution::Context& context) - { - context << - GetInstallerArgs << - RenameDownloadedInstaller << - ShellExecuteInstallImpl; - } - - void MsixInstall(Execution::Context& context) - { - Uri uri = nullptr; - if (context.Contains(Execution::Data::InstallerPath)) - { - uri = Uri(context.Get().c_str()); - } - else - { - uri = Uri(Utility::ConvertToUTF16(context.Get()->Url)); - } - - context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; - - try - { - DeploymentOptions deploymentOptions = - DeploymentOptions::ForceApplicationShutdown | - DeploymentOptions::ForceTargetApplicationShutdown; - - context.Reporter.ExecuteWithProgress(std::bind(Deployment::AddPackage, uri, deploymentOptions, - WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerTrusted), std::placeholders::_1)); - } - catch (const wil::ResultException& re) - { - const auto& manifest = context.Get(); - Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, "MSIX", re.GetErrorCode()); - - context.Reporter.Error() << GetUserPresentableMessage(re) << std::endl; - AICLI_TERMINATE_CONTEXT(re.GetErrorCode()); - } - - context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; - } - - void RemoveInstaller(Execution::Context& context) - { - // Path may not be present if installed from a URL for MSIX - if (context.Contains(Execution::Data::InstallerPath)) - { - const auto& path = context.Get(); - AICLI_LOG(CLI, Info, << "Removing installer: " << path); - - try - { - // best effort - std::filesystem::remove(path); - } - catch (const std::exception& e) - { - AICLI_LOG(CLI, Warning, << "Failed to remove installer file after execution. Reason: " << e.what()); - } - catch (...) - { - AICLI_LOG(CLI, Warning, << "Failed to remove installer file after execution. Reason unknown."); - } - } - } - - void ReportIdentityAndInstallationDisclaimer(Execution::Context& context) - { - context << - Workflow::ReportManifestIdentity << - Workflow::ShowInstallationDisclaimer; - } - - void InstallPackageInstaller(Execution::Context& context) - { - context << - Workflow::ReportExecutionStage(ExecutionStage::Download) << - Workflow::DownloadInstaller << - Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << - Workflow::SnapshotARPEntries << - Workflow::ReportExecutionStage(ExecutionStage::Execution) << - Workflow::ExecuteInstaller << - Workflow::ReportExecutionStage(ExecutionStage::PostExecution) << - Workflow::ReportARPChanges << - Workflow::RemoveInstaller; - } - - void InstallPackageVersion(Execution::Context& context) - { - context << - Workflow::SelectInstaller << - Workflow::EnsureApplicableInstaller << - Workflow::ReportIdentityAndInstallationDisclaimer << - Workflow::GetDependenciesFromInstaller << - Workflow::ManagePackageDependencies << - Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << - Workflow::InstallPackageInstaller; - } - - void SelectInstallerMultiple(Execution::Context& context) - { - bool allSucceeded = true; - DependencyList allDependencies; - std::vector installers; - - for (auto package : context.Get()) - { - Logging::SubExecutionTelemetryScope subExecution; - - // We want to do best effort to install all packages regardless of previous failures - auto installContextPtr = context.Clone(); - Execution::Context& installContext = *installContextPtr; - - // Extract the data needed for installing - installContext.Add(package.PackageVersion); - installContext.Add(package.PackageVersion->GetManifest()); - - // TODO: In the future, it would be better to not have to convert back and forth from a string - installContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(package.PackageRequest.Scope)); - - installContext << - Workflow::SelectInstaller << - Workflow::EnsureApplicableInstaller; - - if (installContext.IsTerminated()) - { - allSucceeded = false; - continue; - } - - const auto& installer = installContext.Get(); - installers.push_back({package.PackageVersion, installer.value(), false}); - - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - allDependencies.Add(installer->Dependencies); - } - } - - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - context.Add(allDependencies); - context << Workflow::ReportDependencies(Resource::String::ImportCommandReportDependencies); - } - context.Add(installers); - context << InstallMultiple; - } - - void InstallMultiple(Execution::Context& context) - { - bool allSucceeded = true; - - const auto& installers = context.Get(); - - for (auto packageAndInstaller : installers) - { - auto packageVersion = packageAndInstaller.PackageVersion; - auto installer = packageAndInstaller.Installer; - - auto installContextPtr = context.Clone(); - Execution::Context& installContext = *installContextPtr; - - // set data needed for installing - installContext.Add(packageVersion); - installContext.Add(packageVersion->GetManifest()); - installContext.Add(installer); - - installContext << - ReportIdentityAndInstallationDisclaimer << - Workflow::InstallPackageInstaller; - - if (installContext.IsTerminated()) - { - if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) - { - // This means that the subcontext being terminated is due to an overall abort - context.Reporter.Info() << Resource::String::Cancelled << std::endl; - return; - } - - allSucceeded = false; - } - } - - if (!allSucceeded) - { - context.Reporter.Error() << Resource::String::ImportInstallFailed << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); - } - } - - void SnapshotARPEntries(Execution::Context& context) try - { - // Ensure that installer type might actually write to ARP, otherwise this is a waste of time - auto installer = context.Get(); - - if (installer && MightWriteToARP(installer->InstallerType)) - { - std::shared_ptr arpSource = context.Reporter.ExecuteWithProgress( - [](IProgressCallback& progress) - { - return Repository::OpenPredefinedSource(PredefinedSource::ARP, progress); - }, true); - - std::vector> entries; - - for (const auto& entry : arpSource->Search({}).Matches) - { - auto installed = entry.Package->GetInstalledVersion(); - if (installed) - { - entries.emplace_back(std::make_tuple( - entry.Package->GetProperty(PackageProperty::Id), - installed->GetProperty(PackageVersionProperty::Version), - installed->GetProperty(PackageVersionProperty::Channel))); - } - } - - std::sort(entries.begin(), entries.end()); - - context.Add(std::move(entries)); - } - } - CATCH_LOG() - - void ReportARPChanges(Execution::Context& context) try - { - if (context.Contains(Execution::Data::ARPSnapshot)) - { - const auto& entries = context.Get(); - - // Open it again to get the (potentially) changed ARP entries - std::shared_ptr arpSource = context.Reporter.ExecuteWithProgress( - [](IProgressCallback& progress) - { - return Repository::OpenPredefinedSource(PredefinedSource::ARP, progress); - }, true); - - std::vector changes; - - for (auto& entry : arpSource->Search({}).Matches) - { - auto installed = entry.Package->GetInstalledVersion(); - - if (installed) - { - auto entryKey = std::make_tuple( - entry.Package->GetProperty(PackageProperty::Id), - installed->GetProperty(PackageVersionProperty::Version), - installed->GetProperty(PackageVersionProperty::Channel)); - - auto itr = std::lower_bound(entries.begin(), entries.end(), entryKey); - if (itr == entries.end() || *itr != entryKey) - { - changes.emplace_back(std::move(entry)); - } - } - } - - // Also attempt to find the entry based on the manifest data - const auto& manifest = context.Get(); - - SearchRequest nameAndPublisherRequest; - - // The default localization must contain the name or we cannot do this lookup - if (manifest.DefaultLocalization.Contains(Localization::PackageName)) - { - AppInstaller::Manifest::Manifest::string_t defaultName = manifest.DefaultLocalization.Get(); - AppInstaller::Manifest::Manifest::string_t defaultPublisher; - if (manifest.DefaultLocalization.Contains(Localization::Publisher)) - { - defaultPublisher = manifest.DefaultLocalization.Get(); - } - - nameAndPublisherRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, defaultName, defaultPublisher)); - - for (const auto& loc : manifest.Localizations) - { - if (loc.Contains(Localization::PackageName) || loc.Contains(Localization::Publisher)) - { - nameAndPublisherRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, - loc.Contains(Localization::PackageName) ? loc.Get() : defaultName, - loc.Contains(Localization::Publisher) ? loc.Get() : defaultPublisher)); - } - } - } - - std::vector productCodes; - for (const auto& installer : manifest.Installers) - { - if (!installer.ProductCode.empty()) - { - if (std::find(productCodes.begin(), productCodes.end(), installer.ProductCode) == productCodes.end()) - { - nameAndPublisherRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, installer.ProductCode)); - productCodes.emplace_back(installer.ProductCode); - } - } - } - - SearchResult findByManifest; - - // Don't execute this search if it would just find everything - if (!nameAndPublisherRequest.IsForEverything()) - { - findByManifest = arpSource->Search(nameAndPublisherRequest); - } - - // Cross reference the changes with the search results - std::vector> packagesInBoth; - - for (const auto& change : changes) - { - for (const auto& byManifest : findByManifest.Matches) - { - if (change.Package->IsSame(byManifest.Package.get())) - { - packagesInBoth.emplace_back(change.Package); - break; - } - } - } - - // We now have all of the package changes; time to report them. - // The set of cases we could have for changes to ARP: - // 0 packages :: No changes were detected to ARP, which could mean that the installer - // did not write an entry. It could also be a forced reinstall. - // 1 package :: Golden path; this should be what we installed. - // 2+ packages :: We need to determine which package actually matches the one that we - // were installing. - // - // The set of cases we could have for finding packages based on the manifest: - // 0 packages :: The manifest data does not match the ARP information. - // 1 package :: Golden path; this should be what we installed. - // 2+ packages :: The data in the manifest is either too broad or we have - // a problem with our name normalization. - // - // ARP Package changes - // 0 1 N - // +------------------+--------------------+--------------------+ - // M | | | | - // a | Package does not | Manifest data does | Manifest data does | - // n 0 | write to ARP | not match ARP | not match ARP | - // i | Log this fact | Log for fixup | Log for fixup | - // f | | | | - // e +------------------+--------------------+--------------------+ - // s | | | | - // t | Reinstall of | Golden Path! | Treat manifest as | - // 1 | existing version | (assuming match) | main if common | - // r | | | | - // e +------------------+--------------------+--------------------+ - // s | | | | - // u | Not expected | Treat ARP as main | Not expected | - // l N | Log this for | | Log this for | - // t | investigation | | investigation | - // s | | | | - // +------------------+--------------------+--------------------+ - - // Find the package that we are going to log - std::shared_ptr toLog; - - // If no changes found, only log if a single matching package was found by the manifest - if (changes.empty() && findByManifest.Matches.size() == 1) - { - toLog = findByManifest.Matches[0].Package->GetInstalledVersion(); - } - // If only a single ARP entry was changed, always log that - else if (changes.size() == 1) - { - toLog = changes[0].Package->GetInstalledVersion(); - } - // Finally, if there is only a single common package, log that one - else if (packagesInBoth.size() == 1) - { - toLog = packagesInBoth[0]->GetInstalledVersion(); - } - - IPackageVersion::Metadata toLogMetadata; - if (toLog) - { - toLogMetadata = toLog->GetMetadata(); - } - - // We can only get the source identifier from an active source - std::string sourceIdentifier; - if (context.Contains(Execution::Data::PackageVersion)) - { - sourceIdentifier = context.Get()->GetProperty(PackageVersionProperty::SourceIdentifier); - } - - Logging::Telemetry().LogSuccessfulInstallARPChange( - sourceIdentifier, - manifest.Id, - manifest.Version, - manifest.Channel, - changes.size(), - findByManifest.Matches.size(), - packagesInBoth.size(), - toLog ? static_cast(toLog->GetProperty(PackageVersionProperty::Name)) : "", - toLog ? static_cast(toLog->GetProperty(PackageVersionProperty::Version)) : "", - toLog ? static_cast(toLogMetadata[PackageVersionMetadata::Publisher]) : "", - toLog ? static_cast(toLogMetadata[PackageVersionMetadata::InstalledLocale]) : "" - ); - } - } - CATCH_LOG() -} From 40abbdae678b5d34280ed80ab775f446c751c0f9 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Thu, 5 Aug 2021 15:09:48 -0300 Subject: [PATCH 75/82] more testing for dependencies on install command --- .../Commands/UpgradeCommand.cpp | 4 +- .../Workflows/UpdateFlow.cpp | 1 + src/AppInstallerCLITests/WorkFlow.cpp | 129 ++++++++++++++++-- 3 files changed, 120 insertions(+), 14 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index eb281e4b4f..3ed208ed52 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -149,10 +149,11 @@ namespace AppInstaller::CLI GetInstalledPackageVersion << EnsureUpdateVersionApplicable << SelectInstaller << - EnsureApplicableInstaller << + EnsureApplicableInstaller << ReportIdentityAndInstallationDisclaimer << GetDependenciesFromInstaller << ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + ManagePackageDependencies << InstallPackageInstaller; } else @@ -183,6 +184,7 @@ namespace AppInstaller::CLI ReportIdentityAndInstallationDisclaimer << GetDependenciesFromInstaller << ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + ManagePackageDependencies << InstallPackageInstaller; } } diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index 238e724ffe..55cf051e34 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -116,6 +116,7 @@ namespace AppInstaller::CLI::Workflow ReportIdentityAndInstallationDisclaimer << GetDependenciesFromInstaller << ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << + ManagePackageDependencies << InstallPackageInstaller; updateContext.Reporter.Info() << std::endl; diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 4d61222ee6..a478f48596 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -262,11 +262,11 @@ namespace auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_Dependencies.yaml")); manifest.Id = input; manifest.Moniker = input; - // TODO maybe change package name on default locale for better debugging auto& installer = manifest.Installers.at(0); installer.ProductId = input; installer.Dependencies.Clear(); + installer.Switches[InstallerSwitchType::Custom] = input; /* * Dependencies: @@ -278,8 +278,12 @@ namespace * F: B * G: C * H: G, B + * + * installed1 */ + bool installed = false; + //-- predefined if (input == "C") { @@ -306,6 +310,12 @@ namespace installer.Dependencies.Add(Dependency(DependencyType::Package, "G")); installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); } + if (input == "installed1") + { + installed = true; + installer.Dependencies.Add(Dependency(DependencyType::Package, "G")); + installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); + } // depends on test if (input == "StackOrderIsOk") @@ -331,18 +341,39 @@ namespace installer.Dependencies.Add(Dependency(DependencyType::Package, "C")); installer.Dependencies.Add(Dependency(DependencyType::Package, "H")); } + if (input == "DependenciesInstalled") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "installed1")); + } + //TODO: - // test for installed packages (or the ones that need upgrade) + // test for installed packages and packages that need upgrades // test for different min Version of dependencies + if (installed) + { + //auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + manifest, + TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + std::vector{ manifest }, + const_cast(this)->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); + } + else + { + result.Matches.emplace_back( + ResultMatch( + TestPackage::Make( + std::vector{ manifest }, + const_cast(this)->shared_from_this() + ), + PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); + } - result.Matches.emplace_back( - ResultMatch( - TestPackage::Make( - std::vector{ manifest }, - const_cast(this)->shared_from_this() - ), - PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); return result; } }; @@ -514,6 +545,24 @@ void OverrideForShellExecute(TestContext& context) OverrideForUpdateInstallerMotw(context); } +void OverrideForShellExecute(TestContext& context, std::vector& installationLog) +{ + context.Override({ DownloadInstallerFile, [&installationLog](TestContext& context) + { + context.Add({ {}, {} }); + context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); + + auto dependency = Dependency(DependencyType::Package, context.Get().Id, context.Get().Version); + installationLog.push_back(dependency); + } }); + + context.Override({ RenameDownloadedInstaller, [](TestContext&) + { + } }); + + OverrideForUpdateInstallerMotw(context); +} + void OverrideForExeUninstall(TestContext& context) { context.Override({ ShellExecuteUninstallImpl, [](TestContext& context) @@ -1808,11 +1857,12 @@ TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph][depe TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideOpenSourceForDependencies(context); - OverrideForShellExecute(context); + OverrideForShellExecute(context, installationOrder); context.Args.AddArg(Execution::Args::Type::Query, "DependencyAlreadyInStackButNoLoop"sv); @@ -1825,16 +1875,24 @@ TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGr REQUIRE(installOutput.str().find("has loop") == std::string::npos); REQUIRE(installOutput.str().find("order: B, C, F, DependencyAlreadyInStackButNoLoop,") != std::string::npos); + + // Verify installers are called in order + REQUIRE(installationOrder.size() == 4); + REQUIRE(installationOrder.at(0).Id == "B"); + REQUIRE(installationOrder.at(1).Id == "C"); + REQUIRE(installationOrder.at(2).Id == "F"); + REQUIRE(installationOrder.at(3).Id == "DependencyAlreadyInStackButNoLoop"); } TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideOpenSourceForDependencies(context); - OverrideForShellExecute(context); + OverrideForShellExecute(context, installationOrder); context.Args.AddArg(Execution::Args::Type::Query, "PathBetweenBranchesButNoLoop"sv); @@ -1848,16 +1906,24 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph REQUIRE(installOutput.str().find("has loop") == std::string::npos); REQUIRE(installOutput.str().find("order: B, C, G, H, PathBetweenBranchesButNoLoop,") != std::string::npos); + // Verify installers are called in order + REQUIRE(installationOrder.size() == 5); + REQUIRE(installationOrder.at(0).Id == "B"); + REQUIRE(installationOrder.at(1).Id == "C"); + REQUIRE(installationOrder.at(2).Id == "G"); + REQUIRE(installationOrder.at(3).Id == "H"); + REQUIRE(installationOrder.at(4).Id == "PathBetweenBranchesButNoLoop"); } TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideOpenSourceForDependencies(context); - OverrideForShellExecute(context); + OverrideForShellExecute(context, installationOrder); context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); @@ -1871,16 +1937,22 @@ TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyG REQUIRE(installOutput.str().find("has loop") == std::string::npos); REQUIRE(installOutput.str().find("order: B, C, StackOrderIsOk,") != std::string::npos); + // Verify installers are called in order + REQUIRE(installationOrder.size() == 3); + REQUIRE(installationOrder.at(0).Id == "B"); + REQUIRE(installationOrder.at(1).Id == "C"); + REQUIRE(installationOrder.at(2).Id == "StackOrderIsOk"); } TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideOpenSourceForDependencies(context); - OverrideForShellExecute(context); + OverrideForShellExecute(context, installationOrder); context.Args.AddArg(Execution::Args::Type::Query, "NeedsToInstallBFirst"sv); @@ -1894,6 +1966,37 @@ TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][de REQUIRE(installOutput.str().find("has loop") == std::string::npos); REQUIRE(installOutput.str().find("order: B, C, NeedsToInstallBFirst,") != std::string::npos); + // Verify installers are called in order + REQUIRE(installationOrder.size() == 3); + REQUIRE(installationOrder.at(0).Id == "B"); + REQUIRE(installationOrder.at(1).Id == "C"); + REQUIRE(installationOrder.at(2).Id == "NeedsToInstallBFirst"); +} + +TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGraph][dependencies]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideOpenSourceForDependencies(context); + OverrideForShellExecute(context, installationOrder); + + context.Args.AddArg(Execution::Args::Type::Query, "DependenciesInstalled"sv); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + REQUIRE(installOutput.str().find("has loop") == std::string::npos); + // dependencies installed will show on the order but the installer will not be called + REQUIRE(installOutput.str().find("order: installed1, DependenciesInstalled,") != std::string::npos); + REQUIRE(installationOrder.size() == 1); + REQUIRE(installationOrder.at(0).Id == "DependenciesInstalled"); } TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") From e8b4e6aa2d01604312f431da737e444efe865a1b Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 6 Aug 2021 13:20:12 -0300 Subject: [PATCH 76/82] valid min version test --- .../Workflows/DependenciesFlow.cpp | 7 +- .../TestData/Installer_Exe_Dependencies.yaml | 2 +- src/AppInstallerCLITests/WorkFlow.cpp | 65 +++++++++++++++++-- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 6785415356..c72e9eff9c 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -112,6 +112,11 @@ namespace AppInstaller::CLI::Workflow void ManagePackageDependencies(Execution::Context& context) { + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + return; + } + auto info = context.Reporter.Info(); const auto& rootManifest = context.Get(); @@ -192,7 +197,7 @@ namespace AppInstaller::CLI::Workflow installer = SelectInstallerFromMetadata(context.Args, manifest, {}); } - const auto& nodeDependencies = installer->Dependencies; + auto& nodeDependencies = installer->Dependencies; idToInstallerMap[node.Id] = {latestVersion, installer.value(), isUpdate}; return nodeDependencies; diff --git a/src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml b/src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml index 9d65242fa5..6933de81b4 100644 --- a/src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml +++ b/src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml @@ -1,5 +1,5 @@ PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies -PackageVersion: 1.0.0.0 +PackageVersion: 1.0 PackageLocale: en-US PackageName: AppInstaller Test Exe Installer With Package Dep Publisher: Microsoft Corporation diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index a478f48596..db8dfbc723 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -266,7 +266,6 @@ namespace auto& installer = manifest.Installers.at(0); installer.ProductId = input; installer.Dependencies.Clear(); - installer.Switches[InstallerSwitchType::Custom] = input; /* * Dependencies: @@ -280,6 +279,10 @@ namespace * H: G, B * * installed1 + * minVersion1.0 + * minVersion1.5 + * requires1.5: minVersion1.5 + * minVersion2.0 //invalid version (not returned as result) */ bool installed = false; @@ -313,8 +316,21 @@ namespace if (input == "installed1") { installed = true; - installer.Dependencies.Add(Dependency(DependencyType::Package, "G")); - installer.Dependencies.Add(Dependency(DependencyType::Package, "B")); + installer.Dependencies.Add(Dependency(DependencyType::Package, "installed1Dep")); + } + if (input == "minVersion1.0") + { + manifest.Id = "minVersion"; + manifest.Version = "1.0"; + } + if (input == "minVersion1.5") + { + manifest.Id = "minVersion"; + manifest.Version = "1.5"; + } + if (input == "requires1.5") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "minVersion", "1.5")); } // depends on test @@ -345,7 +361,15 @@ namespace { installer.Dependencies.Add(Dependency(DependencyType::Package, "installed1")); } - + if (input == "DependenciesValidMinVersions") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "minVersion", "1.0")); + } + if (input == "DependenciesValidMinVersionsMultiple") + { + installer.Dependencies.Add(Dependency(DependencyType::Package, "minVersion", "1.0")); + installer.Dependencies.Add(Dependency(DependencyType::Package, "requires1.5")); + } //TODO: // test for installed packages and packages that need upgrades @@ -1993,10 +2017,41 @@ TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGr INFO(installOutput.str()); REQUIRE(installOutput.str().find("has loop") == std::string::npos); - // dependencies installed will show on the order but the installer will not be called + // dependencies installed will show on the graph order but the installer will not be called REQUIRE(installOutput.str().find("order: installed1, DependenciesInstalled,") != std::string::npos); REQUIRE(installationOrder.size() == 1); REQUIRE(installationOrder.at(0).Id == "DependenciesInstalled"); + // dependencies of an installed package will not be checked nor added to the graph + REQUIRE(installOutput.str().find("installed1Dep") == std::string::npos); +} + +TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependencyGraph][dependencies]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideOpenSourceForDependencies(context); + OverrideForShellExecute(context, installationOrder); + + context.Args.AddArg(Execution::Args::Type::Query, "DependenciesValidMinVersions"sv); + + TestUserSettings settings; + settings.Set({ true }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + REQUIRE(installOutput.str().find("has loop") == std::string::npos); + // dependencies installed will show on the order but the installer will not be called + REQUIRE(installOutput.str().find("order: minVersion, DependenciesValidMinVersions,") != std::string::npos); + REQUIRE(installationOrder.size() == 2); + REQUIRE(installationOrder.at(0).Id == "minVersion"); + // minVersion 1.5 is available but this requires 1.0 so that version is installed + REQUIRE(installationOrder.at(0).MinVersion.value().ToString() == "1.0"); + REQUIRE(installationOrder.at(1).Id == "DependenciesInstalled"); } TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") From cadeaa8c75d13ab33935c5b8e0ef815da497d9f2 Mon Sep 17 00:00:00 2001 From: Florencia Zanollo Date: Fri, 6 Aug 2021 13:30:52 -0300 Subject: [PATCH 77/82] typo --- src/AppInstallerCLITests/WorkFlow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index db8dfbc723..ab4e10f2ae 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -2051,7 +2051,7 @@ TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependenc REQUIRE(installationOrder.at(0).Id == "minVersion"); // minVersion 1.5 is available but this requires 1.0 so that version is installed REQUIRE(installationOrder.at(0).MinVersion.value().ToString() == "1.0"); - REQUIRE(installationOrder.at(1).Id == "DependenciesInstalled"); + REQUIRE(installationOrder.at(1).Id == "DependenciesValidMinVersions"); } TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") From d9f41c8f9279828168c2e798fb0aae0915e44a8a Mon Sep 17 00:00:00 2001 From: Akinwale Alagbe Date: Thu, 30 Sep 2021 18:45:31 -0700 Subject: [PATCH 78/82] temp stash --- .../ExecutionContextData.h | 3 + .../Workflows/DependenciesFlow.cpp | 134 ++++++++++-------- .../Workflows/InstallFlow.cpp | 4 - .../Public/winget/ManifestCommon.h | 2 +- .../Rest/RestSource.cpp | 2 +- 5 files changed, 76 insertions(+), 69 deletions(-) diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index 7b0763fdc9..9cb7d6ee95 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -89,6 +89,8 @@ namespace AppInstaller::CLI::Execution struct InstallerToInstall { std::shared_ptr PackageVersion; + std::shared_ptr InstalledPackageVersion; + uint32_t PackageSubExecutionId = 0; Manifest::ManifestInstaller Installer; bool IsUpdate = false; }; @@ -246,6 +248,7 @@ namespace AppInstaller::CLI::Execution using value_t = std::shared_ptr; }; + template <> struct DataMapping { using value_t = std::vector; diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index c72e9eff9c..f17fe24348 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -1,58 +1,58 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "DependenciesFlow.h" -#include "ManifestComparator.h" +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "DependenciesFlow.h" +#include "ManifestComparator.h" #include "InstallFlow.h" - - -namespace AppInstaller::CLI::Workflow + + +namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::Repository; using namespace Manifest; void ReportDependencies::operator()(Execution::Context& context) const { - if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - return; - } - auto info = context.Reporter.Info(); - - const auto& dependencies = context.Get(); - if (dependencies.HasAny()) - { - info << Resource::StringId(m_messageId) << std::endl; - - if (dependencies.HasAnyOf(DependencyType::WindowsFeature)) - { - info << " - " << Resource::String::WindowsFeaturesDependencies << std::endl; - dependencies.ApplyToType(DependencyType::WindowsFeature, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); - } - - if (dependencies.HasAnyOf(DependencyType::WindowsLibrary)) - { - info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; - dependencies.ApplyToType(DependencyType::WindowsLibrary, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); - } - - if (dependencies.HasAnyOf(DependencyType::Package)) - { - info << " - " << Resource::String::PackageDependencies << std::endl; - dependencies.ApplyToType(DependencyType::Package, [&info](Dependency dependency) - { - info << " " << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value().ToString() << "]"; - info << std::endl; - }); - } - - if (dependencies.HasAnyOf(DependencyType::External)) - { - info << " - " << Resource::String::ExternalDependencies << std::endl; - dependencies.ApplyToType(DependencyType::External, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); - } + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + return; + } + auto info = context.Reporter.Info(); + + const auto& dependencies = context.Get(); + if (dependencies.HasAny()) + { + info << Resource::StringId(m_messageId) << std::endl; + + if (dependencies.HasAnyOf(DependencyType::WindowsFeature)) + { + info << " - " << Resource::String::WindowsFeaturesDependencies << std::endl; + dependencies.ApplyToType(DependencyType::WindowsFeature, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } + + if (dependencies.HasAnyOf(DependencyType::WindowsLibrary)) + { + info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; + dependencies.ApplyToType(DependencyType::WindowsLibrary, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } + + if (dependencies.HasAnyOf(DependencyType::Package)) + { + info << " - " << Resource::String::PackageDependencies << std::endl; + dependencies.ApplyToType(DependencyType::Package, [&info](Dependency dependency) + { + info << " " << dependency.Id; + if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value().ToString() << "]"; + info << std::endl; + }); + } + + if (dependencies.HasAnyOf(DependencyType::External)) + { + info << " - " << Resource::String::ExternalDependencies << std::endl; + dependencies.ApplyToType(DependencyType::External, [&info](Dependency dependency) {info << " " << dependency.Id << std::endl; }); + } } } @@ -112,9 +112,9 @@ namespace AppInstaller::CLI::Workflow void ManagePackageDependencies(Execution::Context& context) { - if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) - { - return; + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) + { + return; } auto info = context.Reporter.Info(); @@ -141,7 +141,7 @@ namespace AppInstaller::CLI::Workflow } const auto& source = context.Get(); - std::map idToInstallerMap; + std::map idToPackageMap; DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, [&](Dependency node) { @@ -167,13 +167,13 @@ namespace AppInstaller::CLI::Workflow } else { - const auto& latestVersion = package->GetLatestAvailableVersion(); + std::shared_ptr latestVersion = package->GetLatestAvailableVersion(); if (!latestVersion) { info << "No package version found"; //TODO localize all errors return DependencyList(); //return empty dependency list, TODO change this to actually manage errors } - const auto& manifest = latestVersion->GetManifest(); + auto manifest = latestVersion->GetManifest(); if (manifest.Installers.empty()) { info << "No installers found"; //TODO localize all errors return DependencyList(); //return empty dependency list, TODO change this to actually manage errors @@ -186,10 +186,11 @@ namespace AppInstaller::CLI::Workflow } std::optional installer; + auto installedVersion = package->GetInstalledVersion(); bool isUpdate = false; - if (package->GetInstalledVersion()) + if (installedVersion) { - installer = SelectInstallerFromMetadata(context.Args, manifest, package->GetInstalledVersion()->GetMetadata()); + installer = SelectInstallerFromMetadata(context.Args, manifest, installedVersion->GetMetadata()); isUpdate = true; } else @@ -198,8 +199,15 @@ namespace AppInstaller::CLI::Workflow } auto& nodeDependencies = installer->Dependencies; - - idToInstallerMap[node.Id] = {latestVersion, installer.value(), isUpdate}; + + Execution::PackageToInstall packageToInstall{ + std::move(latestVersion), + std::move(installedVersion), + std::move(manifest), + std::move(installer.value()) }; + + idToPackageMap.emplace(node.Id, packageToInstall); + return nodeDependencies; } } @@ -223,17 +231,17 @@ namespace AppInstaller::CLI::Workflow const auto& installationOrder = dependencyGraph.GetInstallationOrder(); - std::vector installers; + std::vector installers; info << "order: "; //-- only for debugging for (auto const& node : installationOrder) { info << node.Id << ", "; //-- only for debugging - auto itr = idToInstallerMap.find(node.Id); + auto itr = idToPackageMap.find(node.Id); // if the package was already installed (with a useful version) or is the root // then there will be no installer for it on the map. - if (itr != idToInstallerMap.end()) + if (itr != idToPackageMap.end()) { installers.push_back(itr->second); } @@ -241,8 +249,8 @@ namespace AppInstaller::CLI::Workflow info << std::endl; //-- only for debugging // Install dependencies in the correct order - context.Add(installers); - context << InstallMultiple; + context.Add(installers); + context << Workflow::InstallMultiplePackages(Resource::String::ImportCommandReportDependencies, APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); // Install the root (continue) } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index ca58fcd0a8..c13738ae9e 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -653,11 +653,7 @@ namespace AppInstaller::CLI::Workflow context.Add(allDependencies); context << Workflow::ReportDependencies(m_dependenciesReportMessage); } - context << InstallMultiple; - } - void InstallMultiple(Execution::Context& context) - { bool allSucceeded = true; for (auto package : context.Get()) { diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index c091067bfb..f40b24e904 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -358,7 +358,7 @@ bool HasExtension(std::string_view extension) const; return; } - for (int i = 0; i < toCheck.size(); ++i) + for (unsigned int i = 0; i < toCheck.size(); ++i) { auto node = toCheck.at(i); diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index c0d21db2af..8b751815f6 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -32,7 +32,7 @@ namespace AppInstaller::Repository::Rest // The IPackage implementation for Available packages from RestSource. struct AvailablePackage : public std::enable_shared_from_this, public SourceReference, public IPackage { - AvailablePackage(const std::shared_ptr& source, IRestClient::Package&& package) : + AvailablePackage(const std::shared_ptr& source, IRestClient::Package&& package) : SourceReference(source), m_package(std::move(package)) { SortVersionsInternal(); From 1bd88aa881c75096d77b917767a579e7c579da97 Mon Sep 17 00:00:00 2001 From: Akinwale Alagbe Date: Thu, 30 Sep 2021 18:45:49 -0700 Subject: [PATCH 79/82] temp stash --- src/AppInstallerCLITests/WorkFlow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 612ca797b0..d224983b8c 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -2597,7 +2597,7 @@ TEST_CASE("OpenSource_WithCustomHeader", "[OpenSource][CustomHeader]") context.Args.AddArg(Execution::Args::Type::CustomHeader, customHeader2); context.Args.AddArg(Execution::Args::Type::Source, details.Name); - OpenSource(context); + AppInstaller::CLI::Workflow::OpenSource()(context); auto source = context.Get(); REQUIRE(source.get()->GetDetails().CustomHeader.value_or("").compare(customHeader2) == 0); } From 609f79151fcbf76e3978191da695a27fddfae9e2 Mon Sep 17 00:00:00 2001 From: Akinwale Alagbe Date: Fri, 1 Oct 2021 14:11:07 -0700 Subject: [PATCH 80/82] Fixed merge errors --- .../Workflows/DependenciesFlow.cpp | 19 +++++++++---------- .../Workflows/ShowFlow.cpp | 3 ++- .../Workflows/WorkflowBase.h | 1 - src/AppInstallerCLITests/TestSource.cpp | 10 +++++----- src/AppInstallerCLITests/TestSource.h | 12 ++++++------ .../Public/winget/ManifestCommon.h | 1 + .../Microsoft/SQLiteIndexSource.cpp | 2 +- .../Public/AppInstallerRepositorySearch.h | 2 +- .../Rest/RestSource.cpp | 2 +- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index f17fe24348..b6d7072990 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -97,7 +97,7 @@ namespace AppInstaller::CLI::Workflow if (context.Contains(Execution::Data::PackageVersion)) { const auto& packageVersion = context.Get(); - context.Add(packageVersion->GetSource()); + context.Add(std::const_pointer_cast(packageVersion->GetSource())); context << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true); } @@ -187,18 +187,17 @@ namespace AppInstaller::CLI::Workflow std::optional installer; auto installedVersion = package->GetInstalledVersion(); - bool isUpdate = false; + + IPackageVersion::Metadata installationMetadata; if (installedVersion) { - installer = SelectInstallerFromMetadata(context.Args, manifest, installedVersion->GetMetadata()); - isUpdate = true; - } - else - { - installer = SelectInstallerFromMetadata(context.Args, manifest, {}); + installationMetadata = installedVersion->GetMetadata(); } - auto& nodeDependencies = installer->Dependencies; + ManifestComparator manifestComparator(context, installationMetadata); + installer = manifestComparator.GetPreferredInstaller(manifest); + + auto nodeDependencies = installer.value().Dependencies; Execution::PackageToInstall packageToInstall{ std::move(latestVersion), @@ -206,7 +205,7 @@ namespace AppInstaller::CLI::Workflow std::move(manifest), std::move(installer.value()) }; - idToPackageMap.emplace(node.Id, packageToInstall); + idToPackageMap.emplace(node.Id, packageToInstall); return nodeDependencies; } diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 2663e889dd..ff444d4df0 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -172,7 +172,8 @@ namespace AppInstaller::CLI::Workflow dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { info << " "_liv << dependency.Id; - if (dependency.MinVersion) info << " [>= " << dependency.MinVersion.value() << "]"; + if (dependency.MinVersion) + info << " [>= " << dependency.MinVersion.value().ToString() << "]"; info << std::endl; }); } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index faec145c3d..c56845aa96 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -313,7 +313,6 @@ namespace AppInstaller::CLI::Workflow // Inputs: Manifest // Outputs: Installer void SelectInstaller(Execution::Context& context); - std::optional SelectInstallerFromMetadata(Execution::Args args, AppInstaller::Manifest::Manifest manifest, AppInstaller::Repository::IPackageVersion::Metadata metadata); // Ensures that the process is running as admin. // Required Args: None diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index d7f5025513..0a7bfce688 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -30,10 +30,10 @@ namespace TestCommon } } - TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source) : + TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source) : VersionManifest(manifest), Metadata(std::move(installationMetadata)), Source(source) {} - TestPackageVersion::TestPackageVersion(const Manifest& manifest, std::weak_ptr source) : + TestPackageVersion::TestPackageVersion(const Manifest& manifest, std::weak_ptr source) : VersionManifest(manifest), Source(source) {} TestPackageVersion::LocIndString TestPackageVersion::GetProperty(PackageVersionProperty property) const @@ -96,7 +96,7 @@ namespace TestCommon return VersionManifest; } - std::shared_ptr TestPackageVersion::GetSource() const + std::shared_ptr TestPackageVersion::GetSource() const { return Source.lock(); } @@ -119,7 +119,7 @@ namespace TestCommon } } - TestPackage::TestPackage(const std::vector& available, std::weak_ptr source) + TestPackage::TestPackage(const std::vector& available, std::weak_ptr source) { for (const auto& manifest : available) { @@ -127,7 +127,7 @@ namespace TestCommon } } - TestPackage::TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available, std::weak_ptr source) : + TestPackage::TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available, std::weak_ptr source) : InstalledVersion(TestPackageVersion::Make(installed, std::move(installationMetadata), source)) { for (const auto& manifest : available) diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h index c1c8e8ad4e..58adb06729 100644 --- a/src/AppInstallerCLITests/TestSource.h +++ b/src/AppInstallerCLITests/TestSource.h @@ -18,8 +18,8 @@ namespace TestCommon using LocIndString = AppInstaller::Utility::LocIndString; using MetadataMap = AppInstaller::Repository::IPackageVersion::Metadata; - TestPackageVersion(const Manifest& manifest, std::weak_ptr source = {}); - TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source = {}); + TestPackageVersion(const Manifest& manifest, std::weak_ptr source = {}); + TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) @@ -30,12 +30,12 @@ namespace TestCommon LocIndString GetProperty(AppInstaller::Repository::PackageVersionProperty property) const override; std::vector GetMultiProperty(AppInstaller::Repository::PackageVersionMultiProperty property) const override; Manifest GetManifest() override; - std::shared_ptr GetSource() const override; + std::shared_ptr GetSource() const override; MetadataMap GetMetadata() const override; Manifest VersionManifest; MetadataMap Metadata; - std::weak_ptr Source; + std::weak_ptr Source; protected: static void AddFoldedIfHasValueAndNotPresent(const AppInstaller::Utility::NormalizedString& value, std::vector& target); @@ -50,10 +50,10 @@ namespace TestCommon using MetadataMap = TestPackageVersion::MetadataMap; // Create a package with only available versions using these manifests. - TestPackage(const std::vector& available, std::weak_ptr source = {}); + TestPackage(const std::vector& available, std::weak_ptr source = {}); // Create a package with an installed version, metadata, and optionally available versions. - TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available = {}, std::weak_ptr source = {}); + TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available = {}, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index f40b24e904..351619afb5 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -420,6 +420,7 @@ bool HasExtension(std::string_view extension) const; bool loop = false; visited.insert(node); + auto lAdjacents = adjacents.at(node); for (const auto& adjacent : adjacents.at(node)) { auto search = visited.find(adjacent); diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index dea6786b38..6912b8470f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -82,7 +82,7 @@ namespace AppInstaller::Repository::Microsoft return GetManifestFromArgAndRelativePath(source->GetDetails().Arg, relativePathOpt.value(), manifestSHA256); } - std::shared_ptr GetSource() const override + std::shared_ptr GetSource() const override { return GetReferenceSource(); } diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h index ed23fa69af..e831e6846f 100644 --- a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h @@ -183,7 +183,7 @@ namespace AppInstaller::Repository virtual Manifest::Manifest GetManifest() = 0; // Gets the source where this package version is from. - virtual std::shared_ptr GetSource() const = 0; + virtual std::shared_ptr GetSource() const = 0; // Gets any metadata associated with this package version. // Primarily stores data on installed packages. diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index 8b751815f6..9fbeb29143 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -286,7 +286,7 @@ namespace AppInstaller::Repository::Rest return m_versionInfo.Manifest.value(); } - std::shared_ptr GetSource() const override + std::shared_ptr GetSource() const override { return GetReferenceSource(); } From fddc0ae844b7b9bf71d8410cdab3904a18b98b7e Mon Sep 17 00:00:00 2001 From: Akinwale Alagbe Date: Tue, 5 Oct 2021 08:27:34 -0700 Subject: [PATCH 81/82] Merge branch 'master' of https://github.com/hackean-msft/winget-cli into user/akalagbe/floppy-dependency-graph --- src/AppInstallerCLICore/Resources.h | 10 + .../Workflows/DependenciesFlow.cpp | 153 +++++----- .../Workflows/DependenciesFlow.h | 11 +- .../Workflows/InstallFlow.cpp | 20 +- .../Workflows/InstallFlow.h | 18 +- .../Workflows/ShowFlow.cpp | 2 +- .../Shared/Strings/en-us/winget.resw | 40 +++ src/AppInstallerCLITests/WorkFlow.cpp | 284 +++++++++--------- .../Public/AppInstallerErrors.h | 1 + 9 files changed, 313 insertions(+), 226 deletions(-) diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index fe00789df5..e8b49d4cb5 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -43,6 +43,16 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(CompleteCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(CompleteCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(CountArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowInstall); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceNotFound); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceTooManyMatches); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowPackageVersionNotFound); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowNoInstallerFound); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowNoMinVersion); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowNoMatches); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowContainsLoop); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesManagementError); + WINGET_DEFINE_RESOURCE_STRINGID(DependenciesManagementExitMessage); WINGET_DEFINE_RESOURCE_STRINGID(DisabledByGroupPolicy); WINGET_DEFINE_RESOURCE_STRINGID(Done); WINGET_DEFINE_RESOURCE_STRINGID(ExactArgumentDescription); diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index b6d7072990..7f0453ed85 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -110,7 +110,7 @@ namespace AppInstaller::CLI::Workflow } - void ManagePackageDependencies(Execution::Context& context) + void ManagePackageDependencies::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) { @@ -118,6 +118,7 @@ namespace AppInstaller::CLI::Workflow } auto info = context.Reporter.Info(); + auto error = context.Reporter.Error(); const auto& rootManifest = context.Get(); Dependency rootAsDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); @@ -131,112 +132,118 @@ namespace AppInstaller::CLI::Workflow return; } - info << "Installing dependencies:" << std::endl; //TODO localize message + info << Resource::String::DependenciesFlowInstall << std::endl; context << OpenDependencySource; if (!context.Contains(Execution::Data::DependencySource)) { - info << "dependency source not found" << std::endl; //TODO localize message - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); // TODO create specific error code + info << Resource::String::DependenciesFlowSourceNotFound << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); } const auto& source = context.Get(); std::map idToPackageMap; - + bool foundError = false; DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, [&](Dependency node) { auto info = context.Reporter.Info(); SearchRequest searchRequest; searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, node.Id)); - //TODO add min version filter to search request ? const auto& matches = source->Search(searchRequest).Matches; if (!matches.empty()) { - if (matches.size() > 1) { - info << "Too many matches"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + if (matches.size() > 1) + { + error << Resource::String::DependenciesFlowSourceTooManyMatches; + AICLI_LOG(CLI, Error, << "Too many matches for package " << Utility::Normalize(node.Id)); + foundError = true; + return DependencyList(); } const auto& match = matches.at(0); const auto& package = match.Package; - if (package->GetInstalledVersion() && node.IsVersionOk(package->GetInstalledVersion()->GetManifest().Version)) + + auto packageId = package->GetProperty(PackageProperty::Id); + auto installedVersion = package->GetInstalledVersion(); + + if (installedVersion && node.IsVersionOk(installedVersion->GetManifest().Version)) { - return DependencyList(); //return empty dependency list, as we won't keep searching for dependencies for installed packages + // return empty dependency list, + // as we won't keep searching for dependencies for installed packages + return DependencyList(); } - else + + std::shared_ptr latestVersion = package->GetLatestAvailableVersion(); + if (!latestVersion) { - std::shared_ptr latestVersion = package->GetLatestAvailableVersion(); - if (!latestVersion) { - info << "No package version found"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - - auto manifest = latestVersion->GetManifest(); - if (manifest.Installers.empty()) { - info << "No installers found"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - - if (!node.IsVersionOk(manifest.Version)) - { - info << "Minimum required version not available"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors - } - - std::optional installer; - auto installedVersion = package->GetInstalledVersion(); - - IPackageVersion::Metadata installationMetadata; - if (installedVersion) - { - installationMetadata = installedVersion->GetMetadata(); - } - - ManifestComparator manifestComparator(context, installationMetadata); - installer = manifestComparator.GetPreferredInstaller(manifest); - - auto nodeDependencies = installer.value().Dependencies; - - Execution::PackageToInstall packageToInstall{ - std::move(latestVersion), - std::move(installedVersion), - std::move(manifest), - std::move(installer.value()) }; - - idToPackageMap.emplace(node.Id, packageToInstall); - - return nodeDependencies; + error << Resource::String::DependenciesFlowPackageVersionNotFound; + AICLI_LOG(CLI, Error, << "Latest available version not found for package " << Utility::Normalize(packageId)); + foundError = true; + return DependencyList(); + } + + auto manifest = latestVersion->GetManifest(); + if (manifest.Installers.empty()) { + error << Resource::String::DependenciesFlowNoInstallerFound; + AICLI_LOG(CLI, Error, << "Installer not found for manifest " << Utility::Normalize(manifest.Id) << " with version" << Utility::Normalize(manifest.Version)); + foundError = true; + return DependencyList(); } + + if (!node.IsVersionOk(manifest.Version)) + { + error << Resource::String::DependenciesFlowNoMinVersion; + AICLI_LOG(CLI, Error, << "No suitable min version found for package " << Utility::Normalize(manifest.Id)); + foundError = true; + return DependencyList(); + } + + std::optional installer; + + IPackageVersion::Metadata installationMetadata; + if (installedVersion) + { + installationMetadata = installedVersion->GetMetadata(); + } + + ManifestComparator manifestComparator(context, installationMetadata); + installer = manifestComparator.GetPreferredInstaller(manifest); + + auto nodeDependencies = installer.value().Dependencies; + + Execution::PackageToInstall packageToInstall{ + std::move(latestVersion), + std::move(installedVersion), + std::move(manifest), + std::move(installer.value()) }; + + idToPackageMap.emplace(node.Id, packageToInstall); + + return nodeDependencies; } else { - info << "No matches"; //TODO localize all errors - return DependencyList(); //return empty dependency list, TODO change this to actually manage errors + error << Resource::String::DependenciesFlowNoMatches; + foundError = true; + return DependencyList(); } }); - + dependencyGraph.BuildGraph(); // maybe it's better if it already does it on the constructor? if (dependencyGraph.HasLoop()) { - info << "has loop" << std::endl; - Logging::Log().Write(Logging::Channel::CLI, Logging::Level::Warning, "Dependency loop found"); //TODO localization - //warn user but try to install either way + context.Reporter.Warn() << Resource::String::DependenciesFlowContainsLoop; } - // TODO raise error for failed packages? (if there's at least one) - const auto& installationOrder = dependencyGraph.GetInstallationOrder(); std::vector installers; - info << "order: "; //-- only for debugging for (auto const& node : installationOrder) - { - info << node.Id << ", "; //-- only for debugging - + { auto itr = idToPackageMap.find(node.Id); // if the package was already installed (with a useful version) or is the root // then there will be no installer for it on the map. @@ -245,12 +252,20 @@ namespace AppInstaller::CLI::Workflow installers.push_back(itr->second); } } - info << std::endl; //-- only for debugging + if (foundError) + { + // PM ask. + bool continueExec = context.Reporter.PromptForBoolResponse(Resource::String::DependenciesManagementError); + if (!continueExec) + { + error << Resource::String::DependenciesManagementExitMessage << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY); + } + } + // Install dependencies in the correct order context.Add(installers); - context << Workflow::InstallMultiplePackages(Resource::String::ImportCommandReportDependencies, APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED); - - // Install the root (continue) + context << Workflow::InstallMultiplePackages(m_dependencyReportMessage, APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES, {}, false, true); } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h index b97ab50995..b525461a0a 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.h +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.h @@ -43,7 +43,16 @@ namespace AppInstaller::CLI::Workflow // Required Args: None // Inputs: DependencySource // Outputs: Dependencies - void ManagePackageDependencies(Execution::Context& context); + struct ManagePackageDependencies : public WorkflowTask + { + ManagePackageDependencies(AppInstaller::StringResource::StringId dependencyReportMessage) : + WorkflowTask("ReportDependencies"), m_dependencyReportMessage(dependencyReportMessage) {} + + void operator()(Execution::Context& context) const override; + + private: + AppInstaller::StringResource::StringId m_dependencyReportMessage; + }; // Sets up the source used to get the dependencies. // Required Args: None diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index c13738ae9e..e59f94fad1 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -628,14 +628,18 @@ namespace AppInstaller::CLI::Workflow Workflow::ShowPackageAgreements(/* ensureAcceptance */ true) << Workflow::GetDependenciesFromInstaller << Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << - Workflow::ManagePackageDependencies << + Workflow::ManagePackageDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) << Workflow::InstallPackageInstaller; } void InstallMultiplePackages::operator()(Execution::Context& context) const { - // Show all license agreements before installing anything - context << Workflow::EnsurePackageAgreementsAcceptanceForMultipleInstallers; + if (m_ensurePackageAgreements) + { + // Show all license agreements before installing anything + context << Workflow::EnsurePackageAgreementsAcceptanceForMultipleInstallers; + } + if (context.IsTerminated()) { return; @@ -669,10 +673,12 @@ namespace AppInstaller::CLI::Workflow installContext.Add(package.InstalledPackageVersion); installContext.Add(package.Installer); - installContext << - Workflow::ReportIdentityAndInstallationDisclaimer << - Workflow::InstallPackageInstaller; - + installContext << Workflow::ReportIdentityAndInstallationDisclaimer; + if (!m_ignorePackageDependencies) + { + installContext << Workflow::ManagePackageDependencies(m_dependenciesReportMessage); + } + installContext << Workflow::InstallPackageInstaller; installContext.Reporter.Info() << std::endl; if (installContext.IsTerminated()) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 1fb2a5240b..8e3e31b167 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -121,7 +121,10 @@ namespace AppInstaller::CLI::Workflow struct ReportInstallerResult : public WorkflowTask { ReportInstallerResult(std::string_view installerType, HRESULT hr, bool isHResult = false) : - WorkflowTask("ReportInstallerResult"), m_installerType(installerType), m_hr(hr), m_isHResult(isHResult) {} + WorkflowTask("ReportInstallerResult"), + m_installerType(installerType), + m_hr(hr), + m_isHResult(isHResult) {} void operator()(Execution::Context& context) const override; @@ -166,11 +169,18 @@ namespace AppInstaller::CLI::Workflow // Outputs: None struct InstallMultiplePackages : public WorkflowTask { - InstallMultiplePackages(StringResource::StringId dependenciesReportMessage, HRESULT resultOnFailure, std::vector&& ignorableInstallResults = {}) : + InstallMultiplePackages( + StringResource::StringId dependenciesReportMessage, + HRESULT resultOnFailure, + std::vector&& ignorableInstallResults = {}, + bool ensurePackageAgreements = true, + bool ignoreDependencies = false) : WorkflowTask("InstallMultiplePackages"), m_dependenciesReportMessage(dependenciesReportMessage), m_resultOnFailure(resultOnFailure), - m_ignorableInstallResults(std::move(ignorableInstallResults)) {} + m_ignorableInstallResults(std::move(ignorableInstallResults)), + m_ignorePackageDependencies(ignoreDependencies), + m_ensurePackageAgreements(ensurePackageAgreements) {} void operator()(Execution::Context& context) const override; @@ -178,6 +188,8 @@ namespace AppInstaller::CLI::Workflow HRESULT m_resultOnFailure; std::vector m_ignorableInstallResults; StringResource::StringId m_dependenciesReportMessage; + bool m_ignorePackageDependencies; + bool m_ensurePackageAgreements; }; // Stores the existing set of packages in ARP. diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index ff444d4df0..a33d129e45 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -173,7 +173,7 @@ namespace AppInstaller::CLI::Workflow { info << " "_liv << dependency.Id; if (dependency.MinVersion) - info << " [>= " << dependency.MinVersion.value().ToString() << "]"; + info << " [>= " << dependency.MinVersion.value().ToString() << "]"; info << std::endl; }); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 33b5561bcd..918f8682d5 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -939,6 +939,46 @@ Configuration is disabled due to Group Policy. This package requires the following dependencies: Install and Upgrade commands sentence showed before reporting dependencies + + Installing dependencies: + Install and Upgrade commands sentence showed before reporting dependencies + + + Dependency source not found + Install and Upgrade commands sentence showed before reporting dependencies + + + Too many matches + Install and Upgrade commands sentence showed before reporting dependencies + + + No package version found + Install and Upgrade commands sentence showed before reporting dependencies + + + No installers found + Install and Upgrade commands sentence showed before reporting dependencies + + + Minimum required version not available + Install and Upgrade commands sentence showed before reporting dependencies + + + No matches + Install and Upgrade commands sentence showed before reporting dependencies + + + Has loop + Install and Upgrade commands sentence showed before reporting dependencies + + + Error processing package depencies, do you wish to continue installation? + Install and Upgrade commands sentence showed before reporting dependencies + + + Error processing package dependencies, exiting... + Install and Upgrade commands sentence showed before reporting dependencies + Packages diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index d224983b8c..511b595f0c 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -615,6 +615,8 @@ void OverrideForShellExecute(TestContext& context, std::vector& inst context.Override({ RenameDownloadedInstaller, [](TestContext&) { } }); + + OverrideForUpdateInstallerMotw(context); } void OverrideForDirectMsi(TestContext& context) @@ -2118,89 +2120,117 @@ TEST_CASE("InstallFlowMultiLocale_RequirementNotSatisfied", "[InstallFlow][workf REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); } -TEST_CASE("InstallFlowMultiLocale_RequirementSatisfied", "[InstallFlow][workflow]") +TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") +{ + std::ostringstream validateOutput; + TestContext context{ validateOutput, std::cin }; + context.Args.AddArg(Execution::Args::Type::ValidateManifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); + + TestUserSettings settings; + settings.Set({ true }); + + ValidateCommand validate({}); + validate.Execute(context); + INFO(validateOutput.str()); + + // Verify all types of dependencies are printed + REQUIRE(validateOutput.str().find(Resource::LocString(Resource::String::ValidateCommandReportDependencies).get()) != std::string::npos); + REQUIRE(validateOutput.str().find("WindowsFeaturesDep") != std::string::npos); + REQUIRE(validateOutput.str().find("WindowsLibrariesDep") != std::string::npos); + // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) + REQUIRE(validateOutput.str().find("Package.Dep1-x64 [>= 1.0]") != std::string::npos); + REQUIRE(validateOutput.str().find("Package.Dep2-x64") != std::string::npos); + REQUIRE(validateOutput.str().find("Package.Dep2-x64 [") == std::string::npos); + REQUIRE(validateOutput.str().find("ExternalDep") != std::string::npos); +} + +TEST_CASE("DependenciesMultideclaration_InstallerDependenciesPreference", "[dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); - context.Args.AddArg(Execution::Args::Type::Locale, "fr-FR"sv); + OverrideDependencySource(context); + + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesMultideclaration.yaml").GetPath().u8string()); + + TestUserSettings settings; + settings.Set({ true }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - // Verify Installer is called and parameters are passed in. - REQUIRE(std::filesystem::exists(installResultPath.GetPath())); - std::ifstream installResultFile(installResultPath.GetPath()); - REQUIRE(installResultFile.is_open()); - std::string installResultStr; - std::getline(installResultFile, installResultStr); - REQUIRE(installResultStr.find("/fr-FR") != std::string::npos); + // Verify installer dependencies are shown + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); + REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); + // and root dependencies are not + REQUIRE(installOutput.str().find("PreviewIISOnRoot") == std::string::npos); } -TEST_CASE("InstallFlowMultiLocale_PreferenceNoBetterLocale", "[InstallFlow][workflow]") +TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); + OverrideDependencySource(context); + + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesOnRoot.yaml").GetPath().u8string()); TestUserSettings settings; - settings.Set({ "zh-CN" }); + settings.Set({ true }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - // Verify Installer is called and parameters are passed in. - REQUIRE(std::filesystem::exists(installResultPath.GetPath())); - std::ifstream installResultFile(installResultPath.GetPath()); - REQUIRE(installResultFile.is_open()); - std::string installResultStr; - std::getline(installResultFile, installResultStr); - REQUIRE(installResultStr.find("/unknown") != std::string::npos); + // Verify root dependencies are shown + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); + REQUIRE(installOutput.str().find("PreviewIISOnRoot") != std::string::npos); } -TEST_CASE("InstallFlowMultiLocale_PreferenceWithBetterLocale", "[InstallFlow][workflow]") +TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideForShellExecute(context); - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); + OverrideOpenSourceForDependencies(context); + OverrideForShellExecute(context, installationOrder); + + context.Args.AddArg(Execution::Args::Type::Query, "NeedsToInstallBFirst"sv); TestUserSettings settings; - settings.Set({ "en-US" }); + settings.Set({ true }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - // Verify Installer is called and parameters are passed in. - REQUIRE(std::filesystem::exists(installResultPath.GetPath())); - std::ifstream installResultFile(installResultPath.GetPath()); - REQUIRE(installResultFile.is_open()); - std::string installResultStr; - std::getline(installResultFile, installResultStr); - REQUIRE(installResultStr.find("/en-GB") != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); + + // Verify installers are called in order + REQUIRE(installationOrder.size() == 3); + REQUIRE(installationOrder.at(0).Id == "B"); + REQUIRE(installationOrder.at(1).Id == "C"); + REQUIRE(installationOrder.at(2).Id == "NeedsToInstallBFirst"); } -TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") +TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideForShellExecute(context); - OverrideDependencySource(context); + OverrideOpenSourceForDependencies(context); + OverrideForShellExecute(context, installationOrder); - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Query, "DependenciesInstalled"sv); TestUserSettings settings; settings.Set({ true }); @@ -2209,9 +2239,11 @@ TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") install.Execute(context); INFO(installOutput.str()); - // Verify all types of dependencies are printed - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); - REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); + REQUIRE(installationOrder.size() == 1); + REQUIRE(installationOrder.at(0).Id == "DependenciesInstalled"); + // dependencies of an installed package will not be checked nor added to the graph + REQUIRE(installOutput.str().find("installed1Dep") == std::string::npos); } TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph][dependencies]") @@ -2232,20 +2264,19 @@ TEST_CASE("DependencyGraph_Loop", "[InstallFlow][workflow][dependencyGraph][depe install.Execute(context); INFO(installOutput.str()); - REQUIRE(installOutput.str().find("has loop") != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) != std::string::npos); } -TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]") +TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); - std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideOpenSourceForDependencies(context); - OverrideForShellExecute(context, installationOrder); + OverrideForShellExecute(context); + OverrideDependencySource(context); - context.Args.AddArg(Execution::Args::Type::Query, "DependencyAlreadyInStackButNoLoop"sv); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); TestUserSettings settings; settings.Set({ true }); @@ -2254,18 +2285,12 @@ TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGr install.Execute(context); INFO(installOutput.str()); - REQUIRE(installOutput.str().find("has loop") == std::string::npos); - REQUIRE(installOutput.str().find("order: B, C, F, DependencyAlreadyInStackButNoLoop,") != std::string::npos); - - // Verify installers are called in order - REQUIRE(installationOrder.size() == 4); - REQUIRE(installationOrder.at(0).Id == "B"); - REQUIRE(installationOrder.at(1).Id == "C"); - REQUIRE(installationOrder.at(2).Id == "F"); - REQUIRE(installationOrder.at(3).Id == "DependencyAlreadyInStackButNoLoop"); + // Verify all types of dependencies are printed + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); + REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } -TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]") +TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::vector installationOrder; @@ -2275,7 +2300,7 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context, installationOrder); - context.Args.AddArg(Execution::Args::Type::Query, "PathBetweenBranchesButNoLoop"sv); + context.Args.AddArg(Execution::Args::Type::Query, "DependenciesValidMinVersions"sv); TestUserSettings settings; settings.Set({ true }); @@ -2284,19 +2309,15 @@ TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph install.Execute(context); INFO(installOutput.str()); - REQUIRE(installOutput.str().find("has loop") == std::string::npos); - REQUIRE(installOutput.str().find("order: B, C, G, H, PathBetweenBranchesButNoLoop,") != std::string::npos); - - // Verify installers are called in order - REQUIRE(installationOrder.size() == 5); - REQUIRE(installationOrder.at(0).Id == "B"); - REQUIRE(installationOrder.at(1).Id == "C"); - REQUIRE(installationOrder.at(2).Id == "G"); - REQUIRE(installationOrder.at(3).Id == "H"); - REQUIRE(installationOrder.at(4).Id == "PathBetweenBranchesButNoLoop"); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); + REQUIRE(installationOrder.size() == 2); + REQUIRE(installationOrder.at(0).Id == "minVersion"); + // minVersion 1.5 is available but this requires 1.0 so that version is installed + REQUIRE(installationOrder.at(0).MinVersion.value().ToString() == "1.0"); + REQUIRE(installationOrder.at(1).Id == "DependenciesValidMinVersions"); } -TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyGraph][dependencies]") +TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]", ) { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::vector installationOrder; @@ -2306,7 +2327,7 @@ TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyG OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context, installationOrder); - context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); + context.Args.AddArg(Execution::Args::Type::Query, "PathBetweenBranchesButNoLoop"sv); TestUserSettings settings; settings.Set({ true }); @@ -2315,17 +2336,18 @@ TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyG install.Execute(context); INFO(installOutput.str()); - REQUIRE(installOutput.str().find("has loop") == std::string::npos); - REQUIRE(installOutput.str().find("order: B, C, StackOrderIsOk,") != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); // Verify installers are called in order - REQUIRE(installationOrder.size() == 3); + REQUIRE(installationOrder.size() == 5); REQUIRE(installationOrder.at(0).Id == "B"); REQUIRE(installationOrder.at(1).Id == "C"); - REQUIRE(installationOrder.at(2).Id == "StackOrderIsOk"); + REQUIRE(installationOrder.at(2).Id == "G"); + REQUIRE(installationOrder.at(3).Id == "H"); + REQUIRE(installationOrder.at(4).Id == "PathBetweenBranchesButNoLoop"); } -TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][dependencies]") +TEST_CASE("DependencyGraph_InStackNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::vector installationOrder; @@ -2335,7 +2357,7 @@ TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][de OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context, installationOrder); - context.Args.AddArg(Execution::Args::Type::Query, "NeedsToInstallBFirst"sv); + context.Args.AddArg(Execution::Args::Type::Query, "DependencyAlreadyInStackButNoLoop"sv); TestUserSettings settings; settings.Set({ true }); @@ -2344,17 +2366,17 @@ TEST_CASE("DependencyGraph_BFirst", "[InstallFlow][workflow][dependencyGraph][de install.Execute(context); INFO(installOutput.str()); - REQUIRE(installOutput.str().find("has loop") == std::string::npos); - REQUIRE(installOutput.str().find("order: B, C, NeedsToInstallBFirst,") != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); // Verify installers are called in order - REQUIRE(installationOrder.size() == 3); + REQUIRE(installationOrder.size() == 4); REQUIRE(installationOrder.at(0).Id == "B"); REQUIRE(installationOrder.at(1).Id == "C"); - REQUIRE(installationOrder.at(2).Id == "NeedsToInstallBFirst"); + REQUIRE(installationOrder.at(2).Id == "F"); + REQUIRE(installationOrder.at(3).Id == "DependencyAlreadyInStackButNoLoop"); } -TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGraph][dependencies]") +TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::vector installationOrder; @@ -2364,7 +2386,7 @@ TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGr OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context, installationOrder); - context.Args.AddArg(Execution::Args::Type::Query, "DependenciesInstalled"sv); + context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); TestUserSettings settings; settings.Set({ true }); @@ -2373,114 +2395,86 @@ TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGr install.Execute(context); INFO(installOutput.str()); - REQUIRE(installOutput.str().find("has loop") == std::string::npos); - // dependencies installed will show on the graph order but the installer will not be called - REQUIRE(installOutput.str().find("order: installed1, DependenciesInstalled,") != std::string::npos); - REQUIRE(installationOrder.size() == 1); - REQUIRE(installationOrder.at(0).Id == "DependenciesInstalled"); - // dependencies of an installed package will not be checked nor added to the graph - REQUIRE(installOutput.str().find("installed1Dep") == std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); + + // Verify installers are called in order + REQUIRE(installationOrder.size() == 3); + REQUIRE(installationOrder.at(0).Id == "B"); + REQUIRE(installationOrder.at(1).Id == "C"); + REQUIRE(installationOrder.at(2).Id == "StackOrderIsOk"); } -TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependencyGraph][dependencies]") +TEST_CASE("InstallFlowMultiLocale_RequirementSatisfied", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); - std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; - OverrideOpenSourceForDependencies(context); - OverrideForShellExecute(context, installationOrder); - - context.Args.AddArg(Execution::Args::Type::Query, "DependenciesValidMinVersions"sv); - - TestUserSettings settings; - settings.Set({ true }); + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Locale, "fr-FR"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - REQUIRE(installOutput.str().find("has loop") == std::string::npos); - // dependencies installed will show on the order but the installer will not be called - REQUIRE(installOutput.str().find("order: minVersion, DependenciesValidMinVersions,") != std::string::npos); - REQUIRE(installationOrder.size() == 2); - REQUIRE(installationOrder.at(0).Id == "minVersion"); - // minVersion 1.5 is available but this requires 1.0 so that version is installed - REQUIRE(installationOrder.at(0).MinVersion.value().ToString() == "1.0"); - REQUIRE(installationOrder.at(1).Id == "DependenciesValidMinVersions"); -} - -TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") -{ - std::ostringstream validateOutput; - TestContext context{ validateOutput, std::cin }; - context.Args.AddArg(Execution::Args::Type::ValidateManifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); - - TestUserSettings settings; - settings.Set({ true }); - - ValidateCommand validate({}); - validate.Execute(context); - INFO(validateOutput.str()); - - // Verify all types of dependencies are printed - REQUIRE(validateOutput.str().find(Resource::LocString(Resource::String::ValidateCommandReportDependencies).get()) != std::string::npos); - REQUIRE(validateOutput.str().find("WindowsFeaturesDep") != std::string::npos); - REQUIRE(validateOutput.str().find("WindowsLibrariesDep") != std::string::npos); - // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) - REQUIRE(validateOutput.str().find("Package.Dep1-x64 [>= 1.0]") != std::string::npos); - REQUIRE(validateOutput.str().find("Package.Dep2-x64") != std::string::npos); - REQUIRE(validateOutput.str().find("Package.Dep2-x64 [") == std::string::npos); - REQUIRE(validateOutput.str().find("ExternalDep") != std::string::npos); + // Verify Installer is called and parameters are passed in. + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + std::ifstream installResultFile(installResultPath.GetPath()); + REQUIRE(installResultFile.is_open()); + std::string installResultStr; + std::getline(installResultFile, installResultStr); + REQUIRE(installResultStr.find("/fr-FR") != std::string::npos); } -TEST_CASE("DependenciesMultideclaration_InstallerDependenciesPreference", "[dependencies]") +TEST_CASE("InstallFlowMultiLocale_PreferenceNoBetterLocale", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); - OverrideDependencySource(context); - - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesMultideclaration.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); TestUserSettings settings; - settings.Set({ true }); + settings.Set({ "zh-CN" }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - // Verify installer dependencies are shown - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); - REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); - // and root dependencies are not - REQUIRE(installOutput.str().find("PreviewIISOnRoot") == std::string::npos); + // Verify Installer is called and parameters are passed in. + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + std::ifstream installResultFile(installResultPath.GetPath()); + REQUIRE(installResultFile.is_open()); + std::string installResultStr; + std::getline(installResultFile, installResultStr); + REQUIRE(installResultStr.find("/unknown") != std::string::npos); } -TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies]") +TEST_CASE("InstallFlowMultiLocale_PreferenceWithBetterLocale", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; OverrideForShellExecute(context); - OverrideDependencySource(context); - - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesOnRoot.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); TestUserSettings settings; - settings.Set({ true }); + settings.Set({ "en-US" }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); - // Verify root dependencies are shown - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); - REQUIRE(installOutput.str().find("PreviewIISOnRoot") != std::string::npos); + // Verify Installer is called and parameters are passed in. + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + std::ifstream installResultFile(installResultPath.GetPath()); + REQUIRE(installResultFile.is_open()); + std::string installResultStr; + std::getline(installResultFile, installResultStr); + REQUIRE(installResultStr.find("/en-GB") != std::string::npos); } // TODO diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h index da6bf971c1..b09f83532b 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -104,6 +104,7 @@ #define APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED ((HRESULT)0x8A15010D) #define APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE ((HRESULT)0x8A15010E) #define APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY ((HRESULT)0x8A15010F) +#define APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES ((HRESULT)0x8A150110) namespace AppInstaller From 87b9e10578f77d761541e1235cc6ff4775ef163e Mon Sep 17 00:00:00 2001 From: Akinwale Alagbe Date: Tue, 5 Oct 2021 10:23:26 -0700 Subject: [PATCH 82/82] More cleanup --- .../ExecutionContextData.h | 16 ------ .../Workflows/DependenciesFlow.cpp | 2 +- .../Shared/Strings/en-us/winget.resw | 17 +++---- .../Public/winget/ManifestCommon.h | 51 +++++++++---------- 4 files changed, 31 insertions(+), 55 deletions(-) diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index 9cb7d6ee95..30d75e58f4 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -45,7 +45,6 @@ namespace AppInstaller::CLI::Execution PackageCollection, // On import and upgrade all: A collection of specific package versions to install PackagesToInstall, - InstallersToInstall, // On import: Sources for the imported packages Sources, ARPSnapshot, @@ -86,15 +85,6 @@ namespace AppInstaller::CLI::Execution uint32_t PackageSubExecutionId = 0; }; - struct InstallerToInstall - { - std::shared_ptr PackageVersion; - std::shared_ptr InstalledPackageVersion; - uint32_t PackageSubExecutionId = 0; - Manifest::ManifestInstaller Installer; - bool IsUpdate = false; - }; - namespace details { template @@ -216,12 +206,6 @@ namespace AppInstaller::CLI::Execution { using value_t = std::vector; }; - - template <> - struct DataMapping - { - using value_t = std::vector; - }; template <> struct DataMapping diff --git a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp index 7f0453ed85..17e54d8046 100644 --- a/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp @@ -255,7 +255,7 @@ namespace AppInstaller::CLI::Workflow if (foundError) { - // PM ask. + // ask PM what to do here. bool continueExec = context.Reporter.PromptForBoolResponse(Resource::String::DependenciesManagementError); if (!continueExec) { diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 69508129e2..d0b0d1d64d 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -941,43 +941,38 @@ Configuration is disabled due to Group Policy. Installing dependencies: - Install and Upgrade commands sentence showed before reporting dependencies Dependency source not found - Install and Upgrade commands sentence showed before reporting dependencies Too many matches - Install and Upgrade commands sentence showed before reporting dependencies + When node package id search yield too many matches d No package version found - Install and Upgrade commands sentence showed before reporting dependencies + When no suitagle version found for the specific package. No installers found - Install and Upgrade commands sentence showed before reporting dependencies Minimum required version not available - Install and Upgrade commands sentence showed before reporting dependencies No matches - Install and Upgrade commands sentence showed before reporting dependencies + When package search yields no matches Has loop - Install and Upgrade commands sentence showed before reporting dependencies + Dependency graph has loop - Error processing package depencies, do you wish to continue installation? - Install and Upgrade commands sentence showed before reporting dependencies + Error processing package dependencies, do you wish to continue installation? + Prompt message shown when dependencies processing yields errors. Error processing package dependencies, exiting... - Install and Upgrade commands sentence showed before reporting dependencies Packages diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 351619afb5..139aa3fbdc 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -327,11 +327,11 @@ bool HasExtension(std::string_view extension) const; DependencyGraph(const Dependency& root, const DependencyList& rootDependencies, std::function infoFunction) : m_root(root), getDependencies(infoFunction) { - adjacents[m_root] = std::vector(); - toCheck = std::vector(); + m_adjacents[m_root] = std::set(); + m_toCheck = std::vector(); rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { - toCheck.push_back(dependency); + m_toCheck.push_back(dependency); AddNode(dependency); AddAdjacent(root, dependency); }); @@ -339,13 +339,13 @@ bool HasExtension(std::string_view extension) const; DependencyGraph(const Dependency& root, std::function infoFunction) : m_root(root), getDependencies(infoFunction) { - adjacents[m_root] = std::vector(); - toCheck = std::vector(); + m_adjacents[m_root] = std::set(); + m_toCheck = std::vector(); const DependencyList& rootDependencies = getDependencies(root); rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { - toCheck.push_back(dependency); + m_toCheck.push_back(dependency); AddNode(dependency); AddAdjacent(root, dependency); }); @@ -353,23 +353,21 @@ bool HasExtension(std::string_view extension) const; void BuildGraph() { - if (toCheck.empty()) + if (m_toCheck.empty()) { return; } - for (unsigned int i = 0; i < toCheck.size(); ++i) + for (unsigned int i = 0; i < m_toCheck.size(); ++i) { - auto node = toCheck.at(i); - - const auto& nodeDependencies = getDependencies(node); - //TODO add error stream so we can report back + auto node = m_toCheck.at(i); + const auto& nodeDependencies = getDependencies(node); nodeDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { if (!HasNode(dependency)) { - toCheck.push_back(dependency); + m_toCheck.push_back(dependency); AddNode(dependency); } @@ -382,18 +380,18 @@ bool HasExtension(std::string_view extension) const; void AddNode(const Dependency& node) { - adjacents[node] = std::vector(); + m_adjacents[node] = std::set(); } void AddAdjacent(const Dependency& node,const Dependency& adjacent) { - adjacents[node].push_back(adjacent); + m_adjacents[node].emplace(adjacent); } bool HasNode(const Dependency& dependency) { - auto search = adjacents.find(dependency); - return search != adjacents.end(); + auto search = m_adjacents.find(dependency); + return search != m_adjacents.end(); } bool HasLoop() @@ -403,14 +401,14 @@ bool HasExtension(std::string_view extension) const; void CheckForLoopsAndGetOrder() { - installationOrder = std::vector(); + m_installationOrder = std::vector(); std::set visited; hasLoop = HasLoopDFS(visited, m_root); } std::vector GetInstallationOrder() { - return installationOrder; + return m_installationOrder; } private: @@ -420,8 +418,8 @@ bool HasExtension(std::string_view extension) const; bool loop = false; visited.insert(node); - auto lAdjacents = adjacents.at(node); - for (const auto& adjacent : adjacents.at(node)) + auto lAdjacents = m_adjacents.at(node); + for (const auto& adjacent : m_adjacents.at(node)) { auto search = visited.find(adjacent); if (search == visited.end()) // if not found @@ -440,23 +438,22 @@ bool HasExtension(std::string_view extension) const; } // Adding to have an order even if a loop is present - if (std::find(installationOrder.begin(), installationOrder.end(), node) == installationOrder.end()) + if (std::find(m_installationOrder.begin(), m_installationOrder.end(), node) == m_installationOrder.end()) { - installationOrder.push_back(node); + m_installationOrder.push_back(node); } return loop; } const Dependency& m_root; - std::map> adjacents; //(?) value should be a set instead of a vector? + std::map> m_adjacents; std::function getDependencies; bool hasLoop; - std::vector installationOrder; + std::vector m_installationOrder; - std::vector toCheck; - std::map failedPackages; + std::vector m_toCheck; }; InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in);