Skip to content

Bring Pinning to PowerShell Cmdlets#6190

Open
Trenly wants to merge 46 commits intomicrosoft:masterfrom
Trenly:PinPlus
Open

Bring Pinning to PowerShell Cmdlets#6190
Trenly wants to merge 46 commits intomicrosoft:masterfrom
Trenly:PinPlus

Conversation

@Trenly
Copy link
Copy Markdown
Contributor

@Trenly Trenly commented Apr 29, 2026

Summary

This PR extends the WinGet pinning system with new metadata, a new CLI subcommand, expanded COM API surface, and PowerShell cmdlets for managing pins.

CLI Changes

  • winget pin add: Added --note argument to attach an optional freeform note to a pin. The timestamp when a pin is created is now recorded automatically.
  • winget pin show: New subcommand that displays detailed information about pins for a specific package (query by ID, name, or keyword).

Pinning Index Schema (v1.1)

  • New schema version adds DateAdded and Note columns to the pin table to persist the new metadata.

COM API (Microsoft.Management.Deployment, contract v29)

  • PackageManager.GetAllPins() — retrieve all pins across all sources.
  • PackageManager.GetPins(CatalogPackage) — retrieve pins for a specific package.
  • PackageManager.PinPackage(CatalogPackage, PinPackageOptions) — add or update a pin.
  • PackageManager.UnpinPackage(CatalogPackage) — remove all pins for a package.
  • PackageManager.ResetAllPins(String sourceName) — reset all pins, optionally scoped to a source.
  • New PackagePin runtime class exposing PackageId, SourceId, Type, GatedVersion, DateAdded, Note, and IsForInstalledPackage.
  • New PinPackageOptions and PinPackageResult runtime classes.
  • New PackagePinType enum (PinnedByManifest, Pinning, Gating, Blocking).

PowerShell (Microsoft.WinGet.Client)

  • Add-WinGetPin — pin a package with a specified pin type, optional gated version, note, and force flag.
  • Get-WinGetPin — list pins, filterable by package/source.
  • Remove-WinGetPin — remove pins for a package.
  • Reset-WinGetPin — reset all pins, optionally scoped to a source.
  • Get-WinGetPackage — new IsPinned property on returned objects.

cc @denelon for Naming


Microsoft Reviewers: Open in CodeFlow

@Trenly Trenly marked this pull request as ready for review April 29, 2026 20:22
@Trenly Trenly requested a review from a team as a code owner April 29, 2026 20:22
@denelon
Copy link
Copy Markdown
Collaborator

denelon commented Apr 29, 2026

Were you able to figure out the PlatyPS MAML stuff for the PowerShell help?

@Trenly
Copy link
Copy Markdown
Contributor Author

Trenly commented Apr 29, 2026

Were you able to figure out the PlatyPS MAML stuff for the PowerShell help?

Get-Help seems to work the same way for the new cmdlets as it does for the others. I might look at improving the PowerShell help separately in the future. This PR is already bigger than I'm sure John might like it to be, don't want to make anyone too frazzled

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands WinGet’s pinning feature end-to-end (repository schema + CLI workflow + COM/WinRT API + PowerShell cmdlets), adding pin metadata (DateAdded, Note), a new winget pin show command, and PowerShell cmdlets to manage pins.

Changes:

  • Add DateAdded and Note support to the pinning index (schema v1.1) with migration from v1.0.
  • Extend the COM API surface for pin management (get/pin/unpin/reset) and expose PackagePin, PinPackageOptions, PinPackageResult.
  • Add PowerShell cmdlets (Get/Add/Remove/Reset-WinGetPin) and related PS output objects/help.

Reviewed changes

