winget-cli: winget should not install an already installed package

Description of the new feature/enhancement

winget should check if an application is already installed before it runs the installer

Proposed technical implementation details (optional)

check for installed software (could be a registry key) before you run the install command. example: if the winget install <software> is run once, then the next time it runs, it should prompt that the <software> is already installed and not proceed to reinstall it again.

Current implementation will reinstall the software over and over:

PS C:\Windows\system32> winget install "Visual Studio Code"
Found Visual Studio Code [Microsoft.VisualStudioCode]
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://az764295.vo.msecnd.net/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/VSCodeUserSetup-x64-1.55.2.exe
  ██████████████████████████████  68.9 MB / 68.9 MB
Successfully verified installer hash
Starting package install...
Successfully installed

PS C:\Windows\system32> winget install "Visual Studio Code"
Found Visual Studio Code [Microsoft.VisualStudioCode]
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://az764295.vo.msecnd.net/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/VSCodeUserSetup-x64-1.55.2.exe
  ██████████████████████████████  68.9 MB / 68.9 MB
Successfully verified installer hash
Starting package install...
Successfully installed

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 15
  • Comments: 23 (14 by maintainers)

Commits related to this issue

Most upvoted comments

+1 here.

My scenario: I’m creating my own windows dotfiles repo. I would like it to have a declarative definition of my environment. Kind of a poor’s man puppet/cheff recipe. I thought it would be trivial with winget and a install.ps1 :

winget install Notepad++.Notepad++
winget install Docker.DockerDesktop
winget install minikube
(...)

The thing is, Docker may require a restart, which breaks blocks minikube install. That is something I can handle by manually re-running the script. Nonetheless, every already installed app will attempt a reinstall, including the 500mb Docker wich gets downloaded again and it takes some time…

My script is meant to install, not upgrade the components. To upgrade I already have winget upgrade command. Others mentioned a command to install or upgrade winget install --upgrade. I would find that usefull too.

These are the semantics that feel natural to me and would allow me to script my environment:

  • winget install pkg: Install if not installed (never upgrade):
  • winget install pkg --force: Reinstall, or upgrade (IMO)
  • winget install pkg --upgrade: Install only if not installed, upgrade if newer available, do nothing if already on latest…
  • winget upgrade pkg: Upgrade if installed, fail if not installed.

Can we add option reinstall or flag --force?

How about:

The current version is installed on the system:

>winget install "visual studio code"
Warning: Microsoft Visual Studio Code [Microsoft.VisualStudioCode] is already installed.
No upgrade is available.
>

An upgrade is available:

>winget install "visual studio code"
Warning: Microsoft Visual Studio Code [Microsoft.VisualStudioCode] is already installed.
Upgrading Microsoft Visual Studio Code [Microsoft.VisualStudioCode]
Found Microsoft Visual Studio Code [Microsoft.VisualStudioCode] Version 1.62.1
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://az764295.vo.msecnd.net/stable/f4af3cbf5a99787542e2a30fe1fd37cd644cc31f/VSCodeUserSetup-x64-1.62.1.exew
  ██████████████████████████████  76.2.8 MB / 76.2 MB
Successfully verified installer hash
Starting package install...
Successfully installed
>

We could optionally add a setting for the default behavior. The default setting would be to perform the upgrade, users could also specify not automatically running the upgrade. In that case, the user would be informed an upgrade is available, but they would need to run the upgrade command themselves in that case:

>winget install "visual studio code"
Warning: Microsoft Visual Studio Code [Microsoft.VisualStudioCode] is already installed.
An upgrade is available.
Found Microsoft Visual Studio Code [Microsoft.VisualStudioCode] Version 1.62.1
>

I like the idea of following prior art to avoid some muscle memory confusion “–no-upgrade” makes sense as an argument to “override” settings if they are different in my opinion.

It is possible to get the “file version” from an .exe, but that doesn’t always correlate to the registry entries that are used to display package versions in Windows Apps & Features. That could also lead to other unintended consequences when the package does decide to write something to the registry. There are also cases where there is a “marketing version” for a product that publishers prefer to display to users. Sometimes a program will also display a different version itself than the file version.

There are just too many edge cases that lead to unreliable or confusing behavior for users which is why we defer to the version reported to Windows Apps & Features, and we allow a different “marketing version” to be displayed in the manifest than the version reported in the registry. The .NET packages are a perfect example of that use case as well.

Just in case somebody needs a workaround for a script:

set app=Microsoft.DotNet.Runtime.6
winget list %app% | findstr /c:"No installed package found" /c:"Es wurde kein installiertes Paket gefunden" > nul && (
    winget install %app%
) || (
    winget upgrade %app%
)

Change it to your wanted app. Also you have to change the findstr search text if you need a different/further language than English/German.

Of course it would be way better if winget had a simple flag or command to avoid such mess 😉