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.01k stars 1.43k forks source link

Exclude "remove only" installers from `upgrade --all`, by default. #3130

Open JohnLukeBentley opened 1 year ago

JohnLukeBentley commented 1 year ago

Description of the new feature / enhancement

In rare cases software will provide a "remove only" "installer". E.g. see Speedfan below ...

PS ...Sda\Code> winget upgrade --include-unknown --all
Name                             Id                     Version Available Source
--------------------------------------------------------------------------------
IrfanView 4.60 (32-bit)          IrfanSkiljan.IrfanView 4.60    4.62      winget
SpeedFan (remove only)           Almico.SpeedFan        Unknown 4.52      winget
WinDirStat 1.1.2                 WinDirStat.WinDirStat  Unknown 1.1.2     winget
Microsoft .NET SDK 6.0.310 (x64) Microsoft.DotNet.SDK.6 6.0.310 6.0.407   winget
5 upgrades available.

... I suggest these, by default, should be separately listed above and not included for execution in an --all upgrade.

The new package pinning feature makes working around this problem easy ...

PS ...Sda\Code> winget pin add Speedfan
Found SpeedFan [Almico.SpeedFan]
Pin added successfully

... however, that is a workaround.

Proposed technical implementation details

No response

Trenly commented 1 year ago

"Remove only" isn't about the installer, it's about how the Control Panel can interact with the program. There are 2 different actions installers can register to the Apps And Features Entries - Remove, Repair, and Change. The "Remove only" indicates that the uninstaller for the program can't be used to repair or change the original installation, only to remove it. Note that this isn't an automatic text string, but has been placed there specifically by the publisher and is an anti-pattern. Because WinGet gets the information on installed programs from control panel, that is why the "Remove only" shows up in WinGet.

However, downloading the installer from the publisher's website and running it will work to upgrade the software in a majority of cases. Since WinGet only uses the built in uninstaller for uninstalling, this is a non-issue and I would argue that pinning is not a workaround but is the intended solution for users who do not wish to upgrade certain packages

JohnLukeBentley commented 1 year ago

Thanks for that detail Trenly.

Given

WinGet gets the information on installed programs from control panel, that is why the "Remove only" shows up in WinGet.

... and this "remove only" capability is not some random string (e.g. in the app name), it seems the door is wide open for Winget to handle "remove only" installers specially.

On the issue of whether we should walk through the door ...

Since WinGet only uses the built in uninstaller for uninstalling, this is a non-issue

Are you implying that if we attempt to use winget to upgrade the "remove only" uninstaller it will download any new version and not executed it? That seems consistent with my tests.

PS ...Sda\Code> winget upgrade speedfan --include-unknown
Found SpeedFan [Almico.SpeedFan] Version 4.52
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Successfully verified installer hash
Starting package install...
Installer failed with exit code: 2

(Although that I've had to use --include-unknown makes it an impure test).

Anyway if "remove only" uninstallers will be upgraded by winget, but not execute, that's:

If winget already preforms this special handling, that counts in favour of communicating this in upgrade -all output, I'd suggest.

As an example format

PS ...Sda\Code> winget upgrade --all
Name                             Id                     Version Available Source
--------------------------------------------------------------------------------
IrfanView 4.60 (32-bit)          IrfanSkiljan.IrfanView 4.60    4.62      winget
WinDirStat 1.1.2                 WinDirStat.WinDirStat  Unknown 1.1.2     winget
Microsoft .NET SDK 6.0.310 (x64) Microsoft.DotNet.SDK.6 6.0.310 6.0.407   winget
3 upgrades available.

Name                             Id                     Version Available Source
--------------------------------------------------------------------------------
SpeedFan (remove only)           Almico.SpeedFan        Unknown 4.52      winget
1 remove only upgrade available. The remove only uninstallers will be upgraded but not ran.

Remove only uninstallers may well be an anti-pattern (that seems right to me, it does seem like an anti-pattern). But it's an anti-pattern that exists. And so it seems worth it for winget to make clear how it's going to handle these.

Trenly commented 1 year ago

Are you implying that if we attempt to use winget to upgrade the "remove only" uninstaller it will download any new version and not executed it? That seems consistent with my tests.

No, that's not what I'm implying. I believe that the specific error "2" from your test is a different issue entirely

Anyway if "remove only" uninstallers will be upgraded by winget, but not execute, that's:

  • what we want to happen during an upgrade -all (or even a targeted upgrade); but
  • that is at odds with what winget does with normal installers. Winget will upgrade the installer and executed it.

I don't think it's at odds at all. The installer and uninstaller are 2 different files. When you run an installer, the installer unpacks all of the items needed for the program. This includes the executables to run the program, any DLLs, images, or other assets needed by the program, and the uninstaller. The installer then writes the entry to control panel through the registry.

Presume for a moment that it is a "Remove only" entry, and you have version 1.0.0 installed. You go to the publisher's website and download the installer for version 1.1.0. You run the new installer. If the publisher created everything correctly, the installer will unpack the items needed for the program and update the entry in control panel. The "Remove only" doesn't really matter when executing the installer for a new version.

The "Remove only" is really only related to the uninstaller. Some publishers will write their uninstaller to be able to repair or change the installed program. Others write the uninstaller to only remove the program.

winget install and winget upgrade download the installer from the publisher's website and run it. The only time the uninstaller is called is when uninstallPrevious is specified for an upgrade, or when winget uninstall is run.

And so it seems worth it for winget to make clear how it's going to handle these.

I don't think there is anything to handle. Winget doesn't support the "modify" functions some uninstallers have, and effectively treats all uninstallers as "remove only"

denelon commented 1 year ago

I do consider these as a special case scenario. The challenge is reasoning about what WinGet should display in these cases. I'll leave the issue open to handle this specific case if there are reliable mechanisms or heuristics that are consistent across the set of installers that have a "(remove only)" or similar display string.