Skip to content

Instantly share code, notes, and snippets.

@inxomnyaa
Last active May 12, 2025 02:45
Show Gist options
  • Save inxomnyaa/0ee29d68a6b4c92ba53810828dbc0f4a to your computer and use it in GitHub Desktop.
Save inxomnyaa/0ee29d68a6b4c92ba53810828dbc0f4a to your computer and use it in GitHub Desktop.
Upgrade Selected Apps via Winget
# Based on https://www.codewrecks.com/post/general/winget-update-selective/ (gist at https://gist.github.com/alkampfergit/2f662c07df0ca379c8e8e65e588c687b)
# Adds a grid for selecting multiple apps
# Fixes packages with truncated names
# Adds a confirmation dialogue before actually upgrading
class Software {
[string]$Name
[string]$Id
[string]$Version
[string]$AvailableVersion
}
function Winget-Query-Full-Id{
param(
[string]$string
)
$listLine = winget list $string.TrimEnd("…") | Out-String
$listLines = $listLine.Split([Environment]::NewLine)
For ($i = 0; $i -le $listLines.Length; $i++)
{
$sel = $listLines[$i] | Select-String -Pattern "($($string.TrimEnd("…"))\S*)"# -AllMatches
if (-not $sel) {
continue
}
return $sel.Matches[0].Value
}
return $string.TrimEnd("…")
}
$upgradeResult = winget upgrade --include-unknown | Out-String
$lines = $upgradeResult.Split([Environment]::NewLine)
# Find the line that starts with Name, it contains the header
$fl = 0
while (-not $lines[$fl].StartsWith("Name"))
{
$fl++
}
# Line $i has the header, we can find char where we find ID and Version
$idStart = $lines[$fl].IndexOf("Id")
$versionStart = $lines[$fl].IndexOf("Version")
$availableStart = $lines[$fl].IndexOf("Available")
$sourceStart = $lines[$fl].IndexOf("Source")
# Now cycle in real package and split accordingly
$upgradeList = @()
For ($i = $fl + 1; $i -le $lines.Length; $i++)
{
$line = $lines[$i]
if ($line.Length -gt ($availableStart + 1) -and -not $line.StartsWith('-'))
{
$name = $line.Substring(0, $idStart).TrimEnd()
$id = $line.Substring($idStart, $versionStart - $idStart).TrimEnd()
#Write-Host "$id"
if($line.IndexOf("…") -gt -1){
# Fix Id if it was truncated
$id = Winget-Query-Full-Id -string $id
}
$version = $line.Substring($versionStart, $availableStart - $versionStart).TrimEnd()
$available = $line.Substring($availableStart, $sourceStart - $availableStart).TrimEnd()
$software = [Software]::new()
$software.Name = $name;
$software.Id = $id;
$software.Version = $version
$software.AvailableVersion = $available;
$upgradeList += $software
}
}
$upgradeList | Format-Table
$toUpgrade = $upgradeList | Out-GridView -PassThru
$upgraded = 0
foreach ($package in $toUpgrade)
{
# TODO add option to skip this dialogue
$title = "Upgrade $($package.id)?"
$question = "Upgrade package $($package.id) from $($package.version) to $($package.availableversion)?"
$choices = '&Yes', '&No'
$decision = $Host.UI.PromptForChoice($title, $question, $choices, 0)
if ($decision -eq 0) {
Write-Host "Going to upgrade package $($package.id)"
# TODO add option to skip waiting for input
& winget upgrade --include-unknown --wait --verbose $package.id
if($?){
Write-Host "Success"
$script:upgraded++
}
else{
Write-Host "Failed"
}
}
else {
Write-Host "Skipped upgrade to package $($package.id)"
}
}
Write-Host "Upgraded $($upgraded) out of $($package.Count)"
Read-Host -Prompt "Press any key to exit"
@chriscollingwood613
Copy link

Suggest more error checking on the first parse of the winget output at line 40 to handle the case where there is nothing to process and the output doesn't contain an expected string which causes the script to spew several lines of error messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment