mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-10-22 04:51:11 +00:00
Fix Show Installed Apps missing the app with the widest winget name (#5153)
* Fix Show Installed Apps missing the widest winget name winget pads each column of `winget list` to its widest value plus one space, so the row with the widest Name has a single space before its Id. The installed-app pattern required two or more, so that app was never ticked; for example vc2015_64/vc2015_32 when the VC++ v14 runtimes have the longest names. Require only one whitespace character on each side of the ID instead of two. There is still no start-of-line alternative, so an ID still cannot match at the start of a Name. Add a Pester case where the target app has the widest Name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Only accept a one-space winget Id at the Id column Loosening the pattern to one whitespace character let an ID match a word inside another package's Name, so "Some Git.Git Tool" ticked Git. Restore the original two-space pattern unchanged, so everything main matched still matches. Also accept the ID when it starts exactly at the Id column, taken from the header line above the dashes. That still catches the row with the widest Name or Id, cannot match inside an ASCII Name, and skips rows whose Name has combining marks or emoji, which are more characters than columns. The lookbehind keeps Windows PowerShell 5.1 as fast as main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cover widest WinGet IDs in both package manager modes --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
2 files changed
+75
-2
No files matched your search
@@ -52,14 +52,24 @@ Function Invoke-WinUtilCurrentSystem {
|
||||
}
|
||||
$installedProgramText = $installedProgramOutput -join "`n"
|
||||
|
||||
# winget pads each column to its widest value plus one space, so the row with the widest Name or Id
|
||||
# has only one space next to its Id. Take the Id column position from the header above the dashes.
|
||||
$idColumnStart = 0
|
||||
if ($installedProgramText -match '(?m)^(\S+[^\S\r\n]+)\S[^\r\n]*\r?\n-{3,}') {
|
||||
$idColumnStart = $Matches[1].Length
|
||||
}
|
||||
|
||||
$sync.configs.applicationsHashtable.GetEnumerator() | ForEach-Object {
|
||||
$packageId = (($_.Value.winget -split ";")[-1] -replace "^msstore:", "").Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($packageId) -or $packageId -eq "na") {
|
||||
return
|
||||
}
|
||||
|
||||
$packagePattern = "(?im)[^\S\r\n]{2,}$([regex]::Escape($packageId))(?=[^\S\r\n]{2,}|$)"
|
||||
if ($installedProgramText -match $packagePattern) {
|
||||
$escapedPackageId = [regex]::Escape($packageId)
|
||||
$packagePattern = "(?im)[^\S\r\n]{2,}$escapedPackageId(?=[^\S\r\n]{2,}|$)"
|
||||
# Skip rows whose Name has combining marks or emoji: they are more characters than columns and shift the offset
|
||||
$idColumnPattern = "(?im)(?<=^[^\r\n\p{M}\p{Cs}\p{Cf}]{$($idColumnStart - 1)}[^\S\r\n])$escapedPackageId(?=[^\S\r\n]|$)"
|
||||
if ($installedProgramText -match $packagePattern -or ($idColumnStart -and $installedProgramText -match $idColumnPattern)) {
|
||||
Write-Output $_.Key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,69 @@ Describe "Invoke-WinUtilCurrentSystem installed apps" {
|
||||
$script:wingetArguments | Should -Be @("list", "--accept-source-agreements", "--disable-interactivity")
|
||||
}
|
||||
|
||||
It "matches a package whose name is the widest in the winget list" {
|
||||
Mock winget {
|
||||
$global:LASTEXITCODE = 0
|
||||
@(
|
||||
"Name Id Version Source",
|
||||
"--------------------------------------------------------------------------------------------------------------",
|
||||
"Microsoft Visual C++ v14 Redistributable (x64) - 14.51.36247 Microsoft.VCRedist.2015+.x64 14.51.36247.0 winget",
|
||||
"Git Git.Git 2.0 winget"
|
||||
)
|
||||
}
|
||||
$script:sync.configs.applicationsHashtable["WPFInstallvc2015_64"] = [pscustomobject]@{ winget = "Microsoft.VCRedist.2015+.x64"; choco = "vcredist2015" }
|
||||
|
||||
$result = @(Invoke-WinUtilCurrentSystem -CheckBox "winget")
|
||||
|
||||
$result | Should -HaveCount 2
|
||||
$result | Should -Contain "WPFInstallvc2015_64"
|
||||
$result | Should -Contain "WPFInstallGit"
|
||||
$result | Should -Not -Contain "WPFInstallMissing"
|
||||
}
|
||||
|
||||
It "matches the widest ID when its version has only one preceding space (<Mode>)" -TestCases @(
|
||||
@{ Mode = "winget" }
|
||||
@{ Mode = "choco" }
|
||||
) {
|
||||
param($Mode)
|
||||
Mock winget {
|
||||
$global:LASTEXITCODE = 0
|
||||
@(
|
||||
"Name Id Version Source",
|
||||
"------------------------------------",
|
||||
"Git Git.Git 2.0 winget",
|
||||
"ChatGPT 9NT1R1C2HH7J 1.0 msstore"
|
||||
)
|
||||
}
|
||||
|
||||
$result = @(Invoke-WinUtilCurrentSystem -CheckBox $Mode)
|
||||
|
||||
$result | Should -HaveCount 2
|
||||
$result | Should -Contain "WPFInstallGit"
|
||||
$result | Should -Contain "WPFInstallChatGPT"
|
||||
$result | Should -Not -Contain "WPFInstallMissing"
|
||||
Should -Invoke -CommandName winget -Times 1 -Exactly
|
||||
}
|
||||
|
||||
It "matches IDs only in the Id column, not inside a package name" {
|
||||
Mock winget {
|
||||
$global:LASTEXITCODE = 0
|
||||
# "a" plus 16 combining accents is 17 characters but one column, which puts Git.Git at the Id column offset
|
||||
$accentedName = "a" + ([string][char]0x0301 * 16) + " Git.Git"
|
||||
@(
|
||||
"Name Id Version Source",
|
||||
"-------------------------------------------",
|
||||
"Some Git.Git Tool Other.Tool 1.0 winget",
|
||||
"$accentedName Third.Tool 1.0 winget"
|
||||
)
|
||||
}
|
||||
$script:sync.configs.applicationsHashtable["WPFInstallOther"] = [pscustomobject]@{ winget = "Other.Tool"; choco = "na" }
|
||||
|
||||
$result = @(Invoke-WinUtilCurrentSystem -CheckBox "winget")
|
||||
|
||||
$result | Should -Be @("WPFInstallOther")
|
||||
}
|
||||
|
||||
It "fails promptly when Winget cannot list applications" {
|
||||
Mock winget {
|
||||
$global:LASTEXITCODE = 1
|
||||
|
||||
Reference in new issue
Block a user