winget-cli icon indicating copy to clipboard operation
winget-cli copied to clipboard

📌 Pin a directory (exclude folder from upgrades)

Open pureby opened this issue 2 years ago • 6 comments

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:

  • Preventing conflicts between multiple package managers (Scoop, Chocolatey, etc). By pinning C:\choco or %PROGRAMDATA%\scoop winget upgrades will be disabled automagically for software installed through other means.

  • Preventing accidental upgrades within development environments. By pinning C:\Dev developers can install their tools safely and not worry about accidental upgrades from winget. Developers can even install multiple versions of same program using winget itself, without conflicts!

  • Excluding entire game drives. Games are installed and maintained through self-updating launchers (Steam, Epic, etc.). There are many reasons why users like to keep their game libraries, mods and launchers separate from the rest of their system. Those softwares are often installed on a dedicated game drive, physically separate from the rest of the system. Pinning a game drive, e.g. E:\ will automatically exclude all games and related software from unwanted updates and potential conflicts, including all future installs.


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 <package>. It should not list packages that haven't explicitly been pinned using winget pin <package>.

Remarks

  • winget [un]pin <package> should have prevalence over winget [un]pin --directory. Meaning that if an application has explicitly been (un)pinned using winget [un]pin <package>, 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 <package> 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
  7. winget upgrade --all upgrades neither VSCode nor Firefox.
  8. User unpins C:\MyApps using winget unpin --directory "C:\MyApps".
  9. winget list --pinned lists: Microsoft Visual Studio Code
  10. winget upgrade --all upgrades only Firefox.
  11. winget upgrade vscode upgrades VSCode.

pureby avatar Dec 06 '22 14:12 pureby

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.

denelon avatar Dec 06 '22 20:12 denelon

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.

pureby avatar Dec 09 '22 11:12 pureby

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

masterflitzer avatar Dec 09 '22 14:12 masterflitzer

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.

pureby avatar Dec 10 '22 11:12 pureby

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

Viexi avatar Dec 28 '22 08:12 Viexi

  • #476 is in progress.

denelon avatar Dec 28 '22 18:12 denelon