Copilot reviewed 59 out of 59 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/PowerShell/Microsoft.WinGet.Client/ModuleFiles/Microsoft.WinGet.Client.psd1 Exports new pin cmdlets from the module.
src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSPinResult.cs Adds PS wrapper for COM pin operation results.
src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSPackagePin.cs Adds PS wrapper for COM PackagePin objects.
src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSInstalledCatalogPackage.cs Adds IsPinned property on installed package output objects.
src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PackageManagerWrapper.cs Adds wrapper methods for new COM pin APIs.
src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PSEnumHelpers.cs Adds conversion helper for PackagePinType.
src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/ManagementDeploymentFactory.cs Adds COM factory creation for PinPackageOptions.
src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/ResetPinCommand.cs Adds engine command to reset pins via COM.
src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/PinPackageCommand.cs Adds engine command to get/add/remove pins via COM.
src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Common/Constants.cs Adds parameter sets and noun constant for pin cmdlets.
src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/ResetPinCmdlet.cs Adds Reset-WinGetPin cmdlet.
src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/RemovePinCmdlet.cs Adds Remove-WinGetPin cmdlet (supports pipeline pin input).
src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSPackagePinType.cs Adds PowerShell-facing enum for pin types.
src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/GetPinCmdlet.cs Adds Get-WinGetPin cmdlet.
src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/AddPinCmdlet.cs Adds Add-WinGetPin cmdlet (type/gatedVersion/note/force).
src/PowerShell/Help/Microsoft.WinGet.Client/Reset-WinGetPin.md Adds help documentation for reset pin cmdlet.
src/PowerShell/Help/Microsoft.WinGet.Client/Remove-WinGetPin.md Adds help documentation for remove pin cmdlet.
src/PowerShell/Help/Microsoft.WinGet.Client/Microsoft.WinGet.Client.md Adds module index entries for new cmdlets.
src/PowerShell/Help/Microsoft.WinGet.Client/Get-WinGetPin.md Adds help documentation for get pin cmdlet.
src/PowerShell/Help/Microsoft.WinGet.Client/Add-WinGetPin.md Adds help documentation for add pin cmdlet.
src/Microsoft.Management.Deployment/Public/ComClsids.h Adds CLSIDs for PinPackageOptions activation.
src/Microsoft.Management.Deployment/PinPackageResult.h Introduces WinRT result type for pin operations.
src/Microsoft.Management.Deployment/PinPackageResult.cpp Implements WinRT result type for pin operations.
src/Microsoft.Management.Deployment/PinPackageOptions.h Introduces WinRT options type for pin operations.
src/Microsoft.Management.Deployment/PinPackageOptions.cpp Implements WinRT options type for pin operations.
src/Microsoft.Management.Deployment/PackagePin.h Introduces WinRT PackagePin runtimeclass.
src/Microsoft.Management.Deployment/PackagePin.cpp Implements WinRT PackagePin object creation from internal pins.
src/Microsoft.Management.Deployment/PackageManager.idl Adds pinning APIs + types to the public WinRT contract.
src/Microsoft.Management.Deployment/PackageManager.h Declares new PackageManager pinning methods.
src/Microsoft.Management.Deployment/PackageManager.cpp Implements GetPins/GetAllPins/Pin/Unpin/Reset via pinning data store.
src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj Adds new pin-related source/header files to project build.
src/Microsoft.Management.Deployment/Converters.h Declares GetPinOperationStatus mapping.
src/Microsoft.Management.Deployment/Converters.cpp Implements HRESULT→PinResultStatus mapping.
src/Microsoft.Management.Deployment/ComClsids.cpp Enables in-proc→out-of-proc redirection for PinPackageOptions.
src/Microsoft.Management.Deployment.Projection/WinGetProjectionFactory.cs Adds projection factory method for PinPackageOptions.
src/Microsoft.Management.Deployment.Projection/ClassesDefinition.cs Registers PinPackageOptions class for projection activation.
src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_1/PinningIndexInterface_1_1.cpp Adds v1.1 pinning schema interface + migration path.
src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_1/PinningIndexInterface.h Declares v1.1 schema interface.
src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_1/PinTable.h Declares v1.1 pin table with new columns.
src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_1/PinTable.cpp Implements v1.1 pin table CRUD/migration including note/date.
src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface_1_0.cpp Implements MigrateFrom for v1.0 (not supported).
src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface.h Adds MigrateFrom to v1.0 interface declaration.
src/AppInstallerRepositoryCore/Microsoft/Schema/IPinningIndex.h Adds MigrateFrom to schema interface abstraction.
src/AppInstallerRepositoryCore/Microsoft/PinningIndex.h Adds helper to create schema interface for a specific version.
src/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp Implements migration-on-open for pinning index and version dispatch.
src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj Adds v1.1 pinning schema files to repository core build.
src/AppInstallerCommonCore/Public/winget/Pin.h Adds DateAdded + Note fields to internal Pin model.
src/AppInstallerCLITests/PinningIndex.cpp Adds unit tests for v1.1 schema and migration behavior.
src/AppInstallerCLITests/PinFlow.cpp Adds workflow tests for note/date and new pin show behavior.
src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw Adds localized strings for --note and pin show output.
src/AppInstallerCLIPackage/Package.appxmanifest Registers COM server class for PinPackageOptions (dev out-of-proc).
src/AppInstallerCLICore/Workflows/PinFlow.h Declares new workflow step ShowPinDetails.
src/AppInstallerCLICore/Workflows/PinFlow.cpp Sets date/note on add; implements winget pin show output filtering.
src/AppInstallerCLICore/Resources.h Adds resource IDs for new pin strings.
src/AppInstallerCLICore/ExecutionArgs.h Adds PinNote execution arg.
src/AppInstallerCLICore/Commands/PinCommand.h Adds PinShowCommand declaration.
src/AppInstallerCLICore/Commands/PinCommand.cpp Wires up winget pin show and adds --note to pin add.
src/AppInstallerCLICore/Argument.cpp Adds CLI argument mapping for --note.
.github/actions/spelling/expect.txt Adds expected spelling words introduced by new tests/log strings.

