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

Pin a package #476

Closed denelon closed 1 year ago

denelon commented 4 years ago

Description of the new feature/enhancement

Users should be able to prevent the Windows Package Manager from updating a package (assumes the package doesn't have it's own auto-update).

winget pin <package> and the corollary winget unpin <package>.

This was mentioned by:

@megamorf https://github.com/microsoft/winget-cli/issues/120#issuecomment-635174060 @rodalpho https://github.com/microsoft/winget-cli/issues/120#issuecomment-635520051 @aetos382 https://github.com/microsoft/winget-cli/issues/120#issuecomment-635741018 @kmindi https://github.com/microsoft/winget-cli/issues/120#issuecomment-637343639

Proposed technical implementation details (optional)

There should also be a mechanism to display the packages (and the version) that have been "pinned". This might be a function of list or a function of pin. Additionally, if packages are known to self-update (like Visual Studio Code) an additional meta-data entry in the manifest could help users understand when they may not be able to pin a package.

Edit: Clarifying behavior for ambiguity suggested in other Issues.

Pinning should enable users to specify the exact version (likely the default case with no parameters). It should also allow users to specify what portion of a version should be pinned so bug fixes could be applied, or even minor version bumps.

Note: the syntax below is just suggestive.

Assume the user has installed "Awesome App" version "1.2.3".

winget pin "Awesome App" This would pin to version 1.2.3 and would not be upgraded when any newer version is released.

winget pin "Awesome App" --version 1.2 This would pin version to anything less than version 1.3. If version 1.2.4 were released this would be a valid upgrade.

winget pin "Awesome App" --version 1 This would pin the version to anything less than version 2. If version 1.2.4 were released this would be a valid upgrade. If version 1.3.0 were released this would also be a valid upgrade.

Additional example per @brainz80

E.g. Having nodejs-lts version 12.1.1 installed and running winget pin --all nodejs-lts --version 12.x should make it so that when later winget upgrade --all is ran it'd check if there is a update available for nodejs-lts what matches 12.x - if the latest update is e.g 16.1.1, but there is also a 12.1.2 it'd update nodejs-lts to that version.

megamorf commented 4 years ago

From my experience the most common scenario for why you'd want to pin a package version is when there is a known issue with a newer version of that package itself or with one if its dependencies. So on Linux that meant I pinned the docker package because one of its dependencies (containerd) could not be installed.

winget must be able to resolve dependencies (once implemented) for this feature to be really useful since it needs to block the update of all packages that have the pinned package listed in their dependencies.


