Skip to content
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

Merged
merged 10 commits into from
Feb 9, 2023
Merged

Spec for package pinning #2611

merged 10 commits into from
Feb 9, 2023

Conversation

yao-msft
Copy link
Contributor

@yao-msft yao-msft commented Oct 20, 2022

null

Microsoft Reviewers: Open in CodeFlow

@yao-msft yao-msft requested a review from a team as a code owner October 20, 2022 01:27

- List package pinning configuration:

`winget pin list <package> [--source <source>]` for a specific package or `winget pin list` to list all
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

Copy link
Member

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.)

Copy link
Contributor Author

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:
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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`
Copy link
Contributor

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

Copy link
Member

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]`
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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)

Copy link

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 SourceIdentifiers.

Copy link
Contributor Author

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
Copy link
Contributor

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

Copy link

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),

Copy link

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

Copy link
Contributor

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

Copy link
Contributor Author

@yao-msft yao-msft Oct 21, 2022

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- 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

@jedieaston jedieaston mentioned this pull request Oct 28, 2022
2 tasks
@denelon
Copy link
Contributor

denelon commented Oct 28, 2022

We need to determine the PowerShell (approved nouns & verbs) syntax. @ryfu-msft is working on native PowerShell.

@denelon denelon mentioned this pull request Nov 28, 2022
doc/specs/#476 - Package Pinning.md Show resolved Hide resolved
doc/specs/#476 - Package Pinning.md Show resolved Hide resolved
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
Copy link
Member

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?

Copy link
Contributor

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.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: extra "to"

Suggested change
- **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]`
Copy link
Member

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).

Copy link
Contributor Author

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.

Copy link
Contributor

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 :)

Copy link
Contributor

Choose a reason for hiding this comment

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

;)

Comment on lines +27 to +30
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.
Copy link
Member

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?

@JohnMcPMS
Copy link
Member

Seeing as the actual feature is merged, I'm merging this spec PR for historical purposes.

@JohnMcPMS JohnMcPMS merged commit 5ec3dcf into microsoft:master Feb 9, 2023
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.

6 participants