Skip to content

Instantly share code, notes, and snippets.

@Nejat
Last active March 14, 2023 11:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Nejat/6cb42f098320cb2fcb57a4e1728ca29e to your computer and use it in GitHub Desktop.
Save Nejat/6cb42f098320cb2fcb57a4e1728ca29e to your computer and use it in GitHub Desktop.
Lists and optionally upgrades available updates. Skips Unknown versions or anything in the skip list
param(
# provide a configuration file to define package handling
[string] $skipFile = "wngt-cfg",
# true to upgrade eligible packages, false dry run
[switch] $upgrade = $false
)
# adjust buffer size of output, really don't know if this helps ¯\_(ツ)_/¯
if ($Host -and $Host.UI -and $Host.UI.RawUI) {
$rawUI = $Host.UI.RawUI
$oldSize = $rawUI.BufferSize
$typeName = $oldSize.GetType( ).FullName
$newSize = New-Object $typeName (500, $oldSize.Height)
$rawUI.BufferSize = $newSize
}
# get raw output of available upgrades, not power-shell native yet
[string] $rawOutput = winget upgrade --source winget --include-unknown | out-string -Width 5000
# split raw output into lines
[string[]] $source = $rawOutput.Split([System.Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
# check if available upgrades succeeded, otherwise display message to user
if ($lastExitCode -ne 0) {
Write-Host
Write-Host checking winget upgrade failed, try again -ForegroundColor Red
Write-Host
exit $lastExitCode
}
# load and parse a skip list definition file if it exists, otherwise ignore skips
[PSCustomObject[]] $skip = if (Test-Path -Path $skipFile -PathType Leaf) {
Get-Content $skipFile | ForEach-Object {
[string[]] $parts = $_.Split('#')
[PSCustomObject] @{
Skip = $parts[0].Trim()
Comment = $parts[1].Trim()
}
}
} else {
@() # otherwise $skip is null
}
# check if package matches any of the listed skip packages
function Skip-Update {
param ([string] $name)
foreach ($skipped in $skip) {
[string] $check = "*{0}*" -f $skipped.Skip
if ($name -like $check) {
return $skipped.Comment
}
}
return ""
}
# available upgrades
[PSCustomObject[]] $updates = @()
# all the indexes of column titles for parsing in current grouping
[int32] $idIndex = -1
[int32] $currentIndex = -1
[int32] $availableIndex = -1
[bool] $searching = $true
$source | Select-Object| ForEach-Object {
# if search check if it's a title line
if ($searching -and $_.Contains("Id") -and $_.Contains("Version") -and $_.Contains("Available")) {
$idIndex = $_.IndexOf("Id")
$currentIndex = $_.IndexOf("Version")
$availableIndex = $_.IndexOf("Available")
$searching = $false
} else {
# if in section and not a separator line, look for updates
if (-not $searching -and -not ($_.StartsWith("---") -and $_.EndsWith("---"))) {
# if the line is long enough and the line has an upgrade version
if ($_.Length -gt $availableIndex -and $_.SubString($availableIndex, $_.Length - $availableIndex).Trim() -match "\d+(\.\d+)?") {
#parse package update information
[string] $name = $_.SubString(0, $idIndex).Trim()
[string] $id = $_.SubString($idIndex, $currentIndex - $idIndex).Trim()
[string] $current = $_.SubString($currentIndex, $availableIndex - $currentIndex).Trim()
[string] $available = $_.SubString($availableIndex, $_.Length - $availableIndex).Trim()
$updates += [PSCustomObject] @{
Name = $name
Id = $id
Current = $Current
Available = $available
Skip = Skip-Update $name
}
} else {
#if not, assume you're done with this section and start searching for the next section
$idIndex = -1
$currentIndex = -1
$availableIndex = -1
$searching = $true
}
}
}
}
# check if updates exist
if ($updates.Length -lt 1) {
Write-Host
Write-Host no upgrades available
Write-Host
exit
}
# determine the widest package name for column padding
[int32] $namePadding = ($updates | ForEach-Object { $_.Name.Length } | Measure-Object -Maximum | Select-Object -Property Maximum).Maximum + 2
# determin the widest package version for column padding
[int32] $versionPadding = ($updates | ForEach-Object { [math]::Max($_.Current.Length, $_.Available.Length) } | Measure-Object -Maximum | Select-Object -Property Maximum).Maximum + 2
# const status width padding
[int32] $statusWidth = 42 # ironic? or cosmic? =P
#table variable size column titles
[string] $nameTitle = "Name"
[string] $currentTitle = "Current"
[string] $availableTitle = "Available"
[int32] $extraPadding = 2
# account for padding shorter than titles
$namePadding = [Math]::Max($namePadding, $nameTitle.Length + $extraPadding)
$versionPadding = [Math]::Max([Math]::Max($versionPadding, $currentTitle.Length + $extraPadding), $availableTitle.Length + $extraPadding)
# write table column titles
Write-Host
Write-Host $nameTitle.PadRight($namePadding, ' ') $currentTitle.PadLeft($versionPadding, ' ') $availableTitle.PadLeft($versionPadding, ' ') " Status"
Write-Host ("-" * $namePadding) ("-" * $versionPadding) ("-" * $versionPadding) " " ("-" * $statusWidth)
Write-Host
# process each package
foreach ($package in $updates) {
# output package info coumns
Write-Host $package.Name.PadRight($namePadding) $package.Current.PadLeft($versionPadding, ' ') $package.Available.PadLeft($versionPadding, ' ') "- " -NoNewline
# check if package should be skipped?
if ($package.Skip.Length -gt 0) {
# output package status
if ($upgrade) {
Write-Host "skipping" -ForegroundColor DarkYellow -NoNewline
} else {
Write-Host "skip" -ForegroundColor DarkYellow -NoNewline
}
Write-Host ";" $package.Skip
if ($upgrade) { Write-Host }
} else {
# check if package version is unknown
if ($package.Current -eq "Unknown") {
# output package status
Write-Host "skipping unknown version, check manually" -ForegroundColor Cyan
if ($upgrade) { Write-Host }
} else {
# temporary check if any properties have unicode garbled text
# this is only until winget piped output is fixed or supports power-shell natively
[bool] $garbled = (
$package.Name.EndsWith("ΓÇ") -or
$package.Id.EndsWith("ΓÇ") -or $package.Id.StartsWith("ª ") -or
$package.Current.EndsWith("ΓÇ") -or $package.Current.StartsWith("ª ") -or
$package.Available.EndsWith("ΓÇ") -or $package.Available.StartsWith("ª ")
)
# check if packages should upgraded
if ($upgrade) {
# check if any property is garbled
if ($garbled) {
# output package status and continue to next package
Write-Host "skipping, garbled identifiers prevents update" -ForegroundColor Yellow
} else {
# perform package upgrade, capture output
# [string[]] $result = winget upgrade --exact --name """$($package.Name)""" --id """$($package.Id)""" --version """$($package.Available)""" --source winget
[string[]] $result = winget upgrade --id $package.Id --source winget
# check if package upgrade succeeded and output package status accordingly
if ($lastExitCode -eq 0) {
Write-Host "updated" -ForegroundColor Blue
} else {
Write-Host "failed to update" -ForegroundColor Red
Write-Host
# demonstrate command used for upgrade
# Write-Host ">" winget upgrade --exact --name """$($package.Name)""" --id """$($package.Id)""" --version """$($package.Available)""" --source winget -ForegroundColor Yellow
Write-Host ">" winget upgrade --id $package.Id --source winget -ForegroundColor Yellow
Write-Host
# output captured output from package upgrade
foreach ($line in $result) {
Write-Host $line -ForegroundColor DarkGray
}
Write-Host
Write-Host "* might require admin privledges" -ForegroundColor Yellow
}
}
Write-Host
} else {
# check if any property is garbled and output package status accordingly
if ($garbled) {
Write-Host "skipping new version, garbled identifiers prevent update" -ForegroundColor Yellow
} else {
Write-Host "new version available" -ForegroundColor Blue
}
}
}
}
}
Write-Host
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment