-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spec for package pinning #2611
Spec for package pinning #2611
Conversation
|
||
- List package pinning configuration: | ||
|
||
`winget pin list <package> [--source <source>]` for a specific package or `winget pin list` to list all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might also suggest winget pin remove --all --force
or winget pin reset --force
to fully reset the pinning state, similar to a source reset, where the force argument would be required
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add winget pin reset --force
A new `winget pin` command with 3 sub-commands will be introduced. | ||
- Add package pinning configuration: | ||
|
||
`winget pin add <package> [--version <optional gated version>] [--source <source>] [--force] [--blocking]` |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, they'll be same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that also go for the package query arguments this will use? So, having --query
, --id
, --name
, --moniker
, --tag
, --command
? (I'm already excluding --manifest
as that is not linked to a source.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, any args for finding a package and applicable for search a package to pin.
Gate version `1.0.*` matches Version `1.0.*` | ||
Gate version `1.0.*` does not match Version `1.1.1` | ||
|
||
In rare cases where `*` is actually part of a version, only the last `.*` is considered wild card: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How likely is this case to truly exist? A quick check of winget-pkgs shows that no packages there currently have a version which includes *
. I fear this may be limiting functionality for an edge case that is almost non-existent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unlikely, I just wanted to clarify that for any gate version, only the .*
in the end is considered. If there is a need to match1.*.*
, user should probably use 1.*
instead. This is just for simpler logic for parsing and matching gate version, to be less error-prone.
But if there's a need to match gate version like 1.*.2
where 1.1.2
matches and 1.0.3
does not match, I can make a version part with only *
as a wild card, that's also fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that it really matters too much. Thank you for the explanation on the reasoning!
Gate version `1.*.*` does not match Version `1.1.1` | ||
|
||
If no `.*` in the end is detected, the gate version gates to the specific version: | ||
Gate version `1.0.1` matches Version `1.0.1` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarification would be useful around 1.0.1a
or 1.0.1b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also things like gate version 1.0.1
(no .*
) and version 1.0.1.2
.
A new `winget pin` command with 3 sub-commands will be introduced. | ||
- Add package pinning configuration: | ||
|
||
`winget pin add <package> [--version <optional gated version>] [--source <source>] [--force] [--blocking]` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you clarify the behavior when a pin already exists for a package? Will a user have to remove the existing pin and re-pin? A winget pin update
command? Or is this what the --force
parameter is for, so that a warning can be displayed that existing pins would be overwritten and they must force the addition to override it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I will add more clarification and example for it.
Basically, if a pin configuration already exists, we'll show a warning and exit. User will remove the pin and add a new one, or use --force
to override
PackageIdentifier SourceIdentifier Version PinningType | ||
---------------------------------------------------------------------------- | ||
Microsoft.TestApp winget Blocking | ||
Microsoft.TestAppStore msstore Blocking |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Users might not be able to pin packages from the msstore
source unless, as @JohnMcPMS pointed out, it is made very clear that pinning is not a guarantee the package won't be upgraded outside of winget, it's just saying that it won't be upgraded as part of winget upgrade
. Either documentation around this needs to be considered, or provisions should be made to not allow pinning of packages installed from msstore.
With the traditional Microsoft Store packages, the Microsoft Store automatically updates programs. Users would not be able to pin those packages. I need to see if there is a way to pin packages from being automatically updated by the Microsoft Store.
Originally posted by @denelon in #1894 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the pins are recorded for each source present at the time of adding the pin then if a user later adds another source the package would be upgraded from there right? I think from a UX perspective if I ran winget pin add <package> --blocking
I would expect the package to be pinned indefinitely for all current and future sources. So I would prefer if pinning a package without explicitly listing individual sources would result in a different, bool SQLite column being set like AllSources
rather than multiple entries with arbitrary SourceIdentifier
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re @Trenly , all these pin configurations only apply to winget behaviors, we cannot control upgrades outside winget. Blocking simply blocks winget from doing anything to the blocked package. In the original issue, some community member wanted winget to block upgrade for some specific package, because they want to perform upgrades outside winget through other channel.
Re @jantari , yes, it would be ideal to pin an installed package for all future sources. Due to current limitation on the package identifier generation for installed packages, pinning from installed source is put into future considerations. Currently, a package identifier for an installed package is its produce code, if user upgrade the package, the pin is no longer in effect since most likely product code will change after an upgrade. Regarding AllSources
in sqlite, it's trivial to record it, but we don't have efficient cross reference for remote sources yet, so it's unfortunately moved as future considerations.
|
||
**Notes:** For this iteration, winget will only support pinning packages that are locally installed and correlatable with at least one of the remote sources. Winget will record a pinned package by the PackageIdentifier and SourceIdentifier. There can only be one pinning configuration for a specific package. In the future, winget may consider pinning packages from installed packages (upon improving the installed package's PackageIdentifier logic). | ||
|
||
## UI/UX Design |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the powershell cmdlets considered here? I'm assuming the commands will be something like Add-WingetPackagePin
and Remove-WingetPackagePin
, but didn't see those in here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The verbs Add
and Remove
are usually used for resources that support having multiple things added to them, such as with Add-Content
appending to a file or Add-ADGroupMember
adding one or more members to a group which may countain N amount of members total.
Since a package would probably only ever have one pin state (yes with options or no) at a time, I would maybe suggest the verbs Suspend
/Resume
(updates), Get
/Set
/Reset
(a pin) or Lock
/Unlock
(a version),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok the:
There can only be one pinning configuration for a specific package.
threw me off here, I can see from further down the document now that a package can have multiple co-existing pins for different sources. That makes sense, so Get
/Add
/Remove
/Reset
(or Remove without parameters = Reset) makes sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking along the lines that each pin is an item in a collection of pins (even if the package identifier is different, they are still part of the collection), so something like Get-WingetPackagePin
would show all pins (Similar to how Get-AppxPackage
shows all AppxPackages installed) since Add
and Remove
are referring to the collection of pins, not the individual package pin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Powershell support is not considered in this spec yet :(. I'll add it to future list and we'll probably get back to it after core feature is done.
edit: winget team is moving Powershell cmdlets to call Com Apis to perform winget operations, that means Com Api needs to support the pinning feature first before Powershell support could be enabled. More work is needed..
- A couple UI integrations can be made to `winget upgrade` and `winget list` to show pinned status during listing. | ||
- Dependencies flow can be improved to first check pinned status of each dependant package before trying to install all dependencies. | ||
- Support setting pinned state right after installation/upgrades like `winget install foo --pin`. | ||
- Improvements to import export commands to work seamlessly with existing package pinning configurations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Improvements to import export commands to work seamlessly with existing package pinning configurations. | |
- Improvements to import export commands to work seamlessly with existing package pinning configurations. | |
- Pinning multiple versions of a single package installed side by side |
|
||
- Installation/Upgrades from Com Apis may be impacted by user's package pinning configuration. It could be mitigated by returning a specific error code and the caller retrying with Force option. | ||
- Package dependencies resolution may be impacted by user's package pinning configuration. | ||
- Package imports may be impacted by user's package pinning configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Package imports may be impacted by user's package pinning configuration. | |
- Package imports may be impacted by user's package pinning configuration. | |
- Package exports may not consider user's package pinning configuration |
We need to determine the PowerShell (approved nouns & verbs) syntax. @ryfu-msft is working on native PowerShell. |
Success // If the available versions for upgrade are: 1.2.3 and 1.3.0, the selected version for upgrade is 1.2.3 | ||
|
||
cmd> winget upgrade Microsoft.TestApp | ||
Success // If the available versions for upgrade are: 1.2.3 and 1.3.0, the selected version for upgrade is 1.2.3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the package gets updated to 1.3.0 outside of winget? Do we show a warning, offer a way to re-install the pinned version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've commented about that situation before. I think it would be good for WinGet to show when a "pin" has been violated in cases other than "blocking". I think we would also want to make sure that if a user applied "--force" or some other formal override, that the pin is either removed or ideally updated and the user is informed.
#### Package Pinning types | ||
To achieve goals listed above, winget will support 3 types of Package Pinning: | ||
- **Blocking:** The package is blocked from `winget upgrade --all` or `winget upgrade <specific package>`, user has to unblock the package to let winget perform upgrade. | ||
- **Pinning:** The package is excluded from `winget upgrade --all` but allowed in `winget upgrade <specific package>`, a new argument `--include-pinned` will be introduced to let `winget upgrade --all` to include pinned packages. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: extra "to"
- **Pinning:** The package is excluded from `winget upgrade --all` but allowed in `winget upgrade <specific package>`, a new argument `--include-pinned` will be introduced to let `winget upgrade --all` to include pinned packages. | |
- **Pinning:** The package is excluded from `winget upgrade --all` but allowed in `winget upgrade <specific package>`, a new argument `--include-pinned` will be introduced to let `winget upgrade --all` include pinned packages. |
|
||
- Remove package pinning configuration: | ||
|
||
`winget pin remove <package> [--source <source>] [--force]` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to add aliases for the subcommands? I see we are using source rm
for source remove
, rm
for uninstall
, ls
for list
. Personally, I'd like to have consistency and add pin rm
, pin ls
(and source ls
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, consistency is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, and I bet @Trenly would agree :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
;)
To achieve goals listed above, winget will support 3 types of Package Pinning: | ||
- **Blocking:** The package is blocked from `winget upgrade --all` or `winget upgrade <specific package>`, user has to unblock the package to let winget perform upgrade. | ||
- **Pinning:** The package is excluded from `winget upgrade --all` but allowed in `winget upgrade <specific package>`, a new argument `--include-pinned` will be introduced to let `winget upgrade --all` to include pinned packages. | ||
- **Gating:** The package is pinned to specific version(s). For example, if a package is pinned to version `1.2.*`, any version between `1.2.0` to `1.2.<anything>` is considered valid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to define how each type of pin interacts with RequireExplicitUpgrade
. I'm guessing something like this:
- Pinning pins and
RequireExplicitUpgrade
behave the same way. Even though they do the same things, we allow pinning in case the manifest gets updated to remove the field or the package updates to a version without that field. - Blocking pins override
RequireExplicitUpgrade
, so we won't update even if the include flag is added. We have to both remove the blocking pin and pass the flag when updating. - Gating pins block from updating beyond the gated version, even with the special flag for
RequireExplicitUpgrade
, and updates within that version range still require the special flag.
Does that sound good?
Seeing as the actual feature is merged, I'm merging this spec PR for historical purposes. |
null
Microsoft Reviewers: Open in CodeFlow