Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions doc/ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
## New in v1.29

# New Feature: Source Priority

> [!NOTE]
> Experimental under `sourcePriority`; defaulted to disabled.

With this feature, one can assign a numerical priority to sources when added or later through the `source edit`
command. Sources with higher priority are sorted first in the list of sources, which results in them getting put first
in the results if other things are equal.

> [!TIP]
> Search result ordering in winget is currently based on these values in this order:
> 1. Match quality (how well a valid field matches the search request)
> 2. Match field (which field was matched against the search request)
> 3. Source order (was always relevant, but with priority you can more easily affect this)

Beyond the ability to slightly affect the result ordering, commands that primarily target available packages
(largely `install`) will now prefer to use a single result from a source with higher priority rather than prompting for
disambiguation from the user. Said another way, if multiple sources return results but only one of those sources has
the highest priority value (and it returned only one result) then that package will be used rather than giving a
"multiple packages were found" error. This has been applied to both winget CLI and PowerShell module commands.

### REST result match criteria update

Along with the source priority change, the results from REST sources (like `msstore`) now attempt to correctly set the
match criteria that factor into the result ordering. This will prevent them from being sorted to the top automatically.

## Bug Fixes

<!-- Nothing yet! -->
12 changes: 12 additions & 0 deletions doc/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,15 @@ This feature enables support for fonts via `winget settings`. The `winget font l
"fonts": true
},
```

### sourcePriority

This feature enables sources to have a priority value assigned. Sources with a higher priority will appear earlier in search results and will be selected for installing new packages when multiple sources have a matching package.

Note that search result ordering is dependent on several factors, and source priority is the lowest field in that currently (match quality and field are more important).

```json
"experimentalFeatures": {
"sourcePriority": true
},
```
5 changes: 5 additions & 0 deletions schemas/JSON/settings/settings.schema.0.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,11 @@
"description": "Enable support for managing fonts",
"type": "boolean",
"default": false
},
"sourcePriority": {
"description": "Enable source priority feature",
"type": "boolean",
"default": false
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/AppInstallerCLI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{F49C
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ComInprocTestbed", "ComInprocTestbed\ComInprocTestbed.vcxproj", "{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DSC", "DSC", "{40D7CA7F-EB86-4345-9641-AD27180C559D}"
ProjectSection(SolutionItems) = preProject
PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psd1 = PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psd1
PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psm1 = PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psm1
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.WinGet.UnitTests", "PowerShell\Microsoft.WinGet.UnitTests\Microsoft.WinGet.UnitTests.csproj", "{5421394F-5619-4E4B-8923-F3FB30D5EFAD}"
EndProject
Global
Expand Down Expand Up @@ -1100,6 +1106,7 @@ Global
{7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{F49C4C89-447E-4D15-B38B-5A8DCFB134AF} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{40D7CA7F-EB86-4345-9641-AD27180C559D} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}
{5421394F-5619-4E4B-8923-F3FB30D5EFAD} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
6 changes: 5 additions & 1 deletion src/AppInstallerCLICore/Argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ namespace AppInstaller::CLI
return { type, "trust-level"_liv };
case Execution::Args::Type::SourceEditExplicit:
return { type, "explicit"_liv, 'e' };
case Execution::Args::Type::SourcePriority:
return { type, "priority"_liv, 'p' };

// Hash Command
case Execution::Args::Type::HashFile:
Expand Down Expand Up @@ -415,7 +417,9 @@ namespace AppInstaller::CLI
case Args::Type::SourceExplicit:
return Argument{ type, Resource::String::SourceExplicitArgumentDescription, ArgumentType::Flag };
case Args::Type::SourceEditExplicit:
return Argument{ type, Resource::String::SourceEditExplicitArgumentDescription, ArgumentType::Positional };
return Argument{ type, Resource::String::SourceEditExplicitArgumentDescription, ArgumentType::Standard };
case Args::Type::SourcePriority:
return Argument{ type, Resource::String::SourcePriorityArgumentDescription, ArgumentType::Standard, ExperimentalFeature::Feature::SourcePriority };
case Args::Type::SourceTrustLevel:
return Argument{ type, Resource::String::SourceTrustLevelArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help };
case Args::Type::ValidateManifest:
Expand Down
14 changes: 14 additions & 0 deletions src/AppInstallerCLICore/Commands/DscComposableObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ namespace AppInstaller::CLI
}
};

template <>
struct GetJsonTypeValue<int32_t>
{
static int32_t Get(const Json::Value& value)
{
return value.asInt();
}

static Json::ValueType SchemaType()
{
return Json::ValueType::intValue;
}
};

template <>
struct GetJsonTypeValue<Json::Value>
{
Expand Down
96 changes: 91 additions & 5 deletions src/AppInstallerCLICore/Commands/DscSourceResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Resources.h"
#include "Workflows/SourceFlow.h"
#include <winget/RepositorySource.h>
#include <winget/ExperimentalFeature.h>

using namespace AppInstaller::Utility::literals;
using namespace AppInstaller::Repository;
Expand All @@ -20,8 +21,9 @@ namespace AppInstaller::CLI
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(TrustLevelProperty, std::string, TrustLevel, "trustLevel", Resource::String::DscResourcePropertyDescriptionSourceTrustLevel, ({ "undefined", "none", "trusted" }), "undefined");
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(ExplicitProperty, bool, Explicit, "explicit", Resource::String::DscResourcePropertyDescriptionSourceExplicit);
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(AcceptAgreementsProperty, bool, AcceptAgreements, "acceptAgreements", Resource::String::DscResourcePropertyDescriptionAcceptAgreements);
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(PriorityProperty, int32_t, Priority, "priority", Resource::String::DscResourcePropertyDescriptionSourcePriority);

using SourceResourceObject = DscComposableObject<StandardExistProperty, StandardInDesiredStateProperty, NameProperty, ArgumentProperty, TypeProperty, TrustLevelProperty, ExplicitProperty, AcceptAgreementsProperty>;
using SourceResourceObject = DscComposableObject<StandardExistProperty, StandardInDesiredStateProperty, NameProperty, ArgumentProperty, TypeProperty, TrustLevelProperty, ExplicitProperty, AcceptAgreementsProperty, PriorityProperty>;

std::string TrustLevelStringFromFlags(SourceTrustLevel trustLevel)
{
Expand Down Expand Up @@ -109,6 +111,11 @@ namespace AppInstaller::CLI
Output.TrustLevel(TrustLevelStringFromFlags(source.TrustLevel));
Output.Explicit(source.Explicit);

if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority))
{
Output.Priority(source.Priority);
}

std::vector<Repository::SourceDetails> sources;
sources.emplace_back(source);
SubContext->Add<Execution::Data::SourceList>(std::move(sources));
Expand Down Expand Up @@ -148,6 +155,14 @@ namespace AppInstaller::CLI
SubContext->Args.AddArg(Execution::Args::Type::SourceExplicit);
}

std::string priorityString;
if (Input.Priority())
{
THROW_HR_IF(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED, !Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority));
priorityString = std::to_string(Input.Priority().value());
SubContext->Args.AddArg(Execution::Args::Type::SourcePriority, priorityString);
}

*SubContext <<
Workflow::EnsureRunningAsAdmin <<
Workflow::CreateSourceForSourceAdd <<
Expand All @@ -168,11 +183,50 @@ namespace AppInstaller::CLI
Workflow::RemoveSources;
}

void Edit()
{
AICLI_LOG(CLI, Verbose, << "Source::Edit invoked");

if (!SubContext->Args.Contains(Execution::Args::Type::SourceName))
{
SubContext->Args.AddArg(Execution::Args::Type::SourceName, Input.SourceName().value());
}

std::string explicitString;
if (Input.Explicit())
{
explicitString = Utility::ConvertBoolToString(Input.Explicit().value());
SubContext->Args.AddArg(Execution::Args::Type::SourceEditExplicit, explicitString);
}

std::string priorityString;
if (Input.Priority())
{
THROW_HR_IF(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED, !Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority));
priorityString = std::to_string(Input.Priority().value());
SubContext->Args.AddArg(Execution::Args::Type::SourcePriority, priorityString);
}

*SubContext <<
Workflow::EnsureRunningAsAdmin <<
Workflow::EditSources;
}

void Replace()
{
AICLI_LOG(CLI, Verbose, << "Source::Replace invoked");
Remove();
Add();

// Check to see if we can use an edit rather than a complete replacement
if (TestArgument() && TestType() && TestTrustLevel())
{
// Implies that the failing portion of Test was in the editable Explicit or Priority properties
Edit();
}
else
{
Remove();
Add();
}
}

// Determines if the current Output values match the Input values state.
Expand All @@ -185,8 +239,9 @@ namespace AppInstaller::CLI
{
if (Output.Exist().value())
{
AICLI_LOG(CLI, Verbose, << "Source::Test needed to inspect these properties: Argument(" << TestArgument() << "), Type(" << TestType() << "), TrustLevel(" << TestTrustLevel() << "), Explicit(" << TestExplicit() << ")");
return TestArgument() && TestType() && TestTrustLevel() && TestExplicit();
AICLI_LOG(CLI, Verbose, << "Source::Test needed to inspect these properties: Argument(" << TestArgument() <<
"), Type(" << TestType() << "), TrustLevel(" << TestTrustLevel() << "), Explicit(" << TestExplicit() << "), Priority(" << TestPriority() << ")");
return TestArgument() && TestType() && TestTrustLevel() && TestExplicit() && TestPriority();
}
else
{
Expand Down Expand Up @@ -233,6 +288,11 @@ namespace AppInstaller::CLI
{
result.append(std::string{ ExplicitProperty::Name() });
}

if (!TestPriority())
{
result.append(std::string{ PriorityProperty::Name() });
}
}

return result;
Expand Down Expand Up @@ -308,6 +368,26 @@ namespace AppInstaller::CLI
return true;
}
}