I also like pinning because it allows me to rely on software that is prone to change a lot (for example when it's early in development) and I can rest easy that it will stay the same unless I explicitly unpin it again.

brunovieira97 commented 4 years ago

From my experience the most common scenario for why you'd want to pin a package version is when there is a known issue with a newer version of that package itself or with one if its dependencies.

I don't see it exactly that way. I could simply block an app, let's say NodeJS, of installing updates because I'm developing stuff that is tested only against a specific version of such package. Anyway, there's lots of use cases, specially for devs.

megamorf commented 4 years ago

I don't see it exactly that way. I could simply block an app, let's say NodeJS, of installing updates because I'm developing stuff that is tested only against a specific version of such package. Anyway, there's lots of use cases, specially for devs.

That's what nvm (node version manager) is for ;-) My line of work is DevOps and most of the times this has been necessary was due to issues in operations but your experience may vary.

domoaligato commented 3 years ago

I think that this might be related or something need to be put in place to support software versions that are higher than the repo versions. Here is my example: I have opted into the Beta versions of Battle.Net This is before a upgrade.

winget upgrade
Name                  Id                          Version    Available    Source
--------------------------------------------------------------------------------
Battle.Net            Blizzard.BattleNet          Unknown    1.22.0.12040 winget

winget upgrade Blizzard.BattleNet
Found Battle.Net [Blizzard.BattleNet]
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 http://dist.blizzard.com/downloads/bna-installers/322d5bb9ae0318de3d4cde7641c96425/retail.1/Battle.net-Setup-enUS.exe
  ██████████████████████████████  2.73 MB / 2.73 MB
Successfully verified installer hash
Starting package install...
Successfully installed

The Client then opens and updates itself to the latest beta version and shows the same results as before.

winget upgrade
Name                Id                          Version   Available    Source
-----------------------------------------------------------------------------
Battle.Net          Blizzard.BattleNet          Unknown   1.22.0.12040 winget

Maybe this is just something wrong with the packages and this functionality is not needed, but Discord.Discord , GOG.Galaxy, EpicGames.EpicGamesLauncher all have this issue.

Croydon commented 3 years ago

Additionally, if packages are known to self-update (like Visual Studio Code) an additional meta-data entry in the manifest could help users understand when they may not be able to pin a package.

As a note, please don't block pinning of packages that are known to self-update / have own updaters. I'm using package pinning in chocolatey also for packages, which I want to update via another mechanism than chocolatey itself. I think this is a valid use case too.

theodiefenthal commented 3 years ago

From my experience the most common scenario for why you'd want to pin a package version is when there is a known issue with a newer version of that package itself or with one if its dependencies.

Two more usecases (well one and a half):

  1. Licensing. After I now installed winget, I see it could update my PDF-XChange-Editor, but I only have a license for version 8, so I don't want winget to upgrade it to version 9 and instead, I decide myself when to buy a new license and manually upgrade.
  2. I see that WinGet wants to update my NextCloud client. The latest two versions work in principle but due to some issues with my own nextcloud installation, it doesn't work with my specific server. So I'll stick for a while with an older version whereas for most other users, those newer versions work fine.
Soitora commented 3 years ago

From my experience the most common scenario for why you'd want to pin a package version is when there is a known issue with a newer version of that package itself or with one if its dependencies.

Two more usecases (well one and a half):

Personally, I'd like to block Python as I got multiple Python installations if it's needed, which it has been in the past for a few niche programs. But it wants to update all of them to latest.

bchap1n commented 3 years ago

I just tried 'winget upgrade --all' for the first time and it tried to upgrade Audacity to 3+ which is now suspected for spyware. Audacity 2.4 is safe and will remain very popular. I didn't install Audacity with Winget (I used Scoop and pinned it). If Winget is going to upgrade apps installed via Scoop or Choco then we need to be able to pin.

webartoli commented 3 years ago

From my experience the most common scenario for why you'd want to pin a package version is when there is a known issue with a newer version of that package itself or with one if its dependencies.

Two more usecases (well one and a half):

Personally, I'd like to block Python as I got multiple Python installations if it's needed, which it has been in the past for a few niche programs. But it wants to update all of them to latest.

Same issue here, on a dev host I'm using:

the main issue is that pyenv install python as a program so winget` try to upgrade any versions that is not the expected behaviour.

stevensko commented 3 years ago

I'd also need to block updates. I just had audacity try to update to the spyware version.

dannyglover commented 3 years ago

Any updates on this being included at some stage? Thank you

denelon commented 3 years ago

Yes, we do plan on adding support for pinning packages. We don't have a timeline yet, but I'll go ahead and move it into the v.Next bucket to see if it looks like a good fit for this calendar year or not.

marcinsmialek commented 3 years ago

It might be very useful for things like Python, having other apps side-by-side, or avoiding radical changes, like in Python or Postman.

jaherron commented 3 years ago

This feature would be useful for people who develop games with Unity and have both a standard and an LTS version installed.

derphilipp commented 2 years ago

Another field of use would be packages, that are maintained by the companies IT and therefore always fail updating (e.g. Teams, Office); I would love to ignore those explicitly when using the update command. This would not be pinning but ignoring/masking.

brainz80 commented 2 years ago

Couldn't the same pinning feature be used for those?

ke 20. lokak. 2021 klo 11.08 Philipp Weißmann @.***> kirjoitti:

denelon commented 2 years ago

@brainz80 We've been discussing that with

I think pinning might work, but it's a bit of a different scenario in terms of "ignore"/"exclude" this package when running winget upgrade --all.

derphilipp commented 2 years ago

To be honest with the "ignore this software when updating" I do not really care how the ignoring works. Though I agree: I think pinning is a different topic itself, it probably is related/a "neighbor-feature" to it.

NicTanghe commented 2 years ago

I'd like to not reinstall python 3.7 anytime i upgrade -all as well as just put the name in a file somewhere so i don't have to manually add the exception every time.

Khaos66 commented 2 years ago

I really need this feature. I'm having multiple versions of GhostScript (for debugging). None of them was installed via winget...

GPL Ghostscript                             ArtifexSoftware.GhostScript            9.50           9.55.0         winget
GPL Ghostscript                             ArtifexSoftware.GhostScript            9.52           9.55.0         winget
GPL Ghostscript                             ArtifexSoftware.GhostScript            9.53.0         9.55.0         winget
GPL Ghostscript                             ArtifexSoftware.GhostScript            9.53.1         9.55.0         winget
GPL Ghostscript                             ArtifexSoftware.GhostScript            9.53.3         9.55.0         winget
GPL Ghostscript                             ArtifexSoftware.GhostScript            9.54.0         9.55.0         winget
LeonarddeR commented 2 years ago

While I really dig the idea of pinning, I think in the case of Python, it somehow more like a workaround than a solution to the problem. With Python, every minor release (e.g. 3.7, 3.8. 3.9) can be installed side by side. Therefore these versions really ought to be treated as separate applications by Winget (Python 3.8 shouldn't update to 3.10 but to the most recent patch release of version 3.8). I can think of several other applications where this is applicable.

Khaos66 commented 2 years ago

While I really dig the idea of pinning, I think in the case of Python, it somehow more like a workaround than a solution to the problem. With Python, every minor release (e.g. 3.7, 3.8. 3.9) can be installed side by side. Therefore these versions really ought to be treated as separate applications by Winget (Python 3.8 shouldn't update to 3.10 but to the most recent patch release of version 3.8). I can think of several other applications where this is applicable.

+1 Also make sure, that this isn't a publisher think. The end user should be able to set this up as well, in case the publisher didn't

dafunks commented 2 years ago

Just adding my name to this as this is a much needed feature. For me it just makes using the update --all option a pain as it installs a bunch of programs that do not need updating.

And ignore list or just simply being able to add a version number in some way would fix this.

denelon commented 2 years ago

Please just add a 👍 to the issue rather than a comment. People are subscribed to these Issues, and they don't need to get an e-mail with a "me too" type of comment. We do use the sort by 👍the backlog to help prioritize features.

Dragon1573 commented 2 years ago

Here's my reason why I need this feature:

I'm currently using EdrawSoft.EdrawMax and EdrawSoft.EdrawMind. BUT my installed version is totally different from that in Winget! EdrawMax and EdrawMind stored in winget-pkgs are "international edition" while mine are "Simplified Chinese specified edition" (they called it MindMaster instead of EdrawMind). They aren't the same stuff and license system are different too.

I bought the perpetual license for the Chinese Edition with my QQ account, but have no entrance to login my account in the International Edition (associated e-mail address login failed either). Everytime I invoke winget upgrade --all, Winget will automatically upgrade them from Chinese Edition to International Edition and annoying me.

I've submitted EdrawSoft.EdrawMax.CN and EdrawSoft.MindMaster.CN to Microsoft/winget-pkgs ,but both of them can only be installed in interactive mode and unable to pass the Azure DevOps. The PR is currently at "Draft" state.

So I want to simply pin it and upgrade manually using their own update-checker.

p1r473 commented 2 years ago

+1 for this.

denelon commented 2 years ago

We should also update the output from `winget upgrade" to include a table of pinned packages below the list of upgradable packages and the ones that update themselves.

jedieaston commented 2 years ago

If anyone wants to rip this apart, please feel free. I think I covered most of the major cases: https://github.com/microsoft/winget-cli/pull/1894

denelon commented 2 years ago

You rock @jedieaston!

brainz80 commented 2 years ago

"We should also update the output from `winget upgrade" to include a table of pinned packages below the list of upgradable packages and the ones that update themselves."

Isn't there room to add it beside the rest?

ma 31. tammik. 2022 klo 21.36 denelon @.***> kirjoitti:

denelon commented 2 years ago

The formatting based on the terminal width truncates longer package names and identifiers. We're also conscious about what the object output needs to look like for PowerShell cmdlets in addition to the client executable.

NicTanghe commented 2 years ago

Hello, thought i’d come drop a professional use case and some onlook. In VFX, it's best to follow this reference platform. https://vfxplatform.com/

It would be nice if we could state winget install python 3.9, and it automatically upgrades to the latest version of 3.9

not just install 3.9.10 or 3

also, since latest should always b taken with a grain of salt, maybe allow us to specify to only upgrade to the latest -1 version.

radiorz commented 2 years ago

I just use nodejs to write a simple way to ignore some pacakges:

const ignoreIds = [
  "Python.Python.2",
  "Todesk.Todesk",
];

const { spawn } = require("child_process");
const [command, ...args] = `winget upgrade`.split(" ");
const child = spawn(command, args, {
  stdio: "pipe",
});
let startLog = false;
let appIds = [];
child.stdout.on("data", (data) => {
  const dataStr = data.toString();
  if (dataStr.indexOf("名称") !== -1) startLog = true;
  if (startLog) {
    // 选出 空格字符.字符空格 格式的字符串
    const reg = /\s[A-Za-z]+[A-Za-z1-9]+[.]+[A-Za-z0-9.]+\s/g;
    const matchId = dataStr.match(reg);
    if (matchId) appIds.push(...matchId);
  }
});
(async function start() {
  await new Promise((resolve) => {
    child.stdout.on("close", resolve);
  });
  console.log(`appIds`, appIds);
  // 过滤掉不需要更新的应用
  const needInstallIds = appIds
    .map((id) => id.trim())
    .filter((id) => !ignoreIds.includes(id));
  // 更新需要更新的应用
  for (const id of needInstallIds) {
    const [command, ...args] = `winget upgrade --id ${id}`.split(' ');
    const child = spawn(command, args, { stdio: "pipe" });
    child.stdout.on("data", (data) => {
      console.log(`data`, data.toString());
    });
    child.stderr.on("data", (data) => {
      console.log(
        `data stderr`,
        data.toString()
      );
    });
  }
})();

you can download the nodejs and run it by node index ; and add ignore ids in variable ' ignoreIds' ; waiting for the winget official version

DanTheMan827 commented 2 years ago

One use-case for me would be to restrict the version of an application like Kodi so that it remains compatible with an older database schema.

Or calibre so that I can continue using some plugins that don't yet work properly on the new version.

chbwien commented 2 years ago

This is also true for IrfanView. There is the public package, and bigger companies get special company editions with their branding. Currently, these branded editions will be overwritten with winget, because there is indeed a new version, but winget will pull the public (non branded) edition. I know this, so I could pin Irfanview and do the updates manually. And as described above, licenses may not be available for the next major version (e.g. em-client), or licenses are based on a subscription model (this would need pinning to a time!)

denelon commented 2 years ago

It may be possible to "pin by time" using the releaseDate key in the manifest, but it could be problematic if the releaseDate isn't present or reliable in the manifest.

chbwien commented 2 years ago

Yes, of course it depends on the available data. The weakest information is the current date (no more updates after, even if they might be older). This is not correct but on the safe side.

o-l-a-v commented 2 years ago

PIN is great for a per user basis customization. But for enterprise client management, it would be better if one could set apps to ignore in upgrade --all by using the settings.json or GPO/ ADMX/ registry key IMO.

Or maybe introduce a new option for the upgrade -all command; a comma seperated list of apps to upgrade, or not upgrade, if a new version is available.

A hot one to ignore in enterprise is Microsoft 365 ProPlus, and user scope apps that auto upgrade (that doesn't require admin).

Hitmanforrent commented 2 years ago

One use case for me would be to pin packages that lack a DisplayVersion key. I know it's ultimately the packagers fault, but it's annoying that upgrade --all attempts to upgrade packages that are up to date. Pinning as a workaround would be very helpful.

DarthSonic commented 2 years ago

I have a simple case why need to ignore a package: Package is "Sky.SkyGo" and it will install the british SkyGo app everytime instead of the german one over WinGet. And so I can not login to the app anymore and must manually install the german version.

o-l-a-v commented 2 years ago

@DarthSonic

Don't know if this might help, but you can set default language for installs with settings I think.

So the app exists in german:

And here is doc for settings => locale:

But if next update to winget-pkgs does not include the german version, you're f*cked again probably?

DarthSonic commented 2 years ago

@o-l-a-v That did not work: image

o-l-a-v commented 2 years ago

@DarthSonic

Alright, it was worth a try though. :/

denelon commented 2 years ago

@DarthSonic

The installer file on these lines give the impression the download URL is specific to en-GB. If it's the same installer for "any" locale, and we need to pass switches then we need to specify another locale in the manifest and any appropriate switches.

It's likely given the GB locale is in the download URL, that a different download URL would apply to a German installer.

The presence of a locale file doesn't guarantee there is an installer or configuration for a different locale. The locale files are primarily metadata to help with translated descriptions and other metadata.

denelon commented 2 years ago

@Hitmanforrent

A feature was added to "ignore unknown", but there was a bug. I'm expecting this to be fixed in the next release. Users would still be able to "--include-unknown" if they wanted to run the upgrade anyway.

DarthSonic commented 2 years ago

@denelon and that is why I want to ignore that package on winget upgrade --all and that is a good example why that feature would be helpful.

DanTheMan827 commented 2 years ago

@DarthSonic try it now, I submitted a pull request that was just merged to add the de-DE installer for sky

sibbl commented 2 years ago

My specific use case is that I've got nvm installed but winget always replaces it with Node.js when updating all packages.

I just installed the Powershell module Cobalt and run the following command for updating:

Get-WinGetPackageUpdate | Where-Object { $_.Id -notin @("OpenJS.NodeJS") } | ForEach-Object { Update-WinGetPackage -ID $_.Id }
brianary commented 2 years ago

My hardware isn't supported anymore, but winget upgrade --all re-downloads the update every single time, then fails.

image

jayagami commented 2 years ago

I thought this should be a fundamental function for a package management tools.

apt, pacman, dnf, scoop...

I'm surprised there isn't an elegant way to implement it by now, It's not that difficult to implement this feature, is it?