Bring Pinning to PowerShell Cmdlets#6190
Conversation
|
Were you able to figure out the PlatyPS MAML stuff for the PowerShell help? |
|
There was a problem hiding this comment.
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
DateAddedandNotesupport 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. |
There was a problem hiding this comment.
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'sPinSetparameter set ignores the pipedPSPackagePin.SourceIdandIsForInstalledPackage, and only usesPackageIdto re-resolve aCatalogPackage. This can (a) throwVagueCriteriaExceptionif the same Id exists in multiple sources, and (b) fail entirely for installed-package pins wherePackageIdis a ProductCode/PFN rather than an available package Id. Fix by using the pin object to disambiguate resolution: whenSourceIdis present, scope the search to that source; whenIsForInstalledPackageis 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 fromGet-WinGetPinwhere 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/InvalidOperationExceptionincluding the invalid value and accepted values).
src/PowerShell/tests/Microsoft.WinGet.Client.Tests.ps1:1 RemoveTestSourceis executed both in the newDescribe'sAfterAlland again in the file-levelAfterAll. IfRemoveTestSourceis not idempotent, this can make the test suite flaky (second removal fails) and also makes cleanup ordering harder to reason about. Consider removing the nestedRemoveTestSourceor restructuring so the test source is added/removed in only one place for this file.
src/PowerShell/tests/Microsoft.WinGet.Client.Tests.ps1:1RemoveTestSourceis executed both in the newDescribe'sAfterAlland again in the file-levelAfterAll. IfRemoveTestSourceis not idempotent, this can make the test suite flaky (second removal fails) and also makes cleanup ordering harder to reason about. Consider removing the nestedRemoveTestSourceor 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/awaitandTask.FromResult(...)around a synchronous operation, which creates unnecessary async state machines and makes the flow harder to read. IfGetPackageAndExecuteAsyncexpects aTask<T>, returnTask.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 derivedPinKey(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 inPinningData/index layer to reduce DB round-trips.
- Remove detailed IDL interface definitions (too implementation-level for a spec; keep high-level description of needed COM surface) - Use PinType property instead of IsBlocking for future expandability (supports checking gating pins too) - Add PR microsoft#6190 (Configuration module improvements) to Resources Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
JohnMcPMS
left a comment
There was a problem hiding this comment.
Just a note for the future, please build things in layers 🥲 :
- CLI
- COM
- PowerShell
That will help with faster reviews and iteration when we can focus on each part.
| addDateAdded.Execute(connection); | ||
|
|
||
| StatementBuilder addNote; | ||
| addNote.AlterTable(s_PinTable_Table_Name).Add(s_PinTable_Note_Column, Type::Text); |
There was a problem hiding this comment.
Can't you add multiple columns at once?
There was a problem hiding this comment.
I'm not seeing any examples in the codebase where that's done today - perhaps I'm just missing it?
| Pinning_V1_0::PinningIndexInterface base; | ||
| SQLite::rowid_t pinId = base.AddPin(connection, pin); | ||
| THROW_HR_IF(E_UNEXPECTED, !Pinning_V1_1::PinTable::UpdatePinById(connection, pinId, pin)); |
There was a problem hiding this comment.
This feels like doing things twice, and I don't like the secondary protected interface design here.
This code invokes the base add, which in turn invokes its protected interface add, which in turn invokes our 1.1 add, which should just do the right thing. Then it updates the pin, seemingly with no changes since our 1.1 add should have done the right thing.
The protected interface portions that we use in other schema design is to allow smaller portions of the overall action to be changed. This change is using that to replace the entire action portion, leaving only some bookkeeping happening in the primary function.
I would revert the protected interface portions in favor of:
- 1.1 invokes the 1.0 function
- 1.0 function returns value(s) needed by the 1.1 function to make further updates
- 1.1 function applies its updates
For step 2 to work, you may have to move the actual body of the interface implementation into a protected, non-virtual method that returns a non-interface result.
For the read-only methods, you can probably get away with deferring to the table without a need to invoke the base. The table might invoke its base/shared static that takes in a column list so that at least some amount of code can be reused.
There was a problem hiding this comment.
I'm not fully understanding - Are you saying to defer all the add/get/update to the tables? So then something like UpdatePin in 1_1 would call 1_0 implementation of UpdatePin() to update the base pin information, the 1)0 implementation would return the row key that 1_1 needs, and then 1_1 would update just the new fields?
Apprecate the advice and will do for the future (And thank you for your patience with me and my new friend 🤖 ) |
* Don't fake a name search
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--noteargument 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)
DateAddedandNotecolumns 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.PackagePinruntime class exposingPackageId,SourceId,Type,GatedVersion,DateAdded,Note, andIsForInstalledPackage.PinPackageOptionsandPinPackageResultruntime classes.PackagePinTypeenum (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— newIsPinnedproperty on returned objects.cc @denelon for Naming
Microsoft Reviewers: Open in CodeFlow