Private/Select-Win32ToolkitOlderVersion.ps1
|
function Select-Win32ToolkitOlderVersion { <# .SYNOPSIS Filters a newest-first winget version list to the versions strictly OLDER than CurrentVersion. .DESCRIPTION Replaces the old exact-string-only filtering in Get-WingetVersions, whose miss-fallback returned the FULL list (so the Update test could install an equal/newer "old baseline"). Strategy, in order: 1. Exact match of CurrentVersion in the list (ordinal, then case-insensitive) — winget lists newest-first, so everything after the match is older. 2. Numeric fallback: normalize both sides (strip a leading 'v'/'V' and any -prerelease/+build suffix, pad a bare integer to 'N.0') and keep entries that parse to a [version] strictly lower than CurrentVersion. Non-parseable entries are dropped (conservative: never risk a newer baseline). 3. If CurrentVersion is not in the list AND cannot be parsed, THROW — callers must never fall back to an unfiltered list. The error points the operator at -SpecificVersion. Always returns a real array (unary-comma guarded), so a single-result list never unrolls to a string at the call site (that bug made '-VersionsBack 1' index into the version STRING and produce a one-character version). .PARAMETER Versions Version strings as listed by winget, newest first. .PARAMETER CurrentVersion The packaged version; only strictly-older entries are returned. .EXAMPLE Select-Win32ToolkitOlderVersion -Versions @('2.55.0','2.54.0','2.53.0') -CurrentVersion '2.55.0' # -> @('2.54.0','2.53.0') #> [CmdletBinding()] [OutputType([string[]])] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [string[]]$Versions, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$CurrentVersion ) function ConvertTo-ComparableVersion { param([string]$Value) $t = $Value.Trim() -replace '^[vV]', '' $t = ($t -split '[-+]', 2)[0].Trim() # strip -prerelease / +build metadata # Right-pad numeric versions to 4 components so '1.2' == '1.2.0' == '1.2.0.0' ([version] # treats missing parts as -1, which would classify '1.2' as strictly older than '1.2.0' # and re-admit an equal-in-practice baseline under formatting drift). if ($t -match '^\d+(\.\d+){0,3}$') { $parts = @($t -split '\.') while ($parts.Count -lt 4) { $parts += '0' } $t = $parts -join '.' } $parsed = $null if ([version]::TryParse($t, [ref]$parsed)) { return $parsed } return $null } # NOTE: results are emitted plainly — call sites must wrap in @() (single results otherwise # unroll to a scalar string, which is exactly the -VersionsBack char-indexing bug). $list = @($Versions | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) if ($list.Count -eq 0) { return @() } # 1) Exact position in the newest-first list. $idx = [Array]::IndexOf([string[]]$list, $CurrentVersion) if ($idx -lt 0) { for ($i = 0; $i -lt $list.Count; $i++) { if ($list[$i] -ieq $CurrentVersion) { $idx = $i; break } } } if ($idx -ge 0) { if (($idx + 1) -ge $list.Count) { return @() } return @($list[($idx + 1)..($list.Count - 1)]) } # 2) Numeric strictly-older fallback (current version pulled from winget, formatting drift, ...). $current = ConvertTo-ComparableVersion $CurrentVersion if ($current) { Write-Warning "Current version '$CurrentVersion' is not in the winget list — filtering numerically to strictly-older versions." $parseable = 0 $older = foreach ($v in $list) { $pv = ConvertTo-ComparableVersion $v if ($pv) { $parseable++ if ($pv -lt $current) { $v } } else { Write-Verbose "Dropped non-comparable version entry: '$v'" } } if ($parseable -eq 0) { # Older versions exist but none is comparable — say so honestly instead of letting the # caller report the misleading "no versions older than X are available". throw "None of the $($list.Count) listed versions for this package are comparable version strings, so versions older than '$CurrentVersion' cannot be identified. Use -SpecificVersion to pick the baseline explicitly." } return @($older) } # 3) No safe way to filter — refuse rather than hand back an unfiltered list. throw "Cannot determine which versions are older than '$CurrentVersion': it is not in the winget list and is not a comparable version string. Use -SpecificVersion to pick the baseline explicitly." } |