bool TestPriority()
{
if (Input.Priority())
{
THROW_HR_IF(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED, !Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority));
if (Output.Priority())
{
return Input.Priority().value() == Output.Priority().value();
}
else
{
return false;
}
}
else
{
return true;
}
}
};
}

Expand Down Expand Up @@ -418,6 +498,12 @@ namespace AppInstaller::CLI
output.Type(source.Type);
output.TrustLevel(TrustLevelStringFromFlags(source.TrustLevel));
output.Explicit(source.Explicit);

if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority))
{
output.Priority(source.Priority);
}

WriteJsonOutputLine(context, output.ToJson());
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/AppInstallerCLICore/Commands/SourceCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ namespace AppInstaller::CLI
using namespace AppInstaller::CLI::Execution;
using namespace std::string_view_literals;

namespace
{
void ValidateSourcePriorityArgument(const Args& execArgs)
{
if (execArgs.Contains(Execution::Args::Type::SourcePriority))
{
std::string_view priorityArg = execArgs.GetArg(Execution::Args::Type::SourcePriority);
auto convertedArg = Utility::TryConvertStringToInt32(priorityArg);
if (!convertedArg.has_value())
{
throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues(Argument::ForType(Execution::Args::Type::SourcePriority).Name()));
}
}
}
}

Utility::LocIndView s_SourceCommand_HelpLink = "https://aka.ms/winget-command-source"_liv;

std::vector<std::unique_ptr<Command>> SourceCommand::GetCommands() const
Expand Down Expand Up @@ -57,6 +73,7 @@ namespace AppInstaller::CLI
Argument::ForType(Args::Type::CustomHeader),
Argument::ForType(Args::Type::AcceptSourceAgreements),
Argument::ForType(Args::Type::SourceExplicit),
Argument::ForType(Args::Type::SourcePriority),
};
}

Expand Down Expand Up @@ -96,6 +113,8 @@ namespace AppInstaller::CLI
throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::SourceTrustLevel).Name, Utility::Join(","_liv, validOptions)));
}
}

ValidateSourcePriorityArgument(execArgs);
}

void SourceAddCommand::ExecuteInternal(Context& context) const
Expand Down Expand Up @@ -321,6 +340,7 @@ namespace AppInstaller::CLI
return {
Argument::ForType(Args::Type::SourceName).SetRequired(true),
Argument::ForType(Args::Type::SourceEditExplicit),
Argument::ForType(Args::Type::SourcePriority),
};
}

Expand Down Expand Up @@ -354,6 +374,8 @@ namespace AppInstaller::CLI
throw CommandException(Resource::String::InvalidArgumentValueError(Argument::ForType(Execution::Args::Type::SourceEditExplicit).Name(), validOptions));
}
}

ValidateSourcePriorityArgument(execArgs);
}

void SourceEditCommand::ExecuteInternal(Context& context) const
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/ExecutionArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ namespace AppInstaller::CLI::Execution
ForceSourceReset,
SourceExplicit,
SourceTrustLevel,
SourcePriority,
SourceEditExplicit,

//Hash Command
Expand Down
4 changes: 4 additions & 0 deletions src/AppInstallerCLICore/Resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceType);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceTrustLevel);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceExplicit);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourcePriority);
WINGET_DEFINE_RESOURCE_STRINGID(DscSourceResourceShortDescription);
WINGET_DEFINE_RESOURCE_STRINGID(DscSourceResourceLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(EnableAdminSettingFailed);
Expand Down Expand Up @@ -477,6 +478,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(MultipleExclusiveArgumentsProvided);
WINGET_DEFINE_RESOURCE_STRINGID(MultipleInstalledPackagesFound);
WINGET_DEFINE_RESOURCE_STRINGID(MultiplePackagesFound);
WINGET_DEFINE_RESOURCE_STRINGID(MultiplePackagesFoundFilteredBySourcePriority);
WINGET_DEFINE_RESOURCE_STRINGID(MultipleUnsupportedNestedInstallersSpecified);
WINGET_DEFINE_RESOURCE_STRINGID(MultiQueryArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(MultiQueryPackageAlreadyInstalled);
Expand Down Expand Up @@ -722,6 +724,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(SourceListName);
WINGET_DEFINE_RESOURCE_STRINGID(SourceListNoneFound);
WINGET_DEFINE_RESOURCE_STRINGID(SourceListNoSources);
WINGET_DEFINE_RESOURCE_STRINGID(SourceListPriority);
WINGET_DEFINE_RESOURCE_STRINGID(SourceListTrustLevel);
WINGET_DEFINE_RESOURCE_STRINGID(SourceListType);
WINGET_DEFINE_RESOURCE_STRINGID(SourceListUpdated);
Expand All @@ -731,6 +734,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenFailedSuggestion);
WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenPredefinedFailedSuggestion);
WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenWithFailedUpdate);
WINGET_DEFINE_RESOURCE_STRINGID(SourcePriorityArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveAll);
WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveCommandShortDescription);
Expand Down
Loading