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

winget upgrade returns non-valid version numbers for some packages (v1.1.13405)

Open o-l-a-v opened this issue 3 years ago • 4 comments

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 avatar Jan 15 '22 13:01 o-l-a-v

@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.

denelon avatar Jan 18 '22 22:01 denelon

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 ! image image image

In case of MS Teams it is super funny.

JiriZidek avatar Oct 12 '22 22:10 JiriZidek

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.

denelon avatar Oct 13 '22 00:10 denelon

Looks like App&features is also confused OBS... lies image PG.... lies image But not the case for MS Teams - this is true image

JiriZidek avatar Oct 13 '22 01:10 JiriZidek

Have a similar problem with intelliJ: grafik

(apps features ):

also wrong: grafik

but:

grafik

(not sure if this can be solved on winget side :-( )

dermoritz avatar Nov 14 '22 08:11 dermoritz

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

southrop avatar Dec 13 '22 18:12 southrop