From 4d0d0b60fcd610ac8c7e6fc2c646d4fe5e316845 Mon Sep 17 00:00:00 2001 From: David Bennett Date: Thu, 9 Oct 2025 15:48:07 -0700 Subject: [PATCH 1/7] Remove experimental from Font Install, Uninstall, and source --- doc/ReleaseNotes.md | 21 ++++++++++++++----- .../Workflows/InstallFlow.cpp | 1 - .../Workflows/UninstallFlow.cpp | 1 - src/AppInstallerCLIE2ETests/GroupPolicy.cs | 2 -- .../SourcePolicy.cpp | 2 +- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/doc/ReleaseNotes.md b/doc/ReleaseNotes.md index eb73670206..3157b744f6 100644 --- a/doc/ReleaseNotes.md +++ b/doc/ReleaseNotes.md @@ -2,14 +2,29 @@ * MCP server available; run `winget mcp` for assistance on configuring your client. * App Installer now uses WinUI 3. The package dependency on WinUI 2 has been replaced by a dependency on the Windows App Runtime 1.8. * Manifest schema and validation updated to v1.12. This version update adds `Font` as an `InstallerType` and `NestedInstallerType`. +* Font Install, Uninstall, and a winget-fonts source have been added and are non-experimental. ## Bug Fixes * Manifest validation no longer fails using `UTF-8 BOM` encoding when the schema header is on the first line * Upgrading a portable package with dev mode disabled will no longer remove the package from the PATH variable. * Fixed source open failure when there were multiple sources but less than two non-explicit sources. +## Font Support +Font Install and Uninstall via manifest and package source for user and machine scopes has been added. +A sample Font manifest can be found at: +https://github.com/microsoft/winget-pkgs/tree/master/fonts/m/Microsoft/FluentFonts/1.0.0.0 + +At this time install and removal of fonts is only supported for fonts installed via WinGet Package. + +Fonts must either be the Installer or a .zip archive of NestedInstaller fonts. + +A new explicit source for fonts has been added "winget-font". +```winget search font -s winget-font``` + +This source is not yet accepting public submissions at this time. + ## Experimental Features -* Experimental support for Fonts +* Experimental support still exists for the 'font' command. --- ### Experimental support for Fonts @@ -22,8 +37,4 @@ The following snippet enables experimental support for fonts via `winget setting } } ``` -Manifest Schema version updated to 1.12.0 to add support for fonts. - -Experimental initial Font Install and Uninstall via manifest for user and machine scopes has been added. - The font 'list' command has been updated with a new '--details' feature for an alternate view of the installed fonts. diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 5a3ae11521..130cddb2b2 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -285,7 +285,6 @@ namespace AppInstaller::CLI::Workflow void FontInstall(Execution::Context& context) { context << - EnsureFeatureEnabled(ExperimentalFeature::Feature::Font) << GetInstallerArgs << FontInstallImpl << ReportInstallerResult("Font"sv, APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED, true); diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index e0d4e5706f..55bbe18d6d 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -354,7 +354,6 @@ namespace AppInstaller::CLI::Workflow break; case InstallerTypeEnum::Font: context << - EnsureFeatureEnabled(Settings::ExperimentalFeature::Feature::Font) << Workflow::FontUninstallImpl << ReportUninstallerResult("Font"sv, APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED, true); break; diff --git a/src/AppInstallerCLIE2ETests/GroupPolicy.cs b/src/AppInstallerCLIE2ETests/GroupPolicy.cs index 2934ed556e..cb7f0698f8 100644 --- a/src/AppInstallerCLIE2ETests/GroupPolicy.cs +++ b/src/AppInstallerCLIE2ETests/GroupPolicy.cs @@ -166,9 +166,7 @@ public void EnableMicrosoftStoreSource() [Test] public void EnableFontSource() { - WinGetSettingsHelper.ConfigureFeature("fonts", true); GroupPolicyHelper.EnableFontSource.Disable(); - var result = TestCommon.RunAICLICommand("source list", "winget-font"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); diff --git a/src/AppInstallerRepositoryCore/SourcePolicy.cpp b/src/AppInstallerRepositoryCore/SourcePolicy.cpp index da6f05a222..7662d306fa 100644 --- a/src/AppInstallerRepositoryCore/SourcePolicy.cpp +++ b/src/AppInstallerRepositoryCore/SourcePolicy.cpp @@ -191,7 +191,7 @@ namespace AppInstaller::Repository case AppInstaller::Repository::WellKnownSource::MicrosoftStore: return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::None, onlyExplicit, TogglePolicy::Policy::MSStoreSource); case AppInstaller::Repository::WellKnownSource::WinGetFont: - return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::Font, onlyExplicit, TogglePolicy::Policy::FontSource); + return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::None, onlyExplicit, TogglePolicy::Policy::FontSource); case AppInstaller::Repository::WellKnownSource::DesktopFrameworks: // No corresponding policy available for this source. return true; From 318d75c88cc102913c8df36daf2c565564ebd64d Mon Sep 17 00:00:00 2001 From: John McPherson Date: Fri, 10 Oct 2025 14:44:19 -0700 Subject: [PATCH 2/7] Update various source usages from COM --- samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.cpp | 7 +++++-- .../Commands/Common/FinderCommand.cs | 5 ++--- .../Commands/Common/ManagementDeploymentCommand.cs | 5 +++-- .../Commands/SourceCommand.cs | 6 +++--- src/WinGetMCPServer/WingetPackageTools.cs | 5 +++-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.cpp b/samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.cpp index 37d7434448..de7419fa91 100644 --- a/samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.cpp +++ b/samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.cpp @@ -309,13 +309,16 @@ namespace winrt::WinGetUWPCaller::implementation if (selectedItems.Size() == 0) { - // If no items are selected, we use all available catalogs. + // If no items are selected, we use all implicit catalogs. CreateCompositePackageCatalogOptions createCompositePackageCatalogOptions; createCompositePackageCatalogOptions.CompositeSearchBehavior(CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs); for (const auto& item : m_packageManager.GetPackageCatalogs()) { - createCompositePackageCatalogOptions.Catalogs().Append(item); + if (!item.Info().Explicit()) + { + createCompositePackageCatalogOptions.Catalogs().Append(item); + } } catalogReference = m_packageManager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions); diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs index 9279a9eaee..832003dd68 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs @@ -157,10 +157,9 @@ private PackageCatalog GetPackageCatalog(CompositeSearchBehavior behavior) private PackageCatalogReference GetPackageCatalogReference(CompositeSearchBehavior behavior) { CreateCompositePackageCatalogOptions options = ManagementDeploymentFactory.Instance.CreateCreateCompositePackageCatalogOptions(); - IReadOnlyList references = this.GetPackageCatalogReferences(this.Source); - for (var i = 0; i < references.Count; i++) + foreach (var reference in this.GetPackageCatalogReferences(this.Source)) { - options.Catalogs.Add(references[i]); + options.Catalogs.Add(reference); } options.CompositeSearchBehavior = behavior; diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs index bdb608aa98..37fd68f495 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs @@ -8,6 +8,7 @@ namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System; using System.Collections.Generic; + using System.Linq; using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; @@ -46,11 +47,11 @@ internal ManagementDeploymentCommand(PSCmdlet psCmdlet) /// A list of instances. /// The name of the source to retrieve. If null, then all sources are returned. /// The source does not exist. - internal IReadOnlyList GetPackageCatalogReferences(string? source) + internal IEnumerable GetPackageCatalogReferences(string? source) { if (string.IsNullOrEmpty(source)) { - return PackageManagerWrapper.Instance.GetPackageCatalogs(); + return PackageManagerWrapper.Instance.GetPackageCatalogs().Where(x => !x.Info.Explicit); } else { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs index 78c6559bca..03d9f2c8bd 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // @@ -34,9 +34,9 @@ public void Get(string name) { var results = this.Execute( () => this.GetPackageCatalogReferences(name)); - for (var i = 0; i < results.Count; i++) + foreach (var result in results) { - this.Write(StreamType.Object, new PSSourceResult(results[i])); + this.Write(StreamType.Object, new PSSourceResult(result)); } } } diff --git a/src/WinGetMCPServer/WingetPackageTools.cs b/src/WinGetMCPServer/WingetPackageTools.cs index 0d8b9c3023..4a99c2bba4 100644 --- a/src/WinGetMCPServer/WingetPackageTools.cs +++ b/src/WinGetMCPServer/WingetPackageTools.cs @@ -169,9 +169,10 @@ private ConnectResult ConnectCatalogWithResult(string? catalog = null) for (int i = 0; i < catalogs.Count; ++i) { var catalogRef = catalogs[i]; - if (string.IsNullOrEmpty(catalog) || catalogRef?.Info.Name == catalog) + if ((string.IsNullOrEmpty(catalog) && !catalogRef.Info.Explicit) + || catalogRef?.Info.Name == catalog) { - createCompositePackageCatalogOptions.Catalogs.Add(catalogs[i]); + createCompositePackageCatalogOptions.Catalogs.Add(catalogRef); } } createCompositePackageCatalogOptions.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; From 04dc0b0e4e47d91d6886c8b78d81b36acb1ecec8 Mon Sep 17 00:00:00 2001 From: David Bennett Date: Fri, 10 Oct 2025 14:52:23 -0700 Subject: [PATCH 3/7] Update number of default sources --- src/AppInstallerCLITests/Sources.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/Sources.cpp b/src/AppInstallerCLITests/Sources.cpp index d7e1b1df86..011d98b9a5 100644 --- a/src/AppInstallerCLITests/Sources.cpp +++ b/src/AppInstallerCLITests/Sources.cpp @@ -23,7 +23,7 @@ using namespace AppInstaller::Utility; using namespace std::string_literals; using namespace std::string_view_literals; -constexpr size_t c_DefaultSourceCount = 2; +constexpr size_t c_DefaultSourceCount = 3; constexpr std::string_view s_SourcesYaml_Sources = "Sources"sv; constexpr std::string_view s_SourcesYaml_Source_Name = "Name"sv; From c6979639c9f25c6287330d822c6d04f54e19a941 Mon Sep 17 00:00:00 2001 From: John McPherson Date: Fri, 10 Oct 2025 14:59:35 -0700 Subject: [PATCH 4/7] Handle explicit in finder directly --- .../Commands/Common/FinderCommand.cs | 17 ++++++++++++++++- .../Common/ManagementDeploymentCommand.cs | 5 ++--- .../Commands/SourceCommand.cs | 4 ++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs index 832003dd68..3dbde2ab76 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs @@ -159,7 +159,22 @@ private PackageCatalogReference GetPackageCatalogReference(CompositeSearchBehavi CreateCompositePackageCatalogOptions options = ManagementDeploymentFactory.Instance.CreateCreateCompositePackageCatalogOptions(); foreach (var reference in this.GetPackageCatalogReferences(this.Source)) { - options.Catalogs.Add(reference); + bool isExplicit = false; + try + { + // Execute in try block to catch interface not implemented on older servers. + isExplicit = reference.Info.Explicit; + } + catch + { + // Assume that any failure other than the interface not implemented to get Explicit + // will result in other failures shortly after this (like the server being gone). + } + + if (!isExplicit) + { + options.Catalogs.Add(reference); + } } options.CompositeSearchBehavior = behavior; diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs index 37fd68f495..bdb608aa98 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs @@ -8,7 +8,6 @@ namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System; using System.Collections.Generic; - using System.Linq; using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; @@ -47,11 +46,11 @@ internal ManagementDeploymentCommand(PSCmdlet psCmdlet) /// A list of instances. /// The name of the source to retrieve. If null, then all sources are returned. /// The source does not exist. - internal IEnumerable GetPackageCatalogReferences(string? source) + internal IReadOnlyList GetPackageCatalogReferences(string? source) { if (string.IsNullOrEmpty(source)) { - return PackageManagerWrapper.Instance.GetPackageCatalogs().Where(x => !x.Info.Explicit); + return PackageManagerWrapper.Instance.GetPackageCatalogs(); } else { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs index 03d9f2c8bd..dd9cde8087 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs @@ -34,9 +34,9 @@ public void Get(string name) { var results = this.Execute( () => this.GetPackageCatalogReferences(name)); - foreach (var result in results) + for (var i = 0; i < results.Count; i++) { - this.Write(StreamType.Object, new PSSourceResult(result)); + this.Write(StreamType.Object, new PSSourceResult(results[i])); } } } From 09f38cb76aad7c56e854b8a11296db76aceba9d3 Mon Sep 17 00:00:00 2001 From: David Bennett Date: Fri, 10 Oct 2025 17:19:05 -0700 Subject: [PATCH 5/7] Add font source to sources tests --- src/AppInstallerCLITests/Sources.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/AppInstallerCLITests/Sources.cpp b/src/AppInstallerCLITests/Sources.cpp index 011d98b9a5..1ca3ee51ab 100644 --- a/src/AppInstallerCLITests/Sources.cpp +++ b/src/AppInstallerCLITests/Sources.cpp @@ -50,6 +50,11 @@ constexpr std::string_view s_DefaultSourcesTombstoned = R"( Arg: "" Data: "" IsTombstone: true + - Name: winget-font + Type: "" + Arg: "" + Data: "" + IsTombstone: true )"sv; constexpr std::string_view s_SingleSource = R"( @@ -122,6 +127,11 @@ constexpr std::string_view s_ThreeSources = R"( Arg: "" Data: "" IsTombstone: true + - Name: winget-font + Type: "" + Arg: "" + Data: "" + IsTombstone: true )"sv; constexpr std::string_view s_ThreeSourcesMetadata = R"( From cecad9ce085307c5cb072340dd5d10be3bd5b1b9 Mon Sep 17 00:00:00 2001 From: John McPherson Date: Mon, 13 Oct 2025 09:35:40 -0700 Subject: [PATCH 6/7] Forgot about the IEnumerable handling problems --- .../Commands/Common/FinderCommand.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs index 3dbde2ab76..445690b248 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs @@ -157,8 +157,10 @@ private PackageCatalog GetPackageCatalog(CompositeSearchBehavior behavior) private PackageCatalogReference GetPackageCatalogReference(CompositeSearchBehavior behavior) { CreateCompositePackageCatalogOptions options = ManagementDeploymentFactory.Instance.CreateCreateCompositePackageCatalogOptions(); - foreach (var reference in this.GetPackageCatalogReferences(this.Source)) + IReadOnlyList references = this.GetPackageCatalogReferences(this.Source); + for (var i = 0; i < references.Count; i++) { + var reference = references[i]; bool isExplicit = false; try { From 272387e65725019079f36eb3ee9c6af6ba54460e Mon Sep 17 00:00:00 2001 From: David Bennett Date: Mon, 13 Oct 2025 13:59:55 -0700 Subject: [PATCH 7/7] Change DSC test to use non-explicit source --- src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1 b/src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1 index 0dc01f6dd8..7be3009dd8 100644 --- a/src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1 +++ b/src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1 @@ -171,7 +171,7 @@ Describe 'WinGetPackage' { $testPackageId = 'AppInstallerTest.TestExeInstaller' $testPackageVersion = '1.0.0.0' - InvokeWinGetDSC -Name WinGetSource -Method Set -Property @{ Name = $testSourceName; Argument = $testSourceArg; Type = $testSourceType; TrustLevel = 'Trusted' } + InvokeWinGetDSC -Name WinGetSource -Method Set -Property @{ Name = $testSourceName; Argument = $testSourceArg; Type = $testSourceType; TrustLevel = 'Trusted'; Explicit = $false } } It 'Get WinGetPackage' {