microsoft / winget-cli

WinGet is the Windows Package Manager. This project includes a CLI (Command Line Interface), PowerShell modules, and a COM (Component Object Model) API (Application Programming Interface).
https://learn.microsoft.com/windows/package-manager/
MIT License
23.17k stars 1.45k forks source link

πŸ“Œ Pin a directory (exclude folder from upgrades) #2749

Open pureby opened 1 year ago

pureby commented 1 year ago

This proposal is an extension of Pin a package (#476) by @denelon.

Description of the new feature / enhancement

Users should be able to "pin" not only packages, but also directories (exclude folders from winget upgrade --all).

This can come in handy in many different scenarios, especially:


Proposed technical implementation details

The list of pinned packages and directories should be included in settings.json and should support the usage of default environment variables (%PROGRAMDATA%\scoop, $Env:SYSTEMDRIVE\MyApps, etc.) β€” making it easy to edit, export and import configuration.

When upgrade or list command is initiatied, Winget can simply check the settings file and either perform or not perform an action on certain packages by matching their name and containing directory against the list in settings.json. This can easily be achieved by an IF/ELSE expression.

PROPOSED COMMANDS - `winget pin --directory "C:\MyApps"` should exclude all applications installed into _C:\MyApps_ and all of its subfolders from `winget upgrade --all` command. - `winget unpin --directory "C:\MyApps"` should unpin that directory, making all the applications installed into _C:\MyApps_ and all of its subfolders upgradable using `winget upgrade --all` command. - `winget list --pinned` should list all pinned directories, plus packages that have explicitly been pinned using `winget pin `. It should _not_ list packages that haven't explicitly been pinned using `winget pin `. **Remarks** - **`winget [un]pin ` should have prevalence over `winget [un]pin --directory`.** Meaning that if an application has explicitly been (un)pinned using `winget [un]pin `, it's pinned state should remain unchanged by pinning or unpinning it's containing directory. - If an application installed in a pinned directory is detected by `winget list` command, a user should still be able to update that particular application using `winget upgrade ` explicitly.
EXAMPLE 1. User installs VSCode in C:\MyApps\VSCode manually (not using Winget). 2. User installs Firefox in C:\MyApps\Firefox using `winget install firefox --location "C:\MyApps\Firefox"`. 3. `winget upgrade --all` upgrades both VSCode and Firefox. 4. User pins VSCode using `winget pin vscode`. 5. User pins C:\MyApps using `winget pin --directory "C:\MyApps"`. 6. `winget list --pinned` lists: _C:\MyApps_ _Microsoft Visual Studio Code_ 9. `winget upgrade --all` upgrades neither VSCode nor Firefox. 10. User unpins C:\MyApps using `winget unpin --directory "C:\MyApps"`. 11. `winget list --pinned` lists: _Microsoft Visual Studio Code_ 13. `winget upgrade --all` upgrades only Firefox. 14. `winget upgrade vscode` upgrades VSCode.
denelon commented 1 year ago

I'm not sure how viable this is as we need a way to reason about a directory which isn't necessarily part of the list of things WinGet reasons about. It's possible it could "detect" the registry entries that indicate a known "installed" package is in a particular directory, but it seems orthogonal to "pinning a package".

This seems more like the "exclude" mechanism that would apply to packages installed in directories. Maybe it's a nice to have kind of thing, but not necessarily "critical" path for pinning.

pureby commented 1 year ago

This seems more like the "exclude" mechanism that would apply to packages installed in directories.

This is exactly what I was thinking. Winget knows the install location of packages it is listing or upgrading. The list of pinned packages and directories can be included in settings.json. When upgrade or list command is initiatied, Winget can simply check the settings file and either perform or not perform an action on certain packages by matching their package name and containing directory against the list in settings.json. I updated the OP to include this.

I'm not sure how viable this is

A simple IF MATCH do nothing ELSE upgrade statement should do the trick.

masterflitzer commented 1 year ago

after reading the whole proposal i have to say i really like this

just one thing, shouldn't it be winget [un]pin --directory (two dashes before directory)?

pureby commented 1 year ago

after reading the whole proposal i have to say i really like this just one thing, shouldn't it be winget [un]pin --directory (two dashes before directory)?

You are absolutely right, two hyphens make more sense in winget for long options, since one hypen seems to be reserved for short options. I updated the OP. Thanks for the feedback.

Viexi commented 1 year ago

Any chance this can be implemented in some fashion? I want to run "winget upgrade all" but there are some packages listed that simply must not be upgraded for compatibility purposes

denelon commented 1 year ago