winget-cli

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

commit 30e54e4c3b47d6b9e3de3afaddb2bc0e27365782
parent cdfa268cd0cfbb8c05914ecf19af4c7ac090cffb
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Thu,  8 Feb 2024 08:50:34 -0800

Refactor pinning evaluation (#4151)

Moves the evaluation of pinning to when it is needed, rather than doing
it for every composite search. This both makes it more "pay for play"
and enables more complex scenarios in the future by allowing the call
sites to change behavior more easily.

Also fixes a few problems that I found along the way:
1. CLICore reaching in to RepoCore rather than strictly using publics
2. Move includes towards convention (`"file"` for files in the project,
`<file>` for files outside of the project)
3. Applies pins to the COM `DefaultInstallVersion` and
`IsUpdateAvailable` properties
- Because the caller can still see all available versions, they can
choose to ignore the pinned properties
Diffstat:
Msrc/AppInstallerCLICore/AppInstallerCLICore.vcxproj | 24++++++++++++------------
Msrc/AppInstallerCLICore/CheckpointManager.cpp | 7++++---
Msrc/AppInstallerCLICore/CheckpointManager.h | 7+------
Msrc/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp | 2+-
Msrc/AppInstallerCLICore/ExecutionContext.cpp | 6+++---
Msrc/AppInstallerCLICore/ExecutionContext.h | 2+-
Msrc/AppInstallerCLICore/ExecutionContextData.h | 12++++--------
Msrc/AppInstallerCLICore/PortableInstaller.cpp | 11+++++------
Msrc/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp | 13++++++++-----
Msrc/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h | 3+--
Msrc/AppInstallerCLICore/Workflows/ImportExportFlow.cpp | 4++--
Msrc/AppInstallerCLICore/Workflows/PinFlow.cpp | 31++++++++++++++-----------------
Msrc/AppInstallerCLICore/Workflows/PortableFlow.cpp | 6+++---
Msrc/AppInstallerCLICore/Workflows/UpdateFlow.cpp | 16++++++++++------
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 59++++++++++++++++++++++++++++++++---------------------------
Msrc/AppInstallerCLITests/CheckpointDatabase.cpp | 3+--
Msrc/AppInstallerCLITests/CompositeSource.cpp | 52++++++++++++++++++++++++++++++++--------------------
Msrc/AppInstallerCLITests/PackageTrackingCatalog.cpp | 10+++++-----
Msrc/AppInstallerCLITests/PinFlow.cpp | 20+-------------------
Msrc/AppInstallerCLITests/PortableIndex.cpp | 2+-
Msrc/AppInstallerCLITests/PortableInstaller.cpp | 2+-
Msrc/AppInstallerCLITests/SQLiteIndexSource.cpp | 4++--
Msrc/AppInstallerCLITests/TestCommon.cpp | 12++++++------
Msrc/AppInstallerCLITests/TestCommon.h | 8+++++---
Msrc/AppInstallerCLITests/TestSource.cpp | 17++---------------
Msrc/AppInstallerCLITests/TestSource.h | 5++---
Msrc/AppInstallerCommonCore/Pin.cpp | 5+++++
Msrc/AppInstallerCommonCore/Public/winget/Pin.h | 7+++++--
Msrc/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj | 6++++--
Msrc/AppInstallerRepositoryCore/CompositeSource.cpp | 368+++++++------------------------------------------------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp | 44+++++++++++++++++++++++++++++---------------
Dsrc/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h | 78------------------------------------------------------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/PinningIndex.h | 7++++++-
Msrc/AppInstallerRepositoryCore/Microsoft/PortableIndex.cpp | 16++++++++++++++--
Dsrc/AppInstallerRepositoryCore/Microsoft/PortableIndex.h | 62--------------------------------------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp | 18++++--------------
Msrc/AppInstallerRepositoryCore/PackageInstalledStatus.cpp | 2+-
Asrc/AppInstallerRepositoryCore/PinningData.cpp | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Public/winget/Checkpoint.h | 4++--
Asrc/AppInstallerRepositoryCore/Public/winget/CheckpointDatabase.h | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Public/winget/PinningData.h | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Public/winget/PortableIndex.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h | 38++++----------------------------------
Msrc/AppInstallerRepositoryCore/Rest/RestSource.cpp | 9++-------
Msrc/Microsoft.Management.Deployment/CatalogPackage.cpp | 24+++++++++++++++++++++---
Msrc/Microsoft.Management.Deployment/CatalogPackage.h | 3+++
47 files changed, 818 insertions(+), 770 deletions(-)

diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -223,9 +223,9 @@ <ClCompile> <Optimization>Disabled</Optimization> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatWarningAsError> @@ -249,7 +249,7 @@ <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> <ClCompile> <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatWarningAsError> <SDLCheck Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</SDLCheck> <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</EnablePREfast> @@ -265,10 +265,10 @@ <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</TreatWarningAsError> @@ -302,10 +302,10 @@ <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM64'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|Win32'">true</TreatWarningAsError> diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -6,11 +6,12 @@ #include "ExecutionContextData.h" #include <AppInstallerRuntime.h> +using namespace AppInstaller::CLI; +using namespace AppInstaller::Repository::Microsoft; +using namespace AppInstaller::SQLite; + namespace AppInstaller::Checkpoints { - using namespace AppInstaller::CLI; - using namespace AppInstaller::Repository::Microsoft; - using namespace AppInstaller::SQLite; // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h @@ -3,14 +3,9 @@ #pragma once #include "ExecutionContextData.h" #include "ExecutionContext.h" -#include "Public/winget/Checkpoint.h" +#include <winget/Checkpoint.h> #include <guiddef.h> -namespace AppInstaller::Repository::Microsoft -{ - struct CheckpointDatabase; -} - namespace AppInstaller::Checkpoints { // Reads the command arguments from the automatic checkpoint and populates the context. diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -358,7 +358,7 @@ namespace AppInstaller::CLI::Configuration { if (!package.Version.empty()) { - auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(Repository::PinBehavior::IgnorePins); + auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(); bool foundVersion = false; for (auto const& versionKey : versionKeys) { diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -6,9 +6,9 @@ #include "COMContext.h" #include "Command.h" #include "ExecutionContext.h" -#include "Public/winget/Checkpoint.h" -#include "winget/Reboot.h" -#include "winget/UserSettings.h" +#include <winget/Checkpoint.h> +#include <winget/Reboot.h> +#include <winget/UserSettings.h> using namespace AppInstaller::Checkpoints; diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h @@ -7,7 +7,7 @@ #include "ExecutionContextData.h" #include "CompletionData.h" #include "CheckpointManager.h" -#include "Public/winget/Checkpoint.h" +#include <winget/Checkpoint.h> #include <string_view> diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h @@ -5,6 +5,7 @@ #include <winget/Manifest.h> #include <winget/ARPCorrelation.h> #include <winget/Pin.h> +#include <winget/PinningData.h> #include "CompletionData.h" #include "PackageCollection.h" #include "PortableInstaller.h" @@ -18,11 +19,6 @@ #include <variant> #include <vector> -namespace AppInstaller::Repository::Microsoft -{ - struct PinningIndex; -} - namespace AppInstaller::CLI::Execution { // Names a piece of data stored in the context by a workflow step. @@ -63,7 +59,7 @@ namespace AppInstaller::CLI::Execution AllowedArchitectures, AllowUnknownScope, PortableInstaller, - PinningIndex, + PinningData, Pins, ConfigurationContext, DownloadDirectory, @@ -249,9 +245,9 @@ namespace AppInstaller::CLI::Execution }; template <> - struct DataMapping<Data::PinningIndex> + struct DataMapping<Data::PinningData> { - using value_t = std::shared_ptr<Repository::Microsoft::PinningIndex>; + using value_t = Pinning::PinningData; }; template <> diff --git a/src/AppInstallerCLICore/PortableInstaller.cpp b/src/AppInstallerCLICore/PortableInstaller.cpp @@ -3,12 +3,11 @@ #include "pch.h" #include "ExecutionContext.h" #include "PortableInstaller.h" -#include "winget/Manifest.h" -#include "winget/ManifestCommon.h" -#include "winget/Filesystem.h" -#include "winget/PathVariable.h" -#include "Microsoft/PortableIndex.h" -#include "Microsoft/Schema/IPortableIndex.h" +#include <winget/Manifest.h> +#include <winget/ManifestCommon.h> +#include <winget/Filesystem.h> +#include <winget/PathVariable.h> +#include <winget/PortableIndex.h> #include <AppInstallerErrors.h> #include <AppInstallerRuntime.h> diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "DependencyNodeProcessor.h" #include "ManifestComparator.h" +#include <winget/PinningData.h> using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; @@ -42,17 +43,19 @@ namespace AppInstaller::CLI::Workflow auto packageId = package->GetProperty(PackageProperty::Id); m_nodePackageInstalledVersion = package->GetInstalledVersion(); - PinBehavior pinBehavior; if (m_context.Args.Contains(Execution::Args::Type::Force)) { - pinBehavior = PinBehavior::IgnorePins; + m_nodePackageLatestVersion = package->GetLatestAvailableVersion(); } else { - pinBehavior = m_context.Args.Contains(Execution::Args::Type::IncludePinned) ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins; - } + Pinning::PinBehavior pinBehavior = m_context.Args.Contains(Execution::Args::Type::IncludePinned) ? Pinning::PinBehavior::IncludePinned : Pinning::PinBehavior::ConsiderPins; + + Pinning::PinningData pinningData{ Pinning::PinningData::Disposition::ReadOnly }; + auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, package->GetInstalledVersion()); - m_nodePackageLatestVersion = package->GetLatestAvailableVersion(pinBehavior); + m_nodePackageLatestVersion = evaluator.GetLatestAvailableVersionForPins(package); + } if (m_nodePackageInstalledVersion && dependencyNode.IsVersionOk(Utility::Version(m_nodePackageInstalledVersion->GetProperty(PackageVersionProperty::Version)))) { diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h @@ -1,10 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "pch.h" #include <winget/RepositorySearch.h> #include "ExecutionContext.h" -#include "winget/ManifestCommon.h" +#include <winget/ManifestCommon.h> using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -67,13 +67,13 @@ namespace AppInstaller::CLI::Workflow { if (!checkVersion) { - return package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + return package->GetLatestAvailableVersion(); } auto availablePackageVersion = package->GetAvailableVersion({ "", version, channel }); if (!availablePackageVersion) { - availablePackageVersion = package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + availablePackageVersion = package->GetLatestAvailableVersion(); if (availablePackageVersion) { // Warn installed version is not available. diff --git a/src/AppInstallerCLICore/Workflows/PinFlow.cpp b/src/AppInstallerCLICore/Workflows/PinFlow.cpp @@ -4,12 +4,10 @@ #include "Resources.h" #include "PinFlow.h" #include "TableOutput.h" -#include "Microsoft/PinningIndex.h" -#include <winget/SQLiteStorageBase.h> -#include "winget/RepositorySearch.h" +#include <winget/PinningData.h> +#include <winget/RepositorySearch.h> using namespace AppInstaller::Repository; -using namespace AppInstaller::SQLite; namespace AppInstaller::CLI::Workflow { @@ -81,22 +79,21 @@ namespace AppInstaller::CLI::Workflow void OpenPinningIndex::operator()(Execution::Context& context) const { - auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; - auto pinningIndex = PinningIndex::OpenOrCreateDefault(openDisposition); - if (!pinningIndex) + auto pinningData = Pinning::PinningData{ m_readOnly ? Pinning::PinningData::Disposition::ReadOnly : Pinning::PinningData::Disposition::ReadWrite }; + if (!m_readOnly && !pinningData) { AICLI_LOG(CLI, Error, << "Unable to open pinning index."); context.Reporter.Error() << Resource::String::PinCannotOpenIndex << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CANNOT_OPEN_PINNING_INDEX); } - context.Add<Execution::Data::PinningIndex>(std::move(pinningIndex)); + context.Add<Execution::Data::PinningData>(std::move(pinningData)); } void GetAllPins(Execution::Context& context) { AICLI_LOG(CLI, Info, << "Getting all existing pins"); - context.Add<Execution::Data::Pins>(context.Get<Execution::Data::PinningIndex>()->GetAllPins()); + context.Add<Execution::Data::Pins>(context.Get<Execution::Data::PinningData>().GetAllPins()); } void SearchPin(Execution::Context& context) @@ -104,12 +101,12 @@ namespace AppInstaller::CLI::Workflow auto pinKeys = GetPinKeysForPackage(context); auto package = context.Get<Execution::Data::Package>(); - auto pinningIndex = context.Get<Execution::Data::PinningIndex>(); + auto pinningData = context.Get<Execution::Data::PinningData>(); std::vector<Pinning::Pin> pins; for (const auto& pinKey : pinKeys) { - auto pin = pinningIndex->GetPin(pinKey); + auto pin = pinningData.GetPin(pinKey); if (pin) { pins.emplace_back(std::move(pin.value())); @@ -124,7 +121,7 @@ namespace AppInstaller::CLI::Workflow auto pinKeys = GetPinKeysForPackage(context); auto package = context.Get<Execution::Data::Package>(); - auto pinningIndex = context.Get<Execution::Data::PinningIndex>(); + auto pinningData = context.Get<Execution::Data::PinningData>(); auto installedVersion = context.Get<Execution::Data::InstalledPackageVersion>(); std::vector<Pinning::Pin> pinsToAddOrUpdate; @@ -133,7 +130,7 @@ namespace AppInstaller::CLI::Workflow auto pin = CreatePin(context, pinKey); AICLI_LOG(CLI, Info, << "Evaluating Pin " << pin.ToString()); - auto existingPin = pinningIndex->GetPin(pinKey); + auto existingPin = pinningData.GetPin(pinKey); if (existingPin) { Utility::LocIndString packageNameToReport; @@ -183,7 +180,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& pin : pinsToAddOrUpdate) { - pinningIndex->AddOrUpdatePin(pin); + pinningData.AddOrUpdatePin(pin); } context.Reporter.Info() << Resource::String::PinAdded << std::endl; @@ -195,7 +192,7 @@ namespace AppInstaller::CLI::Workflow auto package = context.Get<Execution::Data::Package>(); auto pins = context.Get<Execution::Data::Pins>(); - auto pinningIndex = context.Get<Execution::Data::PinningIndex>(); + auto pinningData = context.Get<Execution::Data::PinningData>(); bool pinExists = false; // Note that if a source was specified in the command line, @@ -206,7 +203,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& pin : pins) { AICLI_LOG(CLI, Info, << "Removing Pin " << pin.GetKey().ToString()); - pinningIndex->RemovePin(pin.GetKey()); + pinningData.RemovePin(pin.GetKey()); pinExists = true; } @@ -307,7 +304,7 @@ namespace AppInstaller::CLI::Workflow } } - if (context.Get<Execution::Data::PinningIndex>()->ResetAllPins(sourceId)) + if (context.Get<Execution::Data::PinningData>().ResetAllPins(sourceId)) { context.Reporter.Info() << Resource::String::PinResetSuccessful << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/PortableFlow.cpp b/src/AppInstallerCLICore/Workflows/PortableFlow.cpp @@ -4,9 +4,9 @@ #include "PortableFlow.h" #include "PortableInstaller.h" #include "WorkflowBase.h" -#include "winget/Filesystem.h" -#include "winget/PortableFileEntry.h" -#include <Microsoft/PortableIndex.h> +#include <winget/Filesystem.h> +#include <winget/PortableFileEntry.h> +#include <winget/PortableIndex.h> using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -7,7 +7,7 @@ #include "InstallFlow.h" #include "UpdateFlow.h" #include "ManifestComparator.h" -#include <Microsoft/PinningIndex.h> +#include <winget/PinningData.h> using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; @@ -71,6 +71,9 @@ namespace AppInstaller::CLI::Workflow // we include packages with Pinning pins const bool includePinned = m_isSinglePackage || context.Args.Contains(Execution::Args::Type::IncludePinned); + PinningData pinningData{ PinningData::Disposition::ReadOnly }; + auto evaluator = pinningData.CreatePinStateEvaluator(includePinned ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins, package->GetInstalledVersion()); + // The version keys should have already been sorted by version const auto& versionKeys = package->GetAvailableVersionKeys(); // Assume that no update versions are applicable @@ -85,12 +88,14 @@ namespace AppInstaller::CLI::Workflow { upgradeVersionAvailable = true; } + + auto packageVersion = package->GetAvailableVersion(key); + // Check if the package is pinned - if (key.PinnedState == Pinning::PinType::Blocking || - key.PinnedState == Pinning::PinType::Gating || - (key.PinnedState == Pinning::PinType::Pinning && !includePinned)) + PinType pinType = evaluator.EvaluatePinType(packageVersion); + if (pinType != Pinning::PinType::Unknown) { - AICLI_LOG(CLI, Info, << "Package [" << package->GetProperty(PackageProperty::Id) << " with Version[" << key.Version << "] from Source[" << key.SourceId << "] has a Pin with type[" << ToString(key.PinnedState) << "]"); + AICLI_LOG(CLI, Info, << "Package [" << package->GetProperty(PackageProperty::Id) << " with Version[" << key.Version << "] from Source[" << key.SourceId << "] has a Pin with type[" << ToString(pinType) << "]"); if (context.Args.Contains(Execution::Args::Type::Force)) { AICLI_LOG(CLI, Info, << "Ignoring pin due to --force argument"); @@ -102,7 +107,6 @@ namespace AppInstaller::CLI::Workflow } } - auto packageVersion = package->GetAvailableVersion(key); auto manifest = packageVersion->GetManifest(); // Check applicable Installer diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -9,6 +9,7 @@ #include <winget/ExperimentalFeature.h> #include <winget/ManifestYamlParser.h> #include <winget/Pin.h> +#include <winget/PinningData.h> #include <winget/Runtime.h> using namespace std::string_literals; @@ -627,7 +628,7 @@ namespace AppInstaller::CLI::Workflow for (size_t i = 0; i < searchResult.Matches.size(); ++i) { - auto latestVersion = searchResult.Matches[i].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto latestVersion = searchResult.Matches[i].Package->GetLatestAvailableVersion(); table.OutputLine({ latestVersion->GetProperty(PackageVersionProperty::Name), @@ -738,7 +739,7 @@ namespace AppInstaller::CLI::Workflow auto package = searchResult.Matches[i].Package; std::string sourceName; - auto latest = package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto latest = package->GetLatestAvailableVersion(); if (latest) { auto source = latest->GetSource(); @@ -794,14 +795,18 @@ namespace AppInstaller::CLI::Workflow pinBehavior = PinBehavior::IgnorePins; } + PinningData pinningData{ PinningData::Disposition::ReadOnly }; + for (const auto& match : searchResult.Matches) { auto installedVersion = match.Package->GetInstalledVersion(); if (installedVersion) { - auto latestVersion = match.Package->GetLatestAvailableVersion(pinBehavior); - bool updateAvailable = match.Package->IsUpdateAvailable(pinBehavior); + auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, installedVersion); + + auto latestVersion = evaluator.GetLatestAvailableVersionForPins(match.Package); + bool updateAvailable = evaluator.IsUpdate(latestVersion); bool updateIsPinned = false; if (m_onlyShowUpgrades && !context.Args.Contains(Execution::Args::Type::IncludeUnknown) && Utility::Version(installedVersion->GetProperty(PackageVersionProperty::Version)).IsUnknown() && updateAvailable) @@ -813,7 +818,10 @@ namespace AppInstaller::CLI::Workflow if (m_onlyShowUpgrades && !updateAvailable) { - bool updateAvailableWithoutPins = match.Package->IsUpdateAvailable(PinBehavior::IgnorePins); + // Reuse the evaluator to check if there is an update outside of the pinning + auto unpinnedLatestVersion = match.Package->GetLatestAvailableVersion(); + bool updateAvailableWithoutPins = evaluator.IsUpdate(unpinnedLatestVersion); + if (updateAvailableWithoutPins) { // When given the --include-pinned argument, report blocking and gating pins in a separate table. @@ -823,7 +831,7 @@ namespace AppInstaller::CLI::Workflow updateIsPinned = true; // Override these so we generate the table line below. - latestVersion = match.Package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + latestVersion = std::move(unpinnedLatestVersion); updateAvailable = true; } else @@ -1005,26 +1013,29 @@ namespace AppInstaller::CLI::Workflow { bool isPinned = false; + PinBehavior pinBehavior; + if (context.Args.Contains(Execution::Args::Type::Force)) + { + // --force ignores any pins + pinBehavior = PinBehavior::IgnorePins; + } + else + { + pinBehavior = context.Args.Contains(Execution::Args::Type::IncludePinned) ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins; + } + + PinningData pinningData{ PinningData::Disposition::ReadOnly }; + auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, package->GetInstalledVersion()); + // TODO: The logic here will probably have to get more difficult once we support channels if (Utility::IsEmptyOrWhitespace(m_version) && Utility::IsEmptyOrWhitespace(m_channel)) { - PinBehavior pinBehavior; - if (context.Args.Contains(Execution::Args::Type::Force)) - { - // --force ignores any pins - pinBehavior = PinBehavior::IgnorePins; - } - else - { - pinBehavior = context.Args.Contains(Execution::Args::Type::IncludePinned) ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins; - } - - requestedVersion = package->GetLatestAvailableVersion(pinBehavior); + requestedVersion = evaluator.GetLatestAvailableVersionForPins(package); if (!requestedVersion) { // Check whether we didn't find the latest version because it was pinned or because there wasn't one - auto latestVersion = package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto latestVersion = package->GetLatestAvailableVersion(); if (latestVersion) { isPinned = true; @@ -1033,14 +1044,8 @@ namespace AppInstaller::CLI::Workflow } else { - auto requestedVersionAndPin = package->GetAvailableVersionAndPin(key); - requestedVersion = requestedVersionAndPin.first; - auto pin = requestedVersionAndPin.second; - - isPinned = - pin == Pinning::PinType::Blocking || - pin == Pinning::PinType::Gating || - (pin == Pinning::PinType::Pinning && !context.Args.Contains(Execution::Args::Type::IncludePinned)); + requestedVersion = package->GetAvailableVersion(key); + isPinned = evaluator.EvaluatePinType(requestedVersion) != PinType::Unknown; } if (isPinned) diff --git a/src/AppInstallerCLITests/CheckpointDatabase.cpp b/src/AppInstallerCLITests/CheckpointDatabase.cpp @@ -2,14 +2,13 @@ // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" -#include <Microsoft/CheckpointDatabase.h> +#include <winget/CheckpointDatabase.h> #include <Public/winget/Checkpoint.h> using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; -using namespace AppInstaller::Repository::Microsoft::Schema; using namespace AppInstaller::Checkpoints; TEST_CASE("CheckpointDatabaseCreateLatestAndReopen", "[checkpointDatabase]") diff --git a/src/AppInstallerCLITests/CompositeSource.cpp b/src/AppInstallerCLITests/CompositeSource.cpp @@ -9,6 +9,7 @@ #include <Microsoft/PinningIndex.h> #include <PackageTrackingCatalogSourceFactory.h> #include <winget/Pin.h> +#include <winget/PinningData.h> using namespace std::string_literals; using namespace std::string_view_literals; @@ -331,8 +332,8 @@ TEST_CASE("CompositeSource_MultiMatch_FindsStrongMatch", "[CompositeSource]") REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetInstalledVersion()); REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Name).get() == name); - REQUIRE(!Version(result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Version)).IsUnknown()); + REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name).get() == name); + REQUIRE(!Version(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Version)).IsUnknown()); } TEST_CASE("CompositeSource_MultiMatch_DoesNotFindStrongMatch", "[CompositeSource]") @@ -550,11 +551,9 @@ TEST_CASE("CompositePackage_AvailableVersions_ChannelFilteredOut", "[CompositeSo REQUIRE(versionKeys.size() == 1); REQUIRE(versionKeys[0].Channel.empty()); - auto latestVersion = result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto latestVersion = result.Matches[0].Package->GetLatestAvailableVersion(); REQUIRE(latestVersion); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Channel).get().empty()); - - REQUIRE(!result.Matches[0].Package->IsUpdateAvailable(PinBehavior::IgnorePins)); } TEST_CASE("CompositePackage_AvailableVersions_NoChannelFilteredOut", "[CompositeSource]") @@ -586,11 +585,9 @@ TEST_CASE("CompositePackage_AvailableVersions_NoChannelFilteredOut", "[Composite REQUIRE(versionKeys.size() == 1); REQUIRE(versionKeys[0].Channel == channel); - auto latestVersion = result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto latestVersion = result.Matches[0].Package->GetLatestAvailableVersion(); REQUIRE(latestVersion); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Channel).get() == channel); - - REQUIRE(result.Matches[0].Package->IsUpdateAvailable(PinBehavior::IgnorePins)); } TEST_CASE("CompositeSource_MultipleAvailableSources_MatchAll", "[CompositeSource]") @@ -630,7 +627,7 @@ TEST_CASE("CompositeSource_MultipleAvailableSources_MatchAll", "[CompositeSource REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetInstalledVersion()); REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 2); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Name).get() == firstName); + REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name).get() == firstName); } TEST_CASE("CompositeSource_MultipleAvailableSources_MatchSecond", "[CompositeSource]") @@ -659,7 +656,7 @@ TEST_CASE("CompositeSource_MultipleAvailableSources_MatchSecond", "[CompositeSou REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetInstalledVersion()); REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Name).get() == secondName); + REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name).get() == secondName); } TEST_CASE("CompositeSource_MultipleAvailableSources_ReverseMatchBoth", "[CompositeSource]") @@ -731,7 +728,7 @@ TEST_CASE("CompositeSource_AvailableSearchFailure", "[CompositeSource]") REQUIRE(result.Matches.size() == 1); - auto pfns = result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); + auto pfns = result.Matches[0].Package->GetLatestAvailableVersion()->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); REQUIRE(pfns.size() == 1); REQUIRE(pfns[0] == pfn); @@ -877,7 +874,7 @@ TEST_CASE("CompositeSource_TrackingPackageFound", "[CompositeSource]") REQUIRE(result.Matches[0].Package); REQUIRE(result.Matches[0].Package->GetInstalledVersion()); REQUIRE(result.Matches[0].Package->GetInstalledVersion()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)); + REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()); } TEST_CASE("CompositeSource_TrackingPackageFound_MetadataPopulatedFromTracking", "[CompositeSource]") @@ -968,7 +965,7 @@ TEST_CASE("CompositeSource_TrackingFound_AvailableNot", "[CompositeSource]") REQUIRE(result.Matches[0].Package); REQUIRE(result.Matches[0].Package->GetInstalledVersion()); REQUIRE(result.Matches[0].Package->GetInstalledVersion()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); - REQUIRE(!result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)); + REQUIRE(!result.Matches[0].Package->GetLatestAvailableVersion()); } TEST_CASE("CompositeSource_TrackingFound_AvailablePath", "[CompositeSource]") @@ -1009,7 +1006,7 @@ TEST_CASE("CompositeSource_TrackingFound_AvailablePath", "[CompositeSource]") REQUIRE(result.Matches[0].Package); REQUIRE(result.Matches[0].Package->GetInstalledVersion()); REQUIRE(result.Matches[0].Package->GetInstalledVersion()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)); + REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()); } TEST_CASE("CompositeSource_TrackingFound_NotInstalled", "[CompositeSource]") @@ -1060,22 +1057,34 @@ struct ExpectedResultForPinBehavior std::optional<std::string> LatestAvailableVersion; }; +struct ExpectedPackageVersionKey : public PackageVersionKey +{ + ExpectedPackageVersionKey(Utility::NormalizedString sourceId, Utility::NormalizedString version, Utility::NormalizedString channel, PinType pinType) : + PackageVersionKey(sourceId, version, channel), PinnedState(pinType) {} + + PinType PinnedState; +}; + struct ExpectedResultsForPinning { std::map<PinBehavior, ExpectedResultForPinBehavior> ResultsForPinBehavior; - std::vector<PackageVersionKey> AvailableVersions; + std::vector<ExpectedPackageVersionKey> AvailableVersions; }; void RequireExpectedResultsWithPin(std::shared_ptr<IPackage> package, const ExpectedResultsForPinning& expectedResult) { + PinningData pinningData{ PinningData::Disposition::ReadOnly }; + for (const auto& entry : expectedResult.ResultsForPinBehavior) { auto pinBehavior = entry.first; const auto& result = entry.second; - REQUIRE(package->IsUpdateAvailable(pinBehavior) == result.IsUpdateAvailable); + auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, package->GetInstalledVersion()); + auto latestAvailable = evaluator.GetLatestAvailableVersionForPins(package); + + REQUIRE(evaluator.IsUpdate(latestAvailable) == result.IsUpdateAvailable); - auto latestAvailable = package->GetLatestAvailableVersion(pinBehavior); if (result.LatestAvailableVersion.has_value()) { REQUIRE(latestAvailable); @@ -1091,10 +1100,13 @@ void RequireExpectedResultsWithPin(std::shared_ptr<IPackage> package, const Expe REQUIRE(availableVersionKeys.size() == expectedResult.AvailableVersions.size()); for (size_t i = 0; i < availableVersionKeys.size(); ++i) { + auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, package->GetInstalledVersion()); + + auto packageVersion = package->GetAvailableVersion(expectedResult.AvailableVersions[i]); + REQUIRE(packageVersion); REQUIRE(availableVersionKeys[i].SourceId == expectedResult.AvailableVersions[i].SourceId); REQUIRE(availableVersionKeys[i].Version == expectedResult.AvailableVersions[i].Version); - REQUIRE(availableVersionKeys[i].PinnedState == expectedResult.AvailableVersions[i].PinnedState); - REQUIRE(package->GetAvailableVersion(expectedResult.AvailableVersions[i])); + REQUIRE(evaluator.EvaluatePinType(packageVersion) == expectedResult.AvailableVersions[i].PinnedState); } } @@ -1119,7 +1131,7 @@ TEST_CASE("CompositeSource_Pinning_AvailableVersionPinned", "[CompositeSource][P auto manifest2 = MakeDefaultManifest("1.0.1"sv); auto manifest3 = MakeDefaultManifest("1.1.0"sv); auto package = TestPackage::Make( - std::vector<Manifest::Manifest>{ manifest1, manifest2, manifest3 }, + std::vector<Manifest::Manifest>{ manifest3, manifest2, manifest1 }, setup.Available); SearchResult result; diff --git a/src/AppInstallerCLITests/PackageTrackingCatalog.cpp b/src/AppInstallerCLITests/PackageTrackingCatalog.cpp @@ -87,7 +87,7 @@ TEST_CASE("TrackingCatalog_Install", "[tracking_catalog]") SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); - auto trackingVersion = resultAfter.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto trackingVersion = resultAfter.Matches[0].Package->GetLatestAvailableVersion(); REQUIRE(trackingVersion); auto metadata = trackingVersion->GetMetadata(); @@ -113,7 +113,7 @@ TEST_CASE("TrackingCatalog_Reinstall", "[tracking_catalog]") SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Name) == + REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name) == manifest.DefaultLocalization.Get<Localization::PackageName>()); // Change name @@ -124,7 +124,7 @@ TEST_CASE("TrackingCatalog_Reinstall", "[tracking_catalog]") SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Name) == + REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name) == newName); } @@ -147,7 +147,7 @@ TEST_CASE("TrackingCatalog_Upgrade", "[tracking_catalog]") SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Version) == + REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); // Change name @@ -157,7 +157,7 @@ TEST_CASE("TrackingCatalog_Upgrade", "[tracking_catalog]") SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetProperty(PackageVersionProperty::Version) == + REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); } diff --git a/src/AppInstallerCLITests/PinFlow.cpp b/src/AppInstallerCLITests/PinFlow.cpp @@ -8,6 +8,7 @@ #include <Microsoft/PinningIndex.h> #include <AppInstallerRuntime.h> #include <AppInstallerVersions.h> +#include <winget/PinningData.h> using namespace TestCommon; using namespace AppInstaller::CLI; @@ -17,17 +18,6 @@ using namespace AppInstaller::Utility; using namespace AppInstaller::Pinning; using namespace AppInstaller::SQLite; -void OverrideForOpenPinningIndex(TestContext& context, const std::filesystem::path& indexPath) -{ - context.Override({ "OpenPinningIndex", [=](TestContext& context) - { - auto pinningIndex = std::filesystem::exists(indexPath) ? - PinningIndex::Open(indexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite) : - PinningIndex::CreateNew(indexPath.u8string()); - context.Add<Execution::Data::PinningIndex>(std::make_shared<PinningIndex>(std::move(pinningIndex))); - } }); -} - TEST_CASE("PinFlow_Add", "[PinFlow][workflow]") { TempFile indexFile("pinningIndex", ".db"); @@ -35,7 +25,6 @@ TEST_CASE("PinFlow_Add", "[PinFlow][workflow]") std::ostringstream pinAddOutput; TestContext addContext{ pinAddOutput, std::cin }; - OverrideForOpenPinningIndex(addContext, indexFile.GetPath()); OverrideForCompositeInstalledSource(addContext, CreateTestSource({ TSR::TestInstaller_Exe })); addContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); addContext.Args.AddArg(Execution::Args::Type::BlockingPin); @@ -56,7 +45,6 @@ TEST_CASE("PinFlow_Add", "[PinFlow][workflow]") std::ostringstream pinListOutput; TestContext listContext{ pinListOutput, std::cin }; - OverrideForOpenPinningIndex(listContext, indexFile.GetPath()); OverrideForCompositeInstalledSource(listContext, CreateTestSource({ TSR::TestInstaller_Exe })); listContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); @@ -71,7 +59,6 @@ TEST_CASE("PinFlow_Add", "[PinFlow][workflow]") { std::ostringstream pinRemoveOutput; TestContext removeContext{ pinRemoveOutput, std::cin }; - OverrideForOpenPinningIndex(removeContext, indexFile.GetPath()); OverrideForCompositeInstalledSource(removeContext, CreateTestSource({ TSR::TestInstaller_Exe })); removeContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); @@ -87,7 +74,6 @@ TEST_CASE("PinFlow_Add", "[PinFlow][workflow]") { std::ostringstream pinResetOutput; TestContext resetContext{ pinResetOutput, std::cin }; - OverrideForOpenPinningIndex(resetContext, indexFile.GetPath()); SECTION("Without --force") { @@ -117,7 +103,6 @@ TEST_CASE("PinFlow_Add", "[PinFlow][workflow]") { std::ostringstream pinUpdateOutput; TestContext updateContext{ pinUpdateOutput, std::cin }; - OverrideForOpenPinningIndex(updateContext, indexFile.GetPath()); OverrideForCompositeInstalledSource(updateContext, CreateTestSource({ TSR::TestInstaller_Exe })); updateContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); @@ -173,7 +158,6 @@ TEST_CASE("PinFlow_ListEmpty", "[PinFlow][workflow]") std::ostringstream pinListOutput; TestContext listContext{ pinListOutput, std::cin }; - OverrideForOpenPinningIndex(listContext, indexFile.GetPath()); OverrideForCompositeInstalledSource(listContext, CreateTestSource({})); PinListCommand pinList({}); @@ -190,7 +174,6 @@ TEST_CASE("PinFlow_RemoveNonExisting", "[PinFlow][workflow]") std::ostringstream pinRemoveOutput; TestContext removeContext{ pinRemoveOutput, std::cin }; - OverrideForOpenPinningIndex(removeContext, indexFile.GetPath()); OverrideForCompositeInstalledSource(removeContext, CreateTestSource({ TSR::TestInstaller_Exe })); removeContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); @@ -208,7 +191,6 @@ TEST_CASE("PinFlow_ResetEmpty", "[PinFlow][workflow]") std::ostringstream pinResetOutput; TestContext resetContext{ pinResetOutput, std::cin }; - OverrideForOpenPinningIndex(resetContext, indexFile.GetPath()); resetContext.Args.AddArg(Execution::Args::Type::Force); PinResetCommand pinReset({}); diff --git a/src/AppInstallerCLITests/PortableIndex.cpp b/src/AppInstallerCLITests/PortableIndex.cpp @@ -5,7 +5,7 @@ #include <winget/SQLiteWrapper.h> #include <winget/SQLiteStorageBase.h> #include <Microsoft/Schema/IPortableIndex.h> -#include <Microsoft/PortableIndex.h> +#include <winget/PortableIndex.h> #include <Microsoft/Schema/Portable_1_0/PortableTable.h> #include <winget/PortableFileEntry.h> diff --git a/src/AppInstallerCLITests/PortableInstaller.cpp b/src/AppInstallerCLITests/PortableInstaller.cpp @@ -10,7 +10,7 @@ #include <winget/PortableARPEntry.h> #include <winget/SQLiteStorageBase.h> #include <Microsoft/Schema/IPortableIndex.h> -#include <Microsoft/PortableIndex.h> +#include <winget/PortableIndex.h> using namespace std::string_literals; using namespace AppInstaller::CLI::Portable; diff --git a/src/AppInstallerCLITests/SQLiteIndexSource.cpp b/src/AppInstallerCLITests/SQLiteIndexSource.cpp @@ -86,7 +86,7 @@ TEST_CASE("SQLiteIndexSource_Id", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); - auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Id).get() == manifest.Id); } @@ -107,7 +107,7 @@ TEST_CASE("SQLiteIndexSource_Name", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); - auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Name).get() == manifest.DefaultLocalization.Get<Localization::PackageName>()); } diff --git a/src/AppInstallerCLITests/TestCommon.cpp b/src/AppInstallerCLITests/TestCommon.cpp @@ -50,25 +50,25 @@ namespace TestCommon } } - TempFile::TempFile(const std::string& baseName, const std::string& baseExt, bool deleteFileOnConstruction) + TempFile::TempFile(const std::string& baseName, const std::string& baseExt, std::optional<KeepTempFile> keepTempFile) { _filepath = GetTempFilePath(baseName, baseExt); - if (deleteFileOnConstruction) + if (!keepTempFile) { std::filesystem::remove(_filepath); } } - TempFile::TempFile(const std::filesystem::path& parent, const std::string& baseName, const std::string& baseExt, bool deleteFileOnConstruction) + TempFile::TempFile(const std::filesystem::path& parent, const std::string& baseName, const std::string& baseExt, std::optional<KeepTempFile> keepTempFile) { _filepath = GetFilePath(parent, baseName, baseExt); - if (deleteFileOnConstruction) + if (!keepTempFile) { std::filesystem::remove(_filepath); } } - TempFile::TempFile(const std::filesystem::path& filePath, bool deleteFileOnConstruction) + TempFile::TempFile(const std::filesystem::path& filePath, std::optional<KeepTempFile> keepTempFile) { if (filePath.is_relative()) { @@ -79,7 +79,7 @@ namespace TestCommon { _filepath = filePath; } - if (deleteFileOnConstruction) + if (!keepTempFile) { std::filesystem::remove(_filepath); } diff --git a/src/AppInstallerCLITests/TestCommon.h b/src/AppInstallerCLITests/TestCommon.h @@ -22,12 +22,14 @@ namespace TestCommon ShellExecuteOnFailure, }; + struct KeepTempFile {}; + // Use this to create a temporary file for testing. struct TempFile { - TempFile(const std::string& baseName, const std::string& baseExt, bool deleteFileOnConstruction = true); - TempFile(const std::filesystem::path& parent, const std::string& baseName, const std::string& baseExt, bool deleteFileOnConstruction = true); - TempFile(const std::filesystem::path& filePath, bool deleteFileOnConstruction = true); + TempFile(const std::string& baseName, const std::string& baseExt, std::optional<KeepTempFile> keepTempFile = {}); + TempFile(const std::filesystem::path& parent, const std::string& baseName, const std::string& baseExt, std::optional<KeepTempFile> keepTempFile = {}); + TempFile(const std::filesystem::path& filePath, std::optional<KeepTempFile> keepTempFile = {}); TempFile(const TempFile&) = delete; TempFile& operator=(const TempFile&) = delete; diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp @@ -168,7 +168,7 @@ namespace TestCommon return InstalledVersion; } - std::vector<PackageVersionKey> TestPackage::GetAvailableVersionKeys(PinBehavior) const + std::vector<PackageVersionKey> TestPackage::GetAvailableVersionKeys() const { std::vector<PackageVersionKey> result; for (const auto& version : AvailableVersions) @@ -178,7 +178,7 @@ namespace TestCommon return result; } - std::shared_ptr<IPackageVersion> TestPackage::GetLatestAvailableVersion(PinBehavior) const + std::shared_ptr<IPackageVersion> TestPackage::GetLatestAvailableVersion() const { if (AvailableVersions.empty()) { @@ -202,19 +202,6 @@ namespace TestCommon return {}; } - bool TestPackage::IsUpdateAvailable(PinBehavior) const - { - if (InstalledVersion && !AvailableVersions.empty()) - { - Utility::Version installed{ InstalledVersion->GetProperty(PackageVersionProperty::Version) }; - Utility::Version available{ AvailableVersions[0]->GetProperty(PackageVersionProperty::Version) }; - - return available > installed; - } - - return false; - } - bool TestPackage::IsSame(const IPackage* other) const { if (IsSameOverride) diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h @@ -66,10 +66,9 @@ namespace TestCommon AppInstaller::Utility::LocIndString GetProperty(AppInstaller::Repository::PackageProperty property) const override; std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetInstalledVersion() const override; - std::vector<AppInstaller::Repository::PackageVersionKey> GetAvailableVersionKeys(AppInstaller::Repository::PinBehavior) const override; - std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetLatestAvailableVersion(AppInstaller::Repository::PinBehavior) const override; + std::vector<AppInstaller::Repository::PackageVersionKey> GetAvailableVersionKeys() const override; + std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetLatestAvailableVersion() const override; std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetAvailableVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; - bool IsUpdateAvailable(AppInstaller::Repository::PinBehavior) const override; bool IsSame(const IPackage* other) const override; const void* CastTo(AppInstaller::Repository::IPackageType type) const override; diff --git a/src/AppInstallerCommonCore/Pin.cpp b/src/AppInstallerCommonCore/Pin.cpp @@ -64,6 +64,11 @@ namespace AppInstaller::Pinning return first > second; } + PinType Stricter(PinType first, PinType second) + { + return IsStricter(first, second) ? first : second; + } + std::string PinKey::ToString() const { std::stringstream ss; diff --git a/src/AppInstallerCommonCore/Public/winget/Pin.h b/src/AppInstallerCommonCore/Public/winget/Pin.h @@ -32,6 +32,9 @@ namespace AppInstaller::Pinning // Determines which of two pin types is more strict. bool IsStricter(PinType first, PinType second); + // Returns the stricter of two pin types. + PinType Stricter(PinType first, PinType second); + // The set of values needed to uniquely identify a Pin. // A Pin can apply to an installed package or to an available package. // Pins on available packages can persist when an app is updated outside of winget, @@ -71,8 +74,8 @@ namespace AppInstaller::Pinning // Used for logging std::string ToString() const; - const std::string PackageId; - const std::string SourceId; + std::string PackageId; + std::string SourceId; }; struct Pin diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -348,11 +348,9 @@ <ClInclude Include="ISource.h" /> <ClInclude Include="Microsoft\ARPHelper.h" /> <ClInclude Include="Microsoft\PinningIndex.h" /> - <ClInclude Include="Microsoft\PortableIndex.h" /> <ClInclude Include="Microsoft\PredefinedInstalledSourceFactory.h" /> <ClInclude Include="Microsoft\PredefinedWriteableSourceFactory.h" /> <ClInclude Include="Microsoft\PreIndexedPackageSourceFactory.h" /> - <ClInclude Include="Microsoft\CheckpointDatabase.h" /> <ClInclude Include="Microsoft\Schema\1_0\ChannelTable.h" /> <ClInclude Include="Microsoft\Schema\1_0\CommandsTable.h" /> <ClInclude Include="Microsoft\Schema\1_0\IdTable.h" /> @@ -405,12 +403,15 @@ <ClInclude Include="pch.h" /> <ClInclude Include="Public\winget\ARPCorrelation.h" /> <ClInclude Include="Public\winget\Checkpoint.h" /> + <ClInclude Include="Public\winget\CheckpointDatabase.h" /> <ClInclude Include="Public\winget\IconExtraction.h" /> <ClInclude Include="Public\winget\InstalledFilesCorrelation.h" /> <ClInclude Include="Public\winget\InstallerMetadataCollectionContext.h" /> <ClInclude Include="Public\winget\ManifestJSONParser.h" /> <ClInclude Include="Public\winget\ARPCorrelationAlgorithms.h" /> <ClInclude Include="Public\winget\PackageTrackingCatalog.h" /> + <ClInclude Include="Public\winget\PinningData.h" /> + <ClInclude Include="Public\winget\PortableIndex.h" /> <ClInclude Include="Public\winget\RepositorySearch.h" /> <ClInclude Include="Public\winget\RepositorySource.h" /> <ClInclude Include="Rest\RestClient.h" /> @@ -494,6 +495,7 @@ <ClCompile Include="pch.cpp"> <PrecompiledHeader>Create</PrecompiledHeader> </ClCompile> + <ClCompile Include="PinningData.cpp" /> <ClCompile Include="RepositorySearch.cpp" /> <ClCompile Include="RepositorySource.cpp" /> <ClCompile Include="Rest\RestClient.cpp" /> diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -2,10 +2,8 @@ // Licensed under the MIT License. #include "pch.h" #include "CompositeSource.h" -#include "Microsoft/PinningIndex.h" #include <winget/ExperimentalFeature.h> -using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Settings; namespace AppInstaller::Repository @@ -22,92 +20,6 @@ namespace AppInstaller::Repository }; } - Pinning::PinKey GetPinKeyForAvailable(IPackage* availablePackage) - { - return { - availablePackage->GetProperty(PackageProperty::Id).get(), - availablePackage->GetLatestAvailableVersion(PinBehavior::IgnorePins)->GetSource().GetIdentifier() - }; - } - - // Gets the pinned state for an available PackageVersionKey that may have a pin, - // and optionally an additional pin that could come from the installed version or - // be a pin we have not considered yet for the version key. - // If there are a pin both in the version key and passed as an argument, - // we return the one that is the most strict. - // Note that for a package with both available and installed pins, we will call this - // twice: once with the available pin to set the pinned state in the version key, - // and once with the installed pin to set the final pinned state. - Pinning::PinType GetPinnedStateForVersion( - const PackageVersionKey& availableVersionKey, - const std::optional<Pinning::Pin>& pin, - PinBehavior pinBehavior) - { - if (pinBehavior == PinBehavior::IgnorePins) - { - // No need to check anything - return Pinning::PinType::Unknown; - } - - // For the pin in the version version, we can ignore it depending on the behavior and type. - // If it is gating, we don't need to check the version as that was already done when the - // PinnedState info was added (and we don't have the gated version here). - Pinning::PinType pinnedStateFromVersionKey = Pinning::PinType::Unknown; - if (availableVersionKey.PinnedState == Pinning::PinType::Blocking - || (availableVersionKey.PinnedState == Pinning::PinType::Pinning && pinBehavior != PinBehavior::IncludePinned) - || availableVersionKey.PinnedState == Pinning::PinType::Gating) - { - pinnedStateFromVersionKey = availableVersionKey.PinnedState; - } - - // For the additional pin, we can ignore it depending on the behavior and type. - // If it is gating, we need to check the version. - Pinning::PinType pinnedStateFromAdditionalPin = Pinning::PinType::Unknown; - if (pin) - { - if (pin->GetType() == Pinning::PinType::Blocking - || (pin->GetType() == Pinning::PinType::Pinning && pinBehavior != PinBehavior::IncludePinned) - || (pin->GetType() == Pinning::PinType::Gating && !pin->GetGatedVersion().IsValidVersion(availableVersionKey.Version))) - { - pinnedStateFromAdditionalPin = pin->GetType(); - } - } - - return Pinning::IsStricter(pinnedStateFromVersionKey, pinnedStateFromAdditionalPin) ? pinnedStateFromVersionKey : pinnedStateFromAdditionalPin; - } - - // Gets the latest available version that satisfies both the available pin (already tagged on the keys) - // and the installed pin (if any). - // Version keys must be sorted with the latest first. - std::optional<PackageVersionKey> GetLatestAvailableVersionKeySatisfyingPin( - const std::vector<PackageVersionKey>& availableVersionKeys, - const std::optional<Pinning::Pin>& installedPin, - PinBehavior pinBehavior) - { - if (availableVersionKeys.empty()) - { - return {}; - } - - if (pinBehavior == PinBehavior::IgnorePins) - { - return availableVersionKeys.front(); - } - else - { - // Skip until we find a version that isn't pinned - for (const auto& availableVersion : availableVersionKeys) - { - if (GetPinnedStateForVersion(availableVersion, installedPin, pinBehavior) == Pinning::PinType::Unknown) - { - return availableVersion; - } - } - } - - return {}; - } - // Returns true for fields that provide a strong match; one that is not based on a heuristic. bool IsStrongMatchField(PackageMatchField field) { @@ -415,136 +327,6 @@ namespace AppInstaller::Repository std::shared_ptr<IPackageVersion> m_trackingPackageVersion; }; - // Wrapper around a package to add pinning functionality for composite packages. - // Most of the methods are only here for completeness of the interface and are not actually used. - // A pinnable package can either be an installed package or a single available package; - // we deal with composite packages on CompositePackage. - struct PinnablePackage : public IPackage - { - static constexpr IPackageType PackageType = IPackageType::PinnablePackage; - - PinnablePackage() {} - PinnablePackage(std::shared_ptr<IPackage> package, std::optional<Pinning::Pin> pin = {}) - : m_package(package), m_pin(pin) - { - // Get the source ID for available packages - auto availableVersion = m_package->GetLatestAvailableVersion(PinBehavior::IgnorePins); - if (availableVersion) - { - m_sourceId = availableVersion->GetSource().GetIdentifier(); - } - } - - const std::string& GetSourceId() const - { - return m_sourceId; - } - - const std::shared_ptr<IPackage>& GetPackage() const - { - return m_package; - } - - const std::optional<Pinning::Pin>& GetPin() const - { - return m_pin; - } - - void SetPin(Pinning::Pin&& pin) - { - m_pin.emplace(std::move(pin)); - } - - Utility::LocIndString GetProperty(PackageProperty property) const override - { - return m_package->GetProperty(property); - } - - std::shared_ptr<IPackageVersion> GetInstalledVersion() const override - { - return m_package->GetInstalledVersion(); - } - - std::vector<PackageVersionKey> GetAvailableVersionKeys(PinBehavior pinBehavior) const override - { - auto result = m_package->GetAvailableVersionKeys(); - if (m_pin.has_value()) - { - // Add pin information to all version keys - for (auto& pvk : result) - { - pvk.PinnedState = GetPinnedStateForVersion(pvk, m_pin, pinBehavior); - } - } - - return result; - } - - std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const override - { - return GetAvailableVersionAndPin(versionKey).first; - } - - std::shared_ptr<IPackageVersion> GetLatestAvailableVersion(PinBehavior pinBehavior) const override - { - auto availableVersionKeys = GetAvailableVersionKeys(pinBehavior); - auto latestVersionKey = GetLatestAvailableVersionKeySatisfyingPin(availableVersionKeys, /* installedPin */ {}, pinBehavior); - if (!latestVersionKey) - { - return {}; - } - - return GetAvailableVersion(latestVersionKey.value()); - } - - virtual std::pair<std::shared_ptr<IPackageVersion>, Pinning::PinType> GetAvailableVersionAndPin(const PackageVersionKey& versionKey) const override - { - Pinning::PinType pinType = Pinning::PinType::Unknown; - - if (m_pin.has_value()) - { - pinType = GetPinnedStateForVersion(versionKey, m_pin.value(), PinBehavior::ConsiderPins); - } - - return { m_package->GetAvailableVersion(versionKey), pinType }; - } - - bool IsUpdateAvailable(PinBehavior) const override - { - return false; - } - - bool IsSame(const IPackage* other) const override - { - const PinnablePackage* otherAvailable = PackageCast<const PinnablePackage*>(other); - - if (otherAvailable) - { - return - m_sourceId == otherAvailable->m_sourceId && - m_pin == otherAvailable->m_pin && - m_package->IsSame(otherAvailable->m_package.get()); - } - - return false; - } - - const void* CastTo(IPackageType type) const override - { - if (type == PackageType) - { - return this; - } - - return nullptr; - } - - private: - std::string m_sourceId; - std::shared_ptr<IPackage> m_package; - std::optional<Pinning::Pin> m_pin; - }; - // A composite package for the CompositeSource. struct CompositePackage : public IPackage { @@ -555,7 +337,7 @@ namespace AppInstaller::Repository // Grab the installed version's channel to allow for filtering in calls to get available info. if (installedPackage) { - m_installedPackage.emplace(installedPackage); + m_installedPackage = std::move(installedPackage); auto installedVersion = m_installedPackage->GetInstalledVersion(); if (installedVersion) { @@ -571,7 +353,7 @@ namespace AppInstaller::Repository std::shared_ptr<IPackageVersion> truth; if (m_defaultAvailablePackage) { - truth = m_defaultAvailablePackage->GetLatestAvailableVersion(PinBehavior::IgnorePins); + truth = m_defaultAvailablePackage->GetLatestAvailableVersion(); } if (!truth) { @@ -583,7 +365,7 @@ namespace AppInstaller::Repository } if (!truth) { - truth = GetLatestAvailableVersion(PinBehavior::IgnorePins); + truth = GetLatestAvailableVersion(); } switch (property) @@ -611,25 +393,13 @@ namespace AppInstaller::Repository return {}; } - std::vector<PackageVersionKey> GetAvailableVersionKeys(PinBehavior pinBehavior) const override + std::vector<PackageVersionKey> GetAvailableVersionKeys() const override { std::vector<PackageVersionKey> result; - auto installedPin = GetInstalledPin(); for (const auto& availablePackage : m_availablePackages) { - auto versionKeys = availablePackage.GetAvailableVersionKeys(pinBehavior); - - // The version keys we have already have pin information from the available package. - // Here we also add information from the installed package. - if (installedPin) - { - for (auto& versionKey : versionKeys) - { - versionKey.PinnedState = GetPinnedStateForVersion(versionKey, installedPin, pinBehavior); - } - } - + auto versionKeys = availablePackage->GetAvailableVersionKeys(); std::copy(versionKeys.begin(), versionKeys.end(), std::back_inserter(result)); } @@ -645,57 +415,30 @@ namespace AppInstaller::Repository return result; } - std::shared_ptr<IPackageVersion> GetLatestAvailableVersion(PinBehavior pinBehavior) const override + std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const override { - auto availableVersionKeys = GetAvailableVersionKeys(pinBehavior); - auto latestVersionKey = GetLatestAvailableVersionKeySatisfyingPin(availableVersionKeys, GetInstalledPin(), pinBehavior); - if (!latestVersionKey) - { - return {}; - } - - return GetAvailableVersion(latestVersionKey.value()); + return GetAvailableVersion({ "", "", m_installedChannel.get() }); } std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const override { - return GetAvailableVersionAndPin(versionKey).first; - } - - std::pair<std::shared_ptr<IPackageVersion>, Pinning::PinType> GetAvailableVersionAndPin(const PackageVersionKey& versionKey) const override - { for (const auto& availablePackage : m_availablePackages) { - if (!Utility::IsEmptyOrWhitespace(versionKey.SourceId) && versionKey.SourceId != availablePackage.GetSourceId()) + if (!Utility::IsEmptyOrWhitespace(versionKey.SourceId)) { - continue; + auto latestAvailable = availablePackage->GetLatestAvailableVersion(); + if (latestAvailable && versionKey.SourceId != latestAvailable->GetSource().GetIdentifier()) + { + continue; + } } - auto result = availablePackage.GetAvailableVersionAndPin(versionKey); - if (result.first) - { - result.second = GetPinnedStateForVersion(versionKey, GetInstalledPin(), PinBehavior::ConsiderPins); - return result; - } + return availablePackage->GetAvailableVersion(versionKey); } return {}; } - bool IsUpdateAvailable(PinBehavior pinBehavior) const override - { - auto installed = GetInstalledVersion(); - - if (!installed) - { - return false; - } - - auto latest = GetLatestAvailableVersion(pinBehavior); - - return (latest && (GetVACFromVersion(installed.get()).IsUpdatedBy(GetVACFromVersion(latest.get())))); - } - bool IsSame(const IPackage* other) const override { const CompositePackage* otherComposite = PackageCast<const CompositePackage*>(other); @@ -707,15 +450,24 @@ namespace AppInstaller::Repository return false; } - if (m_installedPackage && !m_installedPackage->GetPackage()->IsSame(otherComposite->m_installedPackage->GetPackage().get())) + if (m_installedPackage && !m_installedPackage->IsSame(otherComposite->m_installedPackage.get())) { return false; } - for (size_t i = 0; i < m_availablePackages.size(); ++i) + for (const auto& availablePackage : m_availablePackages) { - if (m_availablePackages[i].GetSourceId() != otherComposite->m_availablePackages[i].GetSourceId() || - !m_availablePackages[i].GetPackage()->IsSame(otherComposite->m_availablePackages[i].GetPackage().get())) + bool foundMatch = false; + for (const auto& otherAvailablePackage : otherComposite->m_availablePackages) + { + if (availablePackage->IsSame(otherAvailablePackage.get())) + { + foundMatch = true; + break; + } + } + + if (!foundMatch) { return false; } @@ -740,7 +492,7 @@ namespace AppInstaller::Repository { for (const auto& availablePackage : m_availablePackages) { - if (other->IsSame(availablePackage.GetPackage().get())) + if (other->IsSame(availablePackage.get())) { return true; } @@ -754,7 +506,7 @@ namespace AppInstaller::Repository { if (m_installedPackage) { - return m_installedPackage->GetPackage(); + return m_installedPackage; } else { @@ -789,40 +541,6 @@ namespace AppInstaller::Repository m_trackingPackageVersion = std::move(trackingPackageVersion); } - // Gets the information about the pins that exist for this package - void GetExistingPins(PinningIndex& pinningIndex) - { - for (auto& availablePackage : m_availablePackages) - { - // Safeguard in case a package with no available sneaks in as we intentionally do in tests - if (availablePackage.GetPackage()->GetAvailableVersionKeys().empty()) - { - continue; - } - - Pinning::PinKey pinKey = GetPinKeyForAvailable(availablePackage.GetPackage().get()); - - auto pin = pinningIndex.GetPin(pinKey); - if (pin.has_value()) - { - availablePackage.SetPin(std::move(pin.value())); - } - } - - if (m_installedPackage) - { - Pinning::PinKey pinKey = Pinning::PinKey::GetPinKeyForInstalled( - m_installedPackage->GetProperty(PackageProperty::Id).get() - ); - - auto pin = pinningIndex.GetPin(pinKey); - if (pin.has_value()) - { - m_installedPackage->SetPin(std::move(pin.value())); - } - } - } - private: // Try to set a version that will override the version string from the installed package void TrySetOverrideInstalledVersion(std::shared_ptr<IPackage> availablePackage) @@ -841,19 +559,14 @@ namespace AppInstaller::Repository } } - std::optional<Pinning::Pin> GetInstalledPin() const - { - return m_installedPackage ? m_installedPackage->GetPin() : std::nullopt; - } - - std::optional<PinnablePackage> m_installedPackage; + std::shared_ptr<IPackage> m_installedPackage; Utility::LocIndString m_installedChannel; Source m_trackingSource; std::shared_ptr<IPackage> m_trackingPackage; std::shared_ptr<IPackageVersion> m_trackingPackageVersion; std::string m_overrideInstalledVersion; std::shared_ptr<IPackage> m_defaultAvailablePackage; - std::vector<PinnablePackage> m_availablePackages; + std::vector<std::shared_ptr<IPackage>> m_availablePackages; }; // The comparator compares the ResultMatch by MatchType first, then Field in a predefined order. @@ -1232,23 +945,6 @@ namespace AppInstaller::Repository return {}; } - - // Adds all the pin information to the results from a search to a CompositeSource. - void AddPinInfoToCompositeSearchResult(CompositeResult& result) - { - if (!result.Matches.empty()) - { - // Look up any pins for the packages found - auto pinningIndex = PinningIndex::OpenOrCreateDefault(); - if (pinningIndex) - { - for (auto& match : result.Matches) - { - match.Package->GetExistingPins(*pinningIndex); - } - } - } - } } CompositeSource::CompositeSource(std::string identifier) @@ -1461,7 +1157,6 @@ namespace AppInstaller::Repository // Optimization for the "everything installed" case, no need to allow for reverse correlations if (request.IsForEverything() && m_searchBehavior == CompositeSearchBehavior::Installed) { - AddPinInfoToCompositeSearchResult(result); return std::move(result); } } @@ -1579,7 +1274,6 @@ namespace AppInstaller::Repository result.Matches.erase(result.Matches.begin() + request.MaximumResults, result.Matches.end()); } - AddPinInfoToCompositeSearchResult(result); return std::move(result); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp @@ -1,11 +1,31 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "CheckpointDatabase.h" -#include "Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h" +#include "Public/winget/CheckpointDatabase.h" +#include "Microsoft/Schema/ICheckpointDatabase.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h" namespace AppInstaller::Repository::Microsoft { + namespace + { + // Creates the ICheckpointDatabase interface object for the given version. + std::unique_ptr<Schema::ICheckpointDatabase> CreateICheckpointDatabase(const SQLite::Version& version) + { + if (version == SQLite::Version{ 1, 0 } || + version.MajorVersion == 1 || + version.IsLatest()) + { + return std::make_unique<Schema::Checkpoint_V1_0::CheckpointDatabaseInterface>(); + } + + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + } + + CheckpointDatabase::CheckpointDatabase(CheckpointDatabase&&) = default; + CheckpointDatabase& CheckpointDatabase::operator=(CheckpointDatabase&&) = default; + std::shared_ptr<CheckpointDatabase> CheckpointDatabase::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Checkpoint database with version [" << version << "] at '" << filePath << "'"); @@ -25,6 +45,11 @@ namespace AppInstaller::Repository::Microsoft return std::make_shared<CheckpointDatabase>(std::move(result)); } + std::shared_ptr<CheckpointDatabase> CheckpointDatabase::Open(const std::string& filePath, SQLite::SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) + { + return std::make_shared<CheckpointDatabase>(CheckpointDatabase{ filePath, disposition, std::move(indexFile) }); + } + bool CheckpointDatabase::IsEmpty() { return m_interface->IsEmpty(m_dbconn); @@ -128,29 +153,18 @@ namespace AppInstaller::Repository::Microsoft return values.value(); } - std::unique_ptr<Schema::ICheckpointDatabase> CheckpointDatabase::CreateICheckpointDatabase() const - { - if (m_version == SQLite::Version{ 1, 0 } || - m_version.MajorVersion == 1 || - m_version.IsLatest()) - { - return std::make_unique<Schema::Checkpoint_V1_0::CheckpointDatabaseInterface>(); - } - - THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); - } CheckpointDatabase::CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : SQLiteStorageBase(target, disposition, std::move(indexFile)) { AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); - m_interface = CreateICheckpointDatabase(); + m_interface = CreateICheckpointDatabase(m_version); THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } CheckpointDatabase::CheckpointDatabase(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version) { - m_interface = CreateICheckpointDatabase(); + m_interface = CreateICheckpointDatabase(m_version); m_version = m_interface->GetVersion(); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h @@ -1,77 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include <winget/SQLiteWrapper.h> -#include "Microsoft/Schema/ICheckpointDatabase.h" -#include <winget/SQLiteStorageBase.h> -#include <winget/ManagedFile.h> - -namespace AppInstaller::Repository::Microsoft -{ - struct CheckpointDatabase : SQLite::SQLiteStorageBase - { - // An id that refers to a specific Checkpoint. - using IdType = SQLite::rowid_t; - - CheckpointDatabase(const CheckpointDatabase&) = delete; - CheckpointDatabase& operator=(const CheckpointDatabase&) = delete; - - CheckpointDatabase(CheckpointDatabase&&) = default; - CheckpointDatabase& operator=(CheckpointDatabase&&) = default; - - // Create a new checkpoint database. - static std::shared_ptr<CheckpointDatabase> CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); - - // Opens an existing checkpoint database. - static std::shared_ptr<CheckpointDatabase> Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) - { - CheckpointDatabase result{ filePath, disposition, std::move(indexFile) }; - return std::make_shared<CheckpointDatabase>(std::move(result)); - } - - // Returns a value indicating whether the database is empty. - bool IsEmpty(); - - // Adds a new checkpoint name to the checkpoint table. - IdType AddCheckpoint(std::string_view checkpointName); - - // Returns all checkpoint ids in descending (newest at the front) order. - std::vector<IdType> GetCheckpointIds(); - - // Returns a boolean value indicating a field exists for a checkpoint data type. - bool HasDataField(IdType checkpointId, int type, const std::string& name); - - // Returns the available data types for a checkpoint id. - std::vector<int> GetDataTypes(IdType checkpointId); - - // Returns the available field names for a checkpoint data. - std::vector<std::string> GetDataFieldNames(IdType checkpointId, int dataType); - - // Sets the value(s) for a data type and field. - void SetDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector<std::string>& values); - - // Updates the value(s) for a data type and field. - void UpdateDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector<std::string>& values); - - // Gets a single value for a data type field. - std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, const std::string& field); - - // Gets multiple values for a data type field. - std::vector<std::string> GetDataFieldMultiValue(IdType checkpointId, int dataType, const std::string& field); - - // Removes the value(s) for a data type. - void RemoveDataType(IdType checkpointId, int dataType); - - private: - // Constructor used to open an existing index. - CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); - - // Constructor used to create a new index. - CheckpointDatabase(const std::string& target, SQLite::Version version); - - // Creates the ICheckpointDatabase interface object for this version. - std::unique_ptr<Schema::ICheckpointDatabase> CreateICheckpointDatabase() const; - - std::unique_ptr<Schema::ICheckpointDatabase> m_interface; - }; -}- \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp @@ -7,6 +7,60 @@ namespace AppInstaller::Repository::Microsoft { +#ifndef AICLI_DISABLE_TEST_HOOKS + std::optional<std::filesystem::path> s_PinningIndexOverride{}; + void TestHook_SetPinningIndex_Override(std::optional<std::filesystem::path>&& indexPath) + { + s_PinningIndexOverride = std::move(indexPath); + } +#endif + + namespace + { + std::filesystem::path GetPinningDatabasePath() + { + const auto DefaultPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / "pinning.db"; + + return +#ifndef AICLI_DISABLE_TEST_HOOKS + s_PinningIndexOverride.has_value() ? s_PinningIndexOverride.value() : +#endif + DefaultPath; + } + + std::shared_ptr<PinningIndex> OpenDatabaseIfExists(const std::filesystem::path& path, SQLite::SQLiteStorageBase::OpenDisposition openDisposition) + { + AICLI_LOG(Repo, Info, << "Attempting to open pinning database: " << path); + + try + { + if (std::filesystem::exists(path)) + { + if (std::filesystem::is_regular_file(path)) + { + try + { + AICLI_LOG(Repo, Info, << "... opening existing pinning database"); + return std::make_shared<PinningIndex>(PinningIndex::Open(path.u8string(), openDisposition)); + } + CATCH_LOG(); + + AICLI_LOG(Repo, Info, << "... deleting bad pinning database file"); + std::filesystem::remove_all(path); + } + else + { + AICLI_LOG(Repo, Info, << "... deleting pinning database path that is a directory"); + std::filesystem::remove_all(path); + } + } + } + CATCH_LOG(); + + return {}; + } + } + PinningIndex PinningIndex::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Pinning Index with version [" << version << "] at '" << filePath << "'"); @@ -26,49 +80,29 @@ namespace AppInstaller::Repository::Microsoft return result; } -#ifndef AICLI_DISABLE_TEST_HOOKS - std::optional<std::filesystem::path> s_PinningIndexOverride{}; - void TestHook_SetPinningIndex_Override(std::optional<std::filesystem::path>&& indexPath) + std::shared_ptr<PinningIndex> PinningIndex::OpenIfExists(OpenDisposition openDisposition) { - s_PinningIndexOverride = std::move(indexPath); + return OpenDatabaseIfExists(GetPinningDatabasePath(), openDisposition); } -#endif std::shared_ptr<PinningIndex> PinningIndex::OpenOrCreateDefault(OpenDisposition openDisposition) { - const auto DefaultIndexPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / "pinning.db"; -#ifndef AICLI_DISABLE_TEST_HOOKS - const auto indexPath = s_PinningIndexOverride.has_value() ? s_PinningIndexOverride.value() : DefaultIndexPath; -#else - const auto indexPath = DefaultIndexPath; -#endif - - AICLI_LOG(Repo, Info, << "Opening pinning index"); + const auto databasePath = GetPinningDatabasePath(); + std::shared_ptr<PinningIndex> result = OpenDatabaseIfExists(databasePath, openDisposition); - try + if (!result) { - if (std::filesystem::exists(indexPath)) - { - if (std::filesystem::is_regular_file(indexPath)) - { - try - { - AICLI_LOG(Repo, Info, << "Opening existing pinning index"); - return std::make_shared<PinningIndex>(PinningIndex::Open(indexPath.u8string(), openDisposition)); - } - CATCH_LOG(); - } + AICLI_LOG(Repo, Info, << "... creating pinning database"); - AICLI_LOG(Repo, Info, << "Attempting to delete bad index file"); - std::filesystem::remove_all(indexPath); + try + { + result = std::make_shared<PinningIndex>(PinningIndex::CreateNew(databasePath.u8string())); } - - return std::make_shared<PinningIndex>(PinningIndex::CreateNew(indexPath.u8string())); + CATCH_LOG(); } - CATCH_LOG(); - return {}; + return result; } PinningIndex::IdType PinningIndex::AddPin(const Pinning::Pin& pin) diff --git a/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.h b/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.h @@ -4,7 +4,7 @@ #include <winget/SQLiteWrapper.h> #include "Microsoft/Schema/IPinningIndex.h" #include <winget/SQLiteStorageBase.h> -#include "winget/Pin.h" +#include <winget/Pin.h> #include <winget/ManagedFile.h> namespace AppInstaller::Repository::Microsoft @@ -32,6 +32,11 @@ namespace AppInstaller::Repository::Microsoft // Opens or creates a PinningIndex database on the default path. // openDisposition is only used when opening an existing database. // Returns nullptr in case of error. + static std::shared_ptr<PinningIndex> OpenIfExists(OpenDisposition openDisposition = OpenDisposition::Read); + + // Opens or creates a PinningIndex database on the default path. + // openDisposition is only used when opening an existing database. + // Returns nullptr in case of error. static std::shared_ptr<PinningIndex> OpenOrCreateDefault(OpenDisposition openDisposition = OpenDisposition::ReadWrite); // Adds a pin to the index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.cpp @@ -1,13 +1,20 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "PortableIndex.h" +#include "Public/winget/PortableIndex.h" +#include "Microsoft/Schema/IPortableIndex.h" +#include "Microsoft/Schema/Portable_1_0/PortableTable.h" #include <winget/SQLiteStorageBase.h> #include "Schema/Portable_1_0/PortableIndexInterface.h" -#include "winget/Filesystem.h" +#include <winget/Filesystem.h> namespace AppInstaller::Repository::Microsoft { + PortableIndex::PortableIndex(PortableIndex&&) = default; + PortableIndex& PortableIndex::operator=(PortableIndex&&) = default; + + PortableIndex::~PortableIndex() = default; + PortableIndex PortableIndex::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Portable Index with version [" << version << "] at '" << filePath << "'"); @@ -30,6 +37,11 @@ namespace AppInstaller::Repository::Microsoft return result; } + PortableIndex PortableIndex::Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile) + { + return { filePath, disposition, std::move(indexFile) }; + } + PortableIndex::IdType PortableIndex::AddPortableFile(const Portable::PortableFileEntry& file) { std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.h b/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.h @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include <winget/SQLiteWrapper.h> -#include "Microsoft/Schema/IPortableIndex.h" -#include "Microsoft/Schema/Portable_1_0/PortableTable.h" -#include <winget/SQLiteStorageBase.h> -#include "winget/PortableFileEntry.h" -#include <winget/ManagedFile.h> - -using namespace AppInstaller::Portable; - -namespace AppInstaller::Repository::Microsoft -{ - struct PortableIndex : SQLite::SQLiteStorageBase - { - // An id that refers to a specific portable file. - using IdType = SQLite::rowid_t; - - PortableIndex(const PortableIndex&) = delete; - PortableIndex& operator=(const PortableIndex&) = delete; - - PortableIndex(PortableIndex&&) = default; - PortableIndex& operator=(PortableIndex&&) = default; - - // Creates a new PortableIndex database of the given version. - static PortableIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); - - // Opens an existing PortableIndex database. - static PortableIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) - { - return { filePath, disposition, std::move(indexFile) }; - } - - IdType AddPortableFile(const Portable::PortableFileEntry& file); - - void RemovePortableFile(const Portable::PortableFileEntry& file); - - bool UpdatePortableFile(const Portable::PortableFileEntry& file); - - void AddOrUpdatePortableFile(const Portable::PortableFileEntry& file); - - std::vector<Portable::PortableFileEntry> GetAllPortableFiles(); - - bool Exists(const Portable::PortableFileEntry& file); - - bool IsEmpty(); - - private: - // Constructor used to open an existing index. - PortableIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); - - // Constructor used to create a new index. - PortableIndex(const std::string& target, SQLite::Version version); - - // Creates the IPortableIndex interface object for this version. - std::unique_ptr<Schema::IPortableIndex> CreateIPortableIndex() const; - - std::unique_ptr<Schema::IPortableIndex> m_interface; - }; -}- \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -285,7 +285,7 @@ namespace AppInstaller::Repository::Microsoft return {}; } - std::vector<PackageVersionKey> GetAvailableVersionKeys(PinBehavior) const override + std::vector<PackageVersionKey> GetAvailableVersionKeys() const override { std::shared_ptr<SQLiteIndexSource> source = GetReferenceSource(); @@ -318,7 +318,7 @@ namespace AppInstaller::Repository::Microsoft return m_availableVersionKeys; } - std::shared_ptr<IPackageVersion> GetLatestAvailableVersion(PinBehavior) const override + std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const override { return GetLatestVersionInternal(); } @@ -359,11 +359,6 @@ namespace AppInstaller::Repository::Microsoft return {}; } - bool IsUpdateAvailable(PinBehavior) const override - { - return false; - } - bool IsSame(const IPackage* other) const override { const AvailablePackage* otherAvailable = PackageCast<const AvailablePackage*>(other); @@ -434,12 +429,12 @@ namespace AppInstaller::Repository::Microsoft return GetLatestVersionInternal(); } - std::vector<PackageVersionKey> GetAvailableVersionKeys(PinBehavior) const override + std::vector<PackageVersionKey> GetAvailableVersionKeys() const override { return {}; } - std::shared_ptr<IPackageVersion> GetLatestAvailableVersion(PinBehavior) const override + std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const override { return {}; } @@ -449,11 +444,6 @@ namespace AppInstaller::Repository::Microsoft return {}; } - bool IsUpdateAvailable(PinBehavior) const override - { - return false; - } - bool IsSame(const IPackage* other) const override { const InstalledPackage* otherInstalled = PackageCast<const InstalledPackage*>(other); diff --git a/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp b/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp @@ -139,7 +139,7 @@ namespace AppInstaller::Repository { // No installed version, or installed version not found in available versions, // then attempt to check installed status using latest version. - availableVersion = package->GetLatestAvailableVersion(PinBehavior::IgnorePins); + availableVersion = package->GetLatestAvailableVersion(); THROW_HR_IF(E_UNEXPECTED, !availableVersion); } diff --git a/src/AppInstallerRepositoryCore/PinningData.cpp b/src/AppInstallerRepositoryCore/PinningData.cpp @@ -0,0 +1,216 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/PinningData.h" +#include "Microsoft/PinningIndex.h" +#include "Public/winget/RepositorySource.h" + +using namespace AppInstaller::SQLite; +using namespace AppInstaller::Repository; +using namespace AppInstaller::Repository::Microsoft; + +namespace AppInstaller::Pinning +{ + namespace + { + // Evaluates the pinning state of a version for a single pin. + PinType EvaluatePinnedStateForVersion( + const Utility::Version& version, + const std::optional<Pin>& pin, + PinBehavior behavior) + { + if (pin) + { + if (pin->GetType() == PinType::Blocking + || (pin->GetType() == PinType::Pinning && behavior != PinBehavior::IncludePinned) + || (pin->GetType() == PinType::Gating && !pin->GetGatedVersion().IsValidVersion(version))) + { + return pin->GetType(); + } + } + + return PinType::Unknown; + } + + // Gets the pinned state for an available version that may have a pin, + // and optionally an additional pin that could come from the installed version. + // If both pins are present, we return the one that is the most strict. + Pinning::PinType GetPinnedStateForVersion( + const Utility::Version& version, + const std::optional<Pinning::Pin>& availablePin, + const std::optional<Pinning::Pin>& installedPin, + PinBehavior behavior) + { + if (behavior == PinBehavior::IgnorePins) + { + return Pinning::PinType::Unknown; + } + + return Stricter( + EvaluatePinnedStateForVersion(version, availablePin, behavior), + EvaluatePinnedStateForVersion(version, installedPin, behavior)); + } + } + + PinningData::PinningData() = default; + PinningData::PinningData(const PinningData&) = default; + PinningData& PinningData::operator=(const PinningData&) = default; + PinningData::PinningData(PinningData&&) noexcept = default; + PinningData& PinningData::operator=(PinningData&&) noexcept = default; + PinningData::~PinningData() = default; + + PinningData::PinningData(Disposition disposition) + { + if (disposition == Disposition::ReadOnly) + { + m_database = PinningIndex::OpenIfExists(SQLiteStorageBase::OpenDisposition::Read); + } + else + { + m_database = PinningIndex::OpenOrCreateDefault(SQLiteStorageBase::OpenDisposition::ReadWrite); + } + } + + PinningData::operator bool() const + { + return IsDatabaseConnected(); + } + + bool PinningData::IsDatabaseConnected() const + { + return static_cast<bool>(m_database); + } + + void PinningData::AddOrUpdatePin(const Pin& pin) + { + THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected()); + m_database->AddOrUpdatePin(pin); + } + + void PinningData::RemovePin(const PinKey& pinKey) + { + THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected()); + m_database->RemovePin(pinKey); + } + + std::optional<Pin> PinningData::GetPin(const PinKey& pinKey) + { + return IsDatabaseConnected() ? m_database->GetPin(pinKey) : std::nullopt; + } + + std::vector<Pin> PinningData::GetAllPins() + { + return IsDatabaseConnected() ? m_database->GetAllPins() : std::vector<Pin>{}; + } + + bool PinningData::ResetAllPins(std::string_view sourceId) + { + THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected()); + return m_database->ResetAllPins(sourceId); + } + + PinningData::PinStateEvaluator::PinStateEvaluator( + PinBehavior behavior, + std::shared_ptr<PinningIndex> database, + const std::shared_ptr<IPackageVersion>& installedVersion) : + m_behavior(behavior), m_database(std::move(database)) + { + if (m_behavior == PinBehavior::IgnorePins || !installedVersion) + { + // Because the database isn't guaranteed to be present, align ignoring pins with there being no pins to ignore. + // Also do not consider pins when there is no installed version. This is to remain consistent with the previous + // implementation. If this is to be changed, more install paths will need to be do pinning checks to ensure + // that one could, for instance, block the install of a package. + m_database.reset(); + } + else if (m_database) + { + PinKey key = PinKey::GetPinKeyForInstalled(installedVersion->GetProperty(PackageVersionProperty::Id)); + m_installedPin = m_database->GetPin(key); + } + + if (installedVersion) + { + m_installedVersion = Utility::VersionAndChannel{ + Utility::Version{ installedVersion->GetProperty(PackageVersionProperty::Version) }, + Utility::Channel{ installedVersion->GetProperty(PackageVersionProperty::Channel) } + }; + } + } + + PinningData::PinStateEvaluator::PinStateEvaluator(const PinStateEvaluator&) = default; + PinningData::PinStateEvaluator& PinningData::PinStateEvaluator::operator=(const PinStateEvaluator&) = default; + PinningData::PinStateEvaluator::PinStateEvaluator(PinStateEvaluator&&) noexcept = default; + PinningData::PinStateEvaluator& PinningData::PinStateEvaluator::operator=(PinStateEvaluator&&) noexcept = default; + + PinningData::PinStateEvaluator::~PinStateEvaluator() = default; + + std::shared_ptr<IPackageVersion> PinningData::PinStateEvaluator::GetLatestAvailableVersionForPins(const std::shared_ptr<IPackage>& package) + { + if (!m_database) + { + return package->GetLatestAvailableVersion(); + } + + auto availableVersionKeys = package->GetAvailableVersionKeys(); + + // Skip until we find a version that isn't pinned + for (const auto& availableVersion : availableVersionKeys) + { + std::shared_ptr<IPackageVersion> packageVersion = package->GetAvailableVersion(availableVersion); + if (EvaluatePinType(packageVersion) == Pinning::PinType::Unknown) + { + return packageVersion; + } + } + + return {}; + } + + bool PinningData::PinStateEvaluator::IsUpdate(const std::shared_ptr<IPackageVersion>& availableVersion) + { + if (m_installedVersion && availableVersion) + { + Utility::VersionAndChannel availableVersionAndChannel{ + Utility::Version{ availableVersion->GetProperty(PackageVersionProperty::Version) }, + Utility::Channel{ availableVersion->GetProperty(PackageVersionProperty::Channel) } + }; + + return m_installedVersion->IsUpdatedBy(availableVersionAndChannel); + } + + return false; + } + + PinType PinningData::PinStateEvaluator::EvaluatePinType(const std::shared_ptr<AppInstaller::Repository::IPackageVersion>& packageVersion) + { + if (!m_database || !packageVersion) + { + return PinType::Unknown; + } + + std::optional<Pin> incomingPin; + + PinKey pinKey{ packageVersion->GetProperty(PackageVersionProperty::Id).get(), packageVersion->GetSource().GetIdentifier()}; + auto itr = m_availablePins.find(pinKey); + if (itr == m_availablePins.end()) + { + incomingPin = m_database->GetPin(pinKey); + m_availablePins[pinKey] = incomingPin; + } + else + { + incomingPin = itr->second; + } + + return GetPinnedStateForVersion(packageVersion->GetProperty(PackageVersionProperty::Version).get(), incomingPin, m_installedPin, m_behavior); + } + + // Creates an object for use in evaluating pinning data for a given package + PinningData::PinStateEvaluator PinningData::CreatePinStateEvaluator( + PinBehavior behavior, + const std::shared_ptr<IPackageVersion>& installedVersion) + { + return { behavior, m_database, installedVersion }; + } +} diff --git a/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h b/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "Microsoft/CheckpointDatabase.h" +#include <winget/CheckpointDatabase.h> #include <guiddef.h> using namespace AppInstaller::Repository::Microsoft; @@ -18,7 +18,7 @@ namespace AppInstaller::Checkpoints struct CheckpointManager; - // A representation of a row in the Checkpoint table. + // A representation of a row in the Checkpoint table. template <typename T> struct Checkpoint { diff --git a/src/AppInstallerRepositoryCore/Public/winget/CheckpointDatabase.h b/src/AppInstallerRepositoryCore/Public/winget/CheckpointDatabase.h @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStorageBase.h> +#include <winget/ManagedFile.h> + +namespace AppInstaller::Repository::Microsoft +{ + namespace Schema + { + struct ICheckpointDatabase; + } + + struct CheckpointDatabase : SQLite::SQLiteStorageBase + { + // An id that refers to a specific Checkpoint. + using IdType = SQLite::rowid_t; + + CheckpointDatabase(const CheckpointDatabase&) = delete; + CheckpointDatabase& operator=(const CheckpointDatabase&) = delete; + + CheckpointDatabase(CheckpointDatabase&&); + CheckpointDatabase& operator=(CheckpointDatabase&&); + + // Create a new checkpoint database. + static std::shared_ptr<CheckpointDatabase> CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); + + // Opens an existing checkpoint database. + static std::shared_ptr<CheckpointDatabase> Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}); + + // Returns a value indicating whether the database is empty. + bool IsEmpty(); + + // Adds a new checkpoint name to the checkpoint table. + IdType AddCheckpoint(std::string_view checkpointName); + + // Returns all checkpoint ids in descending (newest at the front) order. + std::vector<IdType> GetCheckpointIds(); + + // Returns a boolean value indicating a field exists for a checkpoint data type. + bool HasDataField(IdType checkpointId, int type, const std::string& name); + + // Returns the available data types for a checkpoint id. + std::vector<int> GetDataTypes(IdType checkpointId); + + // Returns the available field names for a checkpoint data. + std::vector<std::string> GetDataFieldNames(IdType checkpointId, int dataType); + + // Sets the value(s) for a data type and field. + void SetDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector<std::string>& values); + + // Updates the value(s) for a data type and field. + void UpdateDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector<std::string>& values); + + // Gets a single value for a data type field. + std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, const std::string& field); + + // Gets multiple values for a data type field. + std::vector<std::string> GetDataFieldMultiValue(IdType checkpointId, int dataType, const std::string& field); + + // Removes the value(s) for a data type. + void RemoveDataType(IdType checkpointId, int dataType); + + private: + // Constructor used to open an existing index. + CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); + + // Constructor used to create a new index. + CheckpointDatabase(const std::string& target, SQLite::Version version); + + std::unique_ptr<Schema::ICheckpointDatabase> m_interface; + }; +}+ \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Public/winget/PinningData.h b/src/AppInstallerRepositoryCore/Public/winget/PinningData.h @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/Pin.h> +#include <winget/RepositorySearch.h> +#include <map> +#include <memory> +#include <optional> +#include <vector> + +namespace AppInstaller::Repository::Microsoft +{ + struct PinningIndex; +} + +namespace AppInstaller::Pinning +{ + // Possible ways to consider pins when getting a package's available versions + enum class PinBehavior + { + // Ignore pins, returns all available versions. + IgnorePins, + // Include available versions for packages with a Pinning pin. + // Blocking pins and Gating pins still respected. + IncludePinned, + // Respect all the types of pins. + ConsiderPins, + }; + + // The public representation of the pinning database. + struct PinningData + { + // Creates an empty pinning data. + PinningData(); + + // Enum to make the pinning data disposition clear in the caller. + enum class Disposition + { + // The data can only be read. + ReadOnly, + // The data can be read and written. + ReadWrite, + }; + + // Creates a usable pinning data with the given read/write capability. + PinningData(Disposition disposition); + + PinningData(const PinningData&); + PinningData& operator=(const PinningData&); + PinningData(PinningData&&) noexcept; + PinningData& operator=(PinningData&&) noexcept; + ~PinningData(); + + // Determines if the pinning database is opened + operator bool() const; + bool IsDatabaseConnected() const; + + // Pass through functions to the index itself + void AddOrUpdatePin(const Pin& pin); + void RemovePin(const PinKey& pinKey); + std::optional<Pin> GetPin(const PinKey& pinKey); + std::vector<Pin> GetAllPins(); + bool ResetAllPins(std::string_view sourceId = {}); + + // A type used for evaluating the pinning state for a given package. + struct PinStateEvaluator + { + PinStateEvaluator( + PinBehavior behavior, + std::shared_ptr<AppInstaller::Repository::Microsoft::PinningIndex> database, + const std::shared_ptr<AppInstaller::Repository::IPackageVersion>& installedVersion); + + PinStateEvaluator(const PinStateEvaluator&); + PinStateEvaluator& operator=(const PinStateEvaluator&); + PinStateEvaluator(PinStateEvaluator&&) noexcept; + PinStateEvaluator& operator=(PinStateEvaluator&&) noexcept; + + ~PinStateEvaluator(); + + // Gets the latest available package version that fits within the pinning restrictions. + // This should be the package object that contains available versions associated with the installed version for which this evaluator was created. + std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetLatestAvailableVersionForPins(const std::shared_ptr<AppInstaller::Repository::IPackage>& package); + + // Determines if the given version is an update to the installed version that this object was created with. + // This should be a version associated with the installed version for which this evaluator was created. + bool IsUpdate(const std::shared_ptr<AppInstaller::Repository::IPackageVersion>& availableVersion); + + // Determines the pin type to apply to the given version. + PinType EvaluatePinType(const std::shared_ptr<AppInstaller::Repository::IPackageVersion>& packageVersion); + + private: + PinBehavior m_behavior; + std::shared_ptr<AppInstaller::Repository::Microsoft::PinningIndex> m_database; + std::optional<Pin> m_installedPin; + std::optional<Utility::VersionAndChannel> m_installedVersion; + // Cache pins for available version to reduce database lookups. + std::map<PinKey, std::optional<Pin>> m_availablePins; + }; + + // Creates an object for use in evaluating pinning data for a given package + PinStateEvaluator CreatePinStateEvaluator( + PinBehavior behavior, + const std::shared_ptr<AppInstaller::Repository::IPackageVersion>& installedVersion); + + private: + std::shared_ptr<AppInstaller::Repository::Microsoft::PinningIndex> m_database; + }; +} diff --git a/src/AppInstallerRepositoryCore/Public/winget/PortableIndex.h b/src/AppInstallerRepositoryCore/Public/winget/PortableIndex.h @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStorageBase.h> +#include <winget/PortableFileEntry.h> +#include <winget/ManagedFile.h> + +using namespace AppInstaller::Portable; + +namespace AppInstaller::Repository::Microsoft +{ + namespace Schema + { + struct IPortableIndex; + } + + struct PortableIndex : SQLite::SQLiteStorageBase + { + // An id that refers to a specific portable file. + using IdType = SQLite::rowid_t; + + PortableIndex(const PortableIndex&) = delete; + PortableIndex& operator=(const PortableIndex&) = delete; + + PortableIndex(PortableIndex&&); + PortableIndex& operator=(PortableIndex&&); + + ~PortableIndex(); + + // Creates a new PortableIndex database of the given version. + static PortableIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); + + // Opens an existing PortableIndex database. + static PortableIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}); + + IdType AddPortableFile(const Portable::PortableFileEntry& file); + + void RemovePortableFile(const Portable::PortableFileEntry& file); + + bool UpdatePortableFile(const Portable::PortableFileEntry& file); + + void AddOrUpdatePortableFile(const Portable::PortableFileEntry& file); + + std::vector<Portable::PortableFileEntry> GetAllPortableFiles(); + + bool Exists(const Portable::PortableFileEntry& file); + + bool IsEmpty(); + + private: + // Constructor used to open an existing index. + PortableIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); + + // Constructor used to create a new index. + PortableIndex(const std::string& target, SQLite::Version version); + + // Creates the IPortableIndex interface object for this version. + std::unique_ptr<Schema::IPortableIndex> CreateIPortableIndex() const; + + std::unique_ptr<Schema::IPortableIndex> m_interface; + }; +}+ \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -6,7 +6,6 @@ #include <AppInstallerVersions.h> #include <winget/LocIndependent.h> #include <winget/Manifest.h> -#include <winget/Pin.h> #include <map> #include <memory> @@ -237,8 +236,8 @@ namespace AppInstaller::Repository { PackageVersionKey() = default; - PackageVersionKey(Utility::NormalizedString sourceId, Utility::NormalizedString version, Utility::NormalizedString channel, Pinning::PinType pinnedState = Pinning::PinType::Unknown) : - SourceId(std::move(sourceId)), Version(std::move(version)), Channel(std::move(channel)), PinnedState(pinnedState) {} + PackageVersionKey(Utility::NormalizedString sourceId, Utility::NormalizedString version, Utility::NormalizedString channel) : + SourceId(std::move(sourceId)), Version(std::move(version)), Channel(std::move(channel)) {} // The source id that this version came from. std::string SourceId; @@ -249,10 +248,6 @@ namespace AppInstaller::Repository // The channel. Utility::NormalizedString Channel; - // The pin state for this package version, if it came from a list of available versions. - // When used to look up a package version, this field is not considered. - Pinning::PinType PinnedState = Pinning::PinType::Unknown; - bool operator<(const PackageVersionKey& other) const { // Sort using only the version and channel. @@ -317,18 +312,6 @@ namespace AppInstaller::Repository std::vector<InstalledStatus> Status; }; - // Possible ways to consider pins when getting a package's available versions - enum class PinBehavior - { - // Ignore pins, returns all available versions. - IgnorePins, - // Include available versions for packages with a Pinning pin. - // Blocking pins and Gating pins still respected. - IncludePinned, - // Respect all the types of pins. - ConsiderPins, - }; - // To allow for runtime casting from IPackage to the specific types, this enum contains all of the IPackage implementations. enum class IPackageType { @@ -351,31 +334,18 @@ namespace AppInstaller::Repository // Gets the installed package information. virtual std::shared_ptr<IPackageVersion> GetInstalledVersion() const = 0; - // Note on pins: - // Pins only make sense when there is both an installed and an available version. - // Only for the composite source will GetAvailableVersionKeys() include pinned state, - // and GetLatestAvailableVersion() consider the pin behavior. - // Gets all available versions of this package. // The versions will be returned in sorted, descending order. // Ex. { 4, 3, 2, 1 } // The list may contain versions from multiple sources. - virtual std::vector<PackageVersionKey> GetAvailableVersionKeys(PinBehavior pinBehavior = PinBehavior::ConsiderPins) const = 0; + virtual std::vector<PackageVersionKey> GetAvailableVersionKeys() const = 0; // Gets a specific version of this package. - virtual std::shared_ptr<IPackageVersion> GetLatestAvailableVersion(PinBehavior pinBehavior) const = 0; + virtual std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const = 0; // Gets a specific version of this package. virtual std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const = 0; - virtual std::pair<std::shared_ptr<IPackageVersion>, Pinning::PinType> GetAvailableVersionAndPin(const PackageVersionKey& versionKey) const - { - return { GetAvailableVersion(versionKey), Pinning::PinType::Unknown }; - } - - // Gets a value indicating whether an available version is newer than the installed version. - virtual bool IsUpdateAvailable(PinBehavior pinBehavior) const = 0; - // Determines if the given IPackage refers to the same package as this one. virtual bool IsSame(const IPackage*) const = 0; diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -59,7 +59,7 @@ namespace AppInstaller::Repository::Rest return {}; } - std::vector<PackageVersionKey> GetAvailableVersionKeys(PinBehavior) const override + std::vector<PackageVersionKey> GetAvailableVersionKeys() const override { std::shared_ptr<const RestSource> source = GetReferenceSource(); std::scoped_lock versionsLock{ m_packageVersionsLock }; @@ -74,7 +74,7 @@ namespace AppInstaller::Repository::Rest return result; } - std::shared_ptr<IPackageVersion> GetLatestAvailableVersion(PinBehavior) const override + std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const override { std::scoped_lock versionsLock{ m_packageVersionsLock }; return GetLatestVersionInternal(); @@ -82,11 +82,6 @@ namespace AppInstaller::Repository::Rest std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const override; - bool IsUpdateAvailable(PinBehavior) const override - { - return false; - } - bool IsSame(const IPackage* other) const override { const AvailablePackage* otherAvailablePackage = PackageCast<const AvailablePackage*>(other); diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -11,6 +11,7 @@ #include "PackageInstallerInstalledStatus.h" #include "CheckInstalledStatusResult.h" #include <wil\cppwinrt_wrl.h> +#include <winget/PinningData.h> namespace winrt::Microsoft::Management::Deployment::implementation @@ -62,14 +63,22 @@ namespace winrt::Microsoft::Management::Deployment::implementation }); return m_availableVersions.GetView(); } - Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::DefaultInstallVersion() + + void CatalogPackage::InitializeDefaultInstallVersion() { std::call_once(m_defaultInstallVersionOnceFlag, [&]() { - std::shared_ptr<::AppInstaller::Repository::IPackageVersion> latestVersion = m_package.get()->GetLatestAvailableVersion(AppInstaller::Repository::PinBehavior::IgnorePins); + using namespace AppInstaller::Pinning; + + PinningData pinningData{ PinningData::Disposition::ReadOnly }; + auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, m_package->GetInstalledVersion()); + + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> latestVersion = evaluator.GetLatestAvailableVersionForPins(m_package); if (latestVersion) { + m_updateAvailable = evaluator.IsUpdate(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<wil::details::module_count_wrapper< @@ -78,8 +87,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation m_defaultInstallVersion = *latestVersionImpl; } }); + } + + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::DefaultInstallVersion() + { + InitializeDefaultInstallVersion(); return m_defaultInstallVersion; } + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::GetPackageVersionInfo(Microsoft::Management::Deployment::PackageVersionId const& versionKey) { winrt::Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo{ nullptr }; @@ -95,10 +110,13 @@ namespace winrt::Microsoft::Management::Deployment::implementation } return packageVersionInfo; } + bool CatalogPackage::IsUpdateAvailable() { - return m_package->IsUpdateAvailable(AppInstaller::Repository::PinBehavior::IgnorePins); + InitializeDefaultInstallVersion(); + return m_updateAvailable; } + Windows::Foundation::IAsyncOperation<winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult> CatalogPackage::CheckInstalledStatusAsync( Microsoft::Management::Deployment::InstalledStatusType checkTypes) { diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -35,12 +35,15 @@ namespace winrt::Microsoft::Management::Deployment::implementation private: ::AppInstaller::Repository::Source m_source; std::shared_ptr<::AppInstaller::Repository::IPackage> m_package; + bool m_updateAvailable = false; Windows::Foundation::Collections::IVector<winrt::Microsoft::Management::Deployment::PackageVersionId> m_availableVersions{ winrt::single_threaded_vector<winrt::Microsoft::Management::Deployment::PackageVersionId>() }; 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; + + void InitializeDefaultInstallVersion(); #endif }; } \ No newline at end of file