Skip to content

Bring Pinning to PowerShell Cmdlets#6190

Open
Trenly wants to merge 62 commits into
microsoft:masterfrom
Trenly:PinPlus
Open

Bring Pinning to PowerShell Cmdlets#6190
Trenly wants to merge 62 commits into
microsoft:masterfrom
Trenly:PinPlus

Conversation

@Trenly

@Trenly Trenly commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Bring Pinning to PowerShell Cmdlets

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 list --details: New option that displays detailed information about pins for all packages or 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 v30)

  • 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

denelon commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator

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

@Trenly

Trenly commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

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

Copilot AI left a comment

Copy link
Copy Markdown

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 Outdated
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
Comment thread src/AppInstallerCLICore/Commands/PinCommand.cpp Outdated
Comment thread src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw Outdated
Comment thread src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw Outdated
Comment thread src/Microsoft.Management.Deployment/PackageManager.cpp Outdated
Comment thread src/Microsoft.Management.Deployment/PackageManager.cpp Outdated
Comment thread src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/AddPinCmdlet.cs Outdated

@JohnMcPMS JohnMcPMS left a comment

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 like to see the PinTable stuff be thought through more to re-use code in a way that allows the 1.0 code to stay largely untouched and 1.0 to leverage it without anything over.

Comment thread .github/copilot-instructions.md Outdated
Comment thread .github/copilot-instructions.md Outdated
Comment thread src/AppInstallerCLITests/PinningIndex.cpp Outdated
Comment thread src/AppInstallerCLITests/SQLiteWrapper.cpp Outdated
Comment thread src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.h Outdated
Comment thread src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_1/PinTable_1_1.cpp Outdated
Comment thread src/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp Outdated
/// </summary>
protected override void ProcessRecord()
{
if (this.Blocking && !string.IsNullOrEmpty(this.GatedVersion))

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.

Can we do this through parameter sets? Not sure if it is more complicated or by how much, but it would better integrate with PS (at least I assume that once it has decided which set you are using on the shell it won't offer tab-suggestions from the other sets).

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 took a look and didn't see an easy way to do that using parameter sets. I'll keep digging though

Comment thread src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/FinderPackageCommand.cs Outdated
@Trenly

Trenly commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

Will address the comments in a few days - been super busy;

@JohnMcPMS - Assuming this will go in vNext, does that mean I should then be bumping to the next contract version?

@JohnMcPMS

Copy link
Copy Markdown
Member

Will address the comments in a few days - been super busy;

@JohnMcPMS - Assuming this will go in vNext, does that mean I should then be bumping to the next contract version?

Yes, it should move to 30 as we are branching the 1.29 RC imminently.

@Trenly Trenly requested a review from Copilot June 15, 2026 19:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@Trenly Trenly requested a review from Copilot June 15, 2026 19:42

Copilot AI left a comment

Copy link
Copy Markdown

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 80 out of 80 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (6)

src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/RemovePinCmdlet.cs:1

  • Remove-WinGetPin's PinSet parameter set ignores the piped PSPackagePin.SourceId and IsForInstalledPackage, and only uses PackageId to re-resolve a CatalogPackage. This can (a) throw VagueCriteriaException if the same Id exists in multiple sources, and (b) fail entirely for installed-package pins where PackageId is a ProductCode/PFN rather than an available package Id. Fix by using the pin object to disambiguate resolution: when SourceId is present, scope the search to that source; when IsForInstalledPackage is true, resolve via the installed package identity path (or change the engine command to unpin by pin key rather than re-searching). Add a test that pipes a pin from Get-WinGetPin where the package exists in multiple sources and/or covers installed-pin removal.
    src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PSEnumHelpers.cs:1
  • The default branch throws InvalidOperationException() without a message, which makes user-facing failures (and debugging) opaque when an invalid pin type string flows in. Prefer an exception with a clear message (e.g., ArgumentOutOfRangeException/InvalidOperationException including the invalid value and accepted values).
    src/PowerShell/tests/Microsoft.WinGet.Client.Tests.ps1:1
  • RemoveTestSource is executed both in the new Describe's AfterAll and again in the file-level AfterAll. If RemoveTestSource is not idempotent, this can make the test suite flaky (second removal fails) and also makes cleanup ordering harder to reason about. Consider removing the nested RemoveTestSource or restructuring so the test source is added/removed in only one place for this file.
    src/PowerShell/tests/Microsoft.WinGet.Client.Tests.ps1:1
  • RemoveTestSource is executed both in the new Describe's AfterAll and again in the file-level AfterAll. If RemoveTestSource is not idempotent, this can make the test suite flaky (second removal fails) and also makes cleanup ordering harder to reason about. Consider removing the nested RemoveTestSource or restructuring so the test source is added/removed in only one place for this file.
    src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/PinPackageCommand.cs:1
  • This uses async/await and Task.FromResult(...) around a synchronous operation, which creates unnecessary async state machines and makes the flow harder to read. If GetPackageAndExecuteAsync expects a Task<T>, return Task.FromResult(PackageManagerWrapper.Instance.PinPackage(...)) directly (non-async lambda), or if it supports sync delegates, call the sync operation directly.
    src/Microsoft.Management.Deployment/PackageManager.cpp:1
  • GetPins() performs one DB lookup per derived PinKey (pinningData.GetPin(pinKey)), which can become a multi-query pattern for packages with many available versions and/or multiple installed identities. Consider fetching all pins once (GetAllPins) and filtering in-memory by key (e.g., build a hash set of requested keys) or adding a bulk-get API in PinningData/index layer to reduce DB round-trips.

Comment thread doc/ReleaseNotes.md
@Trenly Trenly requested a review from JohnMcPMS June 15, 2026 19:46
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