winget-cli
winget-cli copied to clipboard
winget upgrade returns non-valid version numbers for some packages (v1.1.13405)
Today, winget upgrade
returned a list of applications where some of the version numbers are not valid. As in converting them to [System.Version]
in PowerShell fails. I can't remember having this issue before.
Packages in this example
Presenting valid version numbers should be something we can take for granted when using a package manager IMO. Either the manifest tests should pick this up (not accept commits where version number is following valid version format), and/ or winget-cli should parse the version numbers before showing them in the terminal.
Raw output from winget upgrade
Name Id Version Available Source
-----------------------------------------------------------------------------------------------------------------------
7-Zip 7zip.7zip 21.06 21.07 winget
Audacity Audacity.Audacity 3.1.2 3.1.3 winget
Discord Discord.Discord 0.0.309 1.0.9003 winget
Eraser Eraser.Eraser 6.2.2992 6.2.2993 winget
Git Git.Git 2.20.1 2.34.1 winget
GitHub Desktop GitHub.GitHubDesktop 2.2.4 2.9.6 winget
HexChat HexChat.HexChat 2.14.2 2.16.0 winget
Intel® Driver & Support Assistant Intel.IntelDriverAndSupportAssistant 21.5.33.3 21.7.50.3 winget
Jabra Direct Jabra.Direct 4.0.6206.0 5.10.2 winget
KeePass DominikReichl.KeePass 2.49 2.50.0 winget
K-Lite Mega Codec Pack CodecGuide.K-LiteCodecPack.Mega 16.6.2 16.7.0 winget
MakeMKV GuinpinSoft.MakeMKV v1.12.3 v1.16.5 winget
Microsoft 365 Apps for enterprise Microsoft.Office 16.0.14701.20262 16.0.14729.20228 winget
Microsoft 365 Apps for enterprise Microsoft.Office 16.0.14701.20262 16.0.14729.20228 winget
Microsoft Azure CLI Microsoft.AzureCLI 2.0.62 2.32.0 winget
Microsoft Teams Microsoft.Teams 1.4.00.23957 1.4.00.32771 winget
Microsoft Visual C++ 2015-2019 Redistrib… Microsoft.VC++2015-2019Redist-x86 14.28.29325.2 14.29.30139.0 winget
Microsoft Visual Studio Code Microsoft.VisualStudioCode 1.56.2 1.63.2 winget
Notepad++ Notepad++.Notepad++ 8.1.9.3 8.2 winget
NTLite Nlitesoft.NTLite 1.7.5.6842 2.3.3.8567 winget
Plex Plex.Plex 1.4.1 1.39.1 winget
Plex Media Server Plex.PlexMediaServer 1.21.0.3711 1.25.3.5409 winget
Signal OpenWhisperSystems.Signal 1.40.1 5.27.1 winget
SyncTrayzor SyncTrayzor.SyncTrayzor 1.1.28.0 1.1.29.0 winget
TagScanner SergeySerkov.TagScanner Unknown 6.1.11 winget
VMware Player VMware.WorkstationPlayer 16.1.0 16.2.1 winget
XnConvert XnSoft.XnConvert 1.77 1.92.0 winget
27 upgrades available.
Environment
winget --version
v1.1.13405
winget --info
Windows Package Manager v1.1.13405
Copyright (c) Microsoft Corporation. All rights reserved.
Windows: Windows.Desktop v10.0.19043.1387
Package: Microsoft.DesktopAppInstaller v1.16.13405.0
Logs: %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir
Links
---------------------------------------------------------------------------
Privacy Statement https://aka.ms/winget-privacy
Licence Agreement https://aka.ms/winget-license
Third Party Notices https://aka.ms/winget-3rdPartyNotice
Homepage https://aka.ms/winget
Windows Store Terms https://www.microsoft.com/en-us/storedocs/terms-of-sale
Why is this important?
Until winget-cli
provides the ability to output results to a standardized format, like JSON or CSV, people trying to use winget-cli
with other tools, like Intune (example: https://github.com/o-l-a-v/winget-intune-win32), must try to parse the output.
Another example: I've made a PowerShell script to update just certain packages, like skipping Microsoft Office ProPlus. Handling all the special problems currently present in winget-cli, like winget upgrade --id <id>
not working for a lot of packages, and now the version numbers not being standardized, the logic is starting to get ugly. Current script right now:
Click to show
#Requires -RunAsAdministrator
#Requires -Version 5.1
<#
.SYNOPSIS
Use winget to update some packages.
.NOTES
Author: Olav Rønnestad Birkeland | github.com/o-l-a-v
Created: 211205
Modified: 220115
Logs
* %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir
.EXAMPLE
& $psISE.CurrentFile.FullPath
#>
# Input parameters
[OutputType($null)]
Param()
# Settings
## PowerShell preferences
$ErrorActionPreference = 'Stop'
$InformationPreference = 'Continue'
## Encoding
$null = cmd /c '' # Workaround for PowerShell ISE "Exception setting "OutputEncoding": "The handle is invalid.""
$Global:OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
# Assets
## Context
$IsAdmin = [bool](
([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
)
$IsSystem = [bool](
[System.Security.Principal.WindowsIdentity]::GetCurrent().'User'.'Value' -eq 'S-1-5-18'
)
## Control what apps to update with winget - MATCHES USING POWERSHELL -like
$Apps = [string[]](
$(
# Device/ system context, requires admin
if ($IsAdmin) {
[string[]](
'7zip.7zip',
'CodecGuide.K-LiteCodecPack.Mega',
'Eraser.Eraser', # "winget upgrade" does not work, must use "winget install".
# 'Intel.IntelDriverAndSupportAssistant',# "winget upgrade" does not work, "winget install" does not remove old versions.
'Jabra.Direct',
'Logitech.Options',
'Microsoft.dotnetRuntime.?-x??', # Include all Microsoft .NET Desktop Runtime
'Microsoft.VC++2005Redist-*', # Includes all architectures
'Microsoft.VC++2008Redist-*', # Includes all architectures
'Microsoft.VC++2010Redist-*', # Includes all architectures
'Microsoft.VC++2012Redist-*', # Includes all architectures
'Microsoft.VC++2013Redist-*', # Includes all architectures
'Microsoft.VC++2015-2022Redist-*', # Includes all architectures | Must use "winget install" due winget-cli bug "Multiple apps found matching input criteria"
'Microsoft.PowerToys', # "winget upgrade" does not work, must use "winget install".
'Microsoft.VisualStudioCode',
'Nlitesoft.NTLite', # URL goes to latest version, which might be newer than whats in winget-pkgs. So add "--force".
'Notepad++.Notepad++',
'Plex.Plex', # Not totally silent, "updating" prompt, and starts after install.
'Postman.Postman',
'SoftDeluxe.FreeDownloadManager',
'WireGuard.WireGuard',
'XnSoft.XnConvert',
'XnSoft.XnViewMP'
)
}
# User context, requires to not run as SYSTEM
if (-not $IsSystem) {
[string[]](
'GitHub.GitHubDesktop',
'JanDeDobbeleer.OhMyPosh',
'OpenWhisperSystems.Signal'
)
}
)
)
## Special behavior - MATCHES USING POWERSHELL -eq
### Remove desktop shortcut if not already present
$OverrideRemoveDesktopShortcut = [ordered]@{
'GitHub.GitHubDesktop' = [string] 'GitHub Desktop'
'Postman.Postman' = [string] 'Postman'
}
$OverrideRemoveDesktopShortcut.GetEnumerator().'Name'.ForEach{
$OverrideRemoveDesktopShortcut.$($_) = [string[]](
('{0}\{1}.lnk' -f [System.Environment]::GetFolderPath('Desktop'), $OverrideRemoveDesktopShortcut.$($_)),
('{0}\{1}.lnk' -f [System.Environment]::GetFolderPath('CommonDesktopDirectory'), $OverrideRemoveDesktopShortcut.$($_))
)
}
### Use install instead of upgrade, due to winget-cli bugs/ missing features
$OverrideUseInstallInsteadOfUpgrade = [string[]](
'Eraser.Eraser',
'Intel.IntelDriverAndSupportAssistant',
'Microsoft.VC++2015-2022Redist-x64',
'Microsoft.VC++2015-2022Redist-x86',
'Microsoft.PowerToys'
)
### Skip checksum validation (--force) because download URI dynamically fetches newest version, which might be newer than winget-pkgs
$OverrideSkipChecksumValidation = [string[]](
'Nlitesoft.NTLite'
)
## Path of winget-cli
$WingetCliPath = [string](
$(
if ([System.Security.Principal.WindowsIdentity]::GetCurrent().'User'.'Value' -eq 'S-1-5-18') {
'{0}\AppInstallerCLI.exe' -f (
(Get-Item -Path ('{0}\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe' -f $env:ProgramW6432)).'FullName' | Select-Object -First 1
)
}
else {
'{0}\Microsoft\WindowsApps\winget.exe' -f $env:LOCALAPPDATA
}
)
)
# Get upgradeable apps
## Introduce step
Write-Information -MessageData '# Get Winget output'
## Get output from "winget upgrade"
### Get
do {
$WingetUpgradeable = [string[]](
cmd /c ('{0} upgrade' -f $WingetCliPath) | Where-Object -FilterScript {$_[0] -notmatch '\s'}
)
}
while (
$(
if ($WingetUpgradeable.'Count' -eq 1 -and $WingetUpgradeable[0] -like 'No installed*') {
$false
}
else {
$WingetUpgradeable[1][0] -ne '-'
}
)
)
### Exit if no packages to update
if ($WingetUpgradeable.'Count' -eq 1) {
Write-Information -MessageData ('No packages to update: "{0}".' -f $WingetUpgradeable[0])
Exit 1
}
### Sort output alphabetically
$WingetUpgradeable = [string[]](
$WingetUpgradeable[0,1] + ($WingetUpgradeable[2 .. $WingetUpgradeable.IndexOf($WingetUpgradeable[-2])] | Sort-Object) + $WingetUpgradeable[-1]
)
### Remove empty entries
$WingetUpgradeable = [string[]]($WingetUpgradeable.Where{-not[string]::IsNullOrEmpty($_)})
## As string array
### Introduce step
Write-Information -MessageData '## Raw output as string array'
### Show output
Write-Information -MessageData ($WingetUpgradeable -join [System.Environment]::NewLine | Out-String).Trim()
## As PowerShell object
### Introduce step
Write-Information -MessageData ('{0}## As PowerShell object' -f [System.Environment]::NewLine)
### Find indexes
#### "Id"
$IndexIdStart = [byte] 0
while ($WingetUpgradeable[0][$IndexIdStart] -cne 'I') {$IndexIdStart++}
#### "Version"
$IndexVersionStart = [byte] $IndexIdStart
while ($WingetUpgradeable[0][$IndexVersionStart] -cne 'V') {$IndexVersionStart++}
#### "Available
$IndexAvailableStart = [byte] $IndexVersionStart
while ($WingetUpgradeable[0][$IndexAvailableStart] -cne 'A') {$IndexAvailableStart++}
#### "Source"
$IndexSourceStart = [byte] $IndexAvailableStart
while ($WingetUpgradeable[0][$IndexSourceStart] -cne 'S') {$IndexSourceStart++}
### Create object
$WingetUpgradeableAsObject = [PSCustomObject[]](
$WingetUpgradeable[2 .. ($WingetUpgradeable.'Length' - 2)].ForEach{
[PSCustomObject]@{
'Name' = [string]($_[0 .. ($IndexIdStart-1)] -join '').Trim()
'Id' = [string]($_[$IndexIdStart .. ($IndexVersionStart -1)] -join '').Trim()
'Version' = [System.Version](
$(
Try {
[System.Version](
$_[$IndexVersionStart .. ($IndexAvailableStart -1)] -join '' -replace '^[vV]{1}', '' -replace '\s', ''
)
}
Catch {
'0.0.0'
}
)
)
'Available' = [System.Version](
$(
Try {
[System.Version](
$_[$IndexAvailableStart .. ($IndexSourceStart -1)] -join '' -replace '^[vV]{1}', '' -replace '\s', ''
)
}
Catch {
'0.0.0'
}
)
)
'Source' = [string]($_.Substring($IndexSourceStart) -split '\s' | Select-Object -First 1)
}
} | Sort-Object -Property 'Name'
)
### Output
Write-Information -MessageData ($WingetUpgradeableAsObject | Format-Table -AutoSize | Out-String).Trim()
# Find upgradeable apps matching input criterias
## Introduce step
Write-Information -MessageData ('{0}# Find upgradeable apps matching input criterias' -f ([System.Environment]::NewLine*2))
## Find
$AppsUpgradeable = [PSCustomObject[]](
$(
foreach ($App in $Apps) {
$WingetUpgradeableAsObject.Where{$_.'Id' -like ('*{0}*'-f$App)}
}
) | Sort-Object -Property 'Name'
)
## Output results
Write-Information -MessageData ('Found {0}.' -f $AppsUpgradeable.'Count'.ToString())
# Upgrade upgradeable apps
## Introduce step
Write-Information -MessageData ('{0}# Upgrade apps' -f ([System.Environment]::NewLine*2))
## Show no upgrades avaialble if none
if ($AppsUpgradeable.'Count' -le 0) {
Write-Information -MessageData 'Found no apps to update.'
}
## Upgrade upgradeable apps
foreach ($App in $AppsUpgradeable) {
Write-Information -MessageData (
'{0}## {1} / {2} "{3}" | {4} => {5}' -f (
$(if($AppsUpgradeable.IndexOf($App) -ne 0){[System.Environment]::NewLine}),
(1+$AppsUpgradeable.IndexOf($App)).ToString('0'*$AppsUpgradeable.'Count'.ToString().'Length'),
$AppsUpgradeable.'Count'.ToString(),
$App.'Name',
$App.'Version',
$App.'Available'
)
)
# Check if desktop shortcut should be deleted
$DeleteDesktopShortcut = [bool](
$(
if (
$OverrideRemoveDesktopShortcut.$($App.'Id') -and
$OverrideRemoveDesktopShortcut.$($App.'Id').Where{
-not [string]::IsNullOrEmpty($_)
}.'Count' -gt 0 -and
$OverrideRemoveDesktopShortcut.$($App.'Id').Where{
[System.IO.File]::Exists($_)
}.'Count' -le 0
) {
$true
}
else {
$false
}
)
)
# Create upgrade command
$Command = [string](
'{0} {1} --id {2}{3} --silent --accept-package-agreements --accept-source-agreements' -f (
$WingetCliPath,
$(if($App.'Id' -in $OverrideUseInstallInsteadOfUpgrade){'install'}else{'upgrade'}),
$App.'Id',
$(if($App.'Id' -in $OverrideSkipChecksumValidation){' --force'})
)
)
Write-Information -MessageData $Command
# Upgrade
$null = cmd /c $Command
Write-Information -MessageData ('Done. $? = {0}, $LASTEXITCODE = {1}.' -f $?.ToString(), $LASTEXITCODE)
# Remove desktop shortcut if install success and override exists
if ($LASTEXITCODE -and $LASTEXITCODE -eq 0 -and $DeleteDesktopShortcut) {
$OverrideRemoveDesktopShortcut.$($App.'Id').Where{
-not [string]::IsNullOrEmpty($_) -and
[System.IO.File]::Exists($_)
}.ForEach{
$null = [System.IO.File]::Delete($OverrideRemoveDesktopShortcut.$($App.'Id'))
}
Write-Information -MessageData 'Desktop shortcut deleted.'
}
}
@o-l-a-v we've run into plenty of strange version numbers attempting to meet developers where they are. We've also encountered subtle differences between a "marketing" package version and the version reported in Windows Apps & Features. We're still working on the implementation for the 1.1 schema so we can have a rational separation between these two types of software versions.
Here is an example where build numbers might use dates, product codes, and branch numbers: https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
Versions today are essentially just "strings' we attempt to reason about as best we can. We've had a few different issues submitted related to versioning. We're also looking at how we may handle this in the PowerShell cmdlets. Unfortunately, not all software adheres to https://semver.org.
winget does not correctly recognize installed version of these programs:
Name Id Version Available Source
----------------------------------------------------------------------------------
OBS Studio OBSProject.OBSStudio 26.1.1 28.0.3 winget
PostgreSQL 14 PostgreSQL.PostgreSQL 14 14.5 winget
Teams Machine-Wide Installer Microsoft.Teams 1.3.0.21759 1.5.00.21668 winget
In reality all three are updated to current !
In case of MS Teams it is super funny.
It looks like OBS Studio is either not performing an upgrade including updating registry keys, or it's doing a side-by-side install and the older version is still present.
The same appears to be true for PostgreSQL.
Teams is a unique one. If Teams is installed in "user" scope, the Machine Wide Installer is first installed, and we're getting a false match for an upgrade to the machine wide installer even if you have the latest (or a newer) version installed.
We've got some improvements coming for the winget upgrade
logic and the winget install
logic to see if the latest version is already installed so we don't need to prompt for an upgrade to an older version.
Looks like App&features is also confused
OBS... lies
PG.... lies
But not the case for MS Teams - this is true
Have a similar problem with intelliJ:
(apps features ):
also wrong:
but:
(not sure if this can be solved on winget side :-( )
I'm actually seeing an interesting issue with Jetbrains ReSharper tools after I updated them to 2022.3 via Jetbrains Toolbox.
JetBrains dotTrace 2022.3 JetBrains.ReSharper > 2022.2.2 2022.2.3 winget
JetBrains ETW Host Service (x64) JetBrains.ReSharper > 2022.2.2 2022.2.3 winget