Comment thread src/Microsoft.Management.Deployment/PackageManager.idl
Comment thread src/Microsoft.Management.Deployment/PackageManager.idl Outdated
Comment thread src/Microsoft.Management.Deployment/PackageManager.cpp Outdated
Comment thread src/AppInstallerCLICore/Workflows/PinFlow.cpp Outdated
Comment thread src/AppInstallerCLICore/Workflows/PinFlow.cpp Outdated
std::make_unique<PinRemoveCommand>(FullName()),
std::make_unique<PinListCommand>(FullName()),
std::make_unique<PinResetCommand>(FullName()),
std::make_unique<PinShowCommand>(FullName()),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that we follow the model of package list and have --details on the existing list command. That also removes the need to create a whole new search mechanism and just makes the change in ReportPins.

Followup: Well it looks like it would need a whole new search mechanism to actually implement the arguments that it claims to support...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we would need a whole new search mechanism anyways. I do think that there is some overlap with winget list <query> --details, but to me it felt more akin to winget show <query>. The semantics of list vs show as a base command don't really apply to pins. Where show in the base command is for a remote package / manifest information, list is for installed applications.

I will think on this and probably end up refactoring to extend the behavior of list instead of adding show

Comment thread src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw Outdated
Comment thread src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
auto newPin = CreatePinFromOptions(pinKey, options);

auto existingPin = pinningData.GetPin(pinKey);
if (existingPin && !(*existingPin == newPin))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this only be comparing the pin type rather than the entire pin?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The CLI compares the version as well for gating pins so that updating a gating pin by overwriting it requires --force . e.g winget pin add Microsoft.WingetCreate --version 1.9 and then running winget pin add Microsoft.WingetCreate --version 1.10 would give an error that there is already a pin and it needs to be overriden.

This matches the behavior of the CLI

Comment thread src/Microsoft.Management.Deployment/PackageManager.cpp Outdated
/// <summary>
/// Must match the user-settable values of Microsoft.Management.Deployment.PackagePinType.
/// </summary>
public enum PSPackagePinType
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the "pinned by the manifest" type?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe we do, since it isn't user-settable and the CLI doesn't expose it when listing pins as far as I can tell

Comment thread src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/AddPinCmdlet.cs Outdated
@github-actions

This comment has been minimized.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 78 out of 78 changed files in this pull request and generated 8 comments.

bool hasName = context.Args.Contains(Execution::Args::Type::Name);
bool hasQuery = context.Args.Contains(Execution::Args::Type::Query);
bool exactMatch = context.Args.Contains(Execution::Args::Type::Exact);

Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

winget pin show currently allows running with no query/id/name arguments, in which case it will match every pin and dump verbose details for all of them. This seems inconsistent with the intent of a "show" command (and the comment above suggests at least one filter is expected). Consider enforcing that at least one of --id/--name/--query is provided and terminating with an appropriate error message when none are specified.

Suggested change
if (!hasId && !hasName && !hasQuery)
{
context.Reporter.Error() << "The 'pin show' command requires at least one of --id, --name, or --query." << std::endl;
AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS);
}

Copilot uses AI. Check for mistakes.
Comment thread src/Microsoft.Management.Deployment/PackageManager.cpp
Comment thread src/Microsoft.Management.Deployment/PackageManager.idl
Comment thread .github/copilot-instructions.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants