Cobalt.psm1
# Module created by Microsoft.PowerShell.Crescendo Function Get-WinGetSource { [CmdletBinding()] param( [Parameter()] [string]$Name ) BEGIN { $__PARAMETERMAP = @{ Name = @{ OriginalName = '--name='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) if ($output) { $output | ConvertFrom-Json } } } } } PROCESS { $__commandArgs = @( "source" "export" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Return WinGet package sources .PARAMETER Name Source Name #> } Function Register-WinGetSource { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$Name, [Parameter(Mandatory=$true)] [string]$Argument ) BEGIN { $__PARAMETERMAP = @{ Name = @{ OriginalName = '--name='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Argument = @{ OriginalName = '--arg='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) if ($output) { if ($output[-1] -ne 'Done') { Write-Error ($output -join "`r`n") } } } } } } PROCESS { $__commandArgs = @( "source" "add" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Register a new WinGet package source .PARAMETER Name Source Name .PARAMETER Argument Source Argument #> } Function Unregister-WinGetSource { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$Name ) BEGIN { $__PARAMETERMAP = @{ Name = @{ OriginalName = '--name='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) if ($output) { if ($output[-1] -match 'Did not find a source') { Write-Error ($output -join "`r`n") } } } } } } PROCESS { $__commandArgs = @( "source" "remove" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Unegister an existing WinGet package source .PARAMETER Name Source Name #> } Function Install-WinGetPackage { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$ID, [Parameter()] [switch]$Exact, [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$Source, [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$Version ) BEGIN { $__PARAMETERMAP = @{ ID = @{ OriginalName = '--id='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Exact = @{ OriginalName = '--exact'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } Source = @{ OriginalName = '--source='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Version = @{ OriginalName = '--version='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) $language = (Get-UICulture).Name $languageData = $( $hash = @{} $(try { # We have to trim the leading BOM for .NET's XML parser to correctly read Microsoft's own files - go figure ([xml](((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/winget-cli/master/Localization/Resources/$language/winget.resw" -ErrorAction Stop ).Content -replace "\uFEFF", ""))).root.data } catch { # Fall back to English if a locale file doesn't exist ( ('SearchName','Name'), ('SearchID','Id'), ('SearchVersion','Version'), ('AvailableHeader','Available'), ('SearchSource','Source'), ('ShowVersion','Version'), ('GetManifestResultVersionNotFound','No version found matching:'), ('InstallerFailedWithCode','Installer failed with exit code:'), ('UninstallFailedWithCode','Uninstall failed with exit code:'), ('AvailableUpgrades','upgrades available.') ) | ForEach-Object {[pscustomobject]@{name = $_[0]; value = $_[1]}} }) | ForEach-Object { # Convert the array into a hashtable $hash[$_.name] = $_.value } $hash ) if ($output) { if ($output -match $languageData.InstallerFailedWithCode) { # Only show output that matches or comes after the 'failed' keyword Write-Error ($output[$output.IndexOf($($output -match $languageData.InstallerFailedWithCode | Select-Object -First 1))..($output.Length-1)] -join "`r`n") } else { $output | ForEach-Object { if ($_ -match 'Found .+ \[(?<id>[\S]+)\] Version (?<version>[\S]+)' -and $Matches.id -and $Matches.version) { [pscustomobject]@{ ID = $Matches.id Version = $Matches.version } } } } } } } } } PROCESS { $__commandArgs = @( "install" "--accept-package-agreements" "--accept-source-agreements" "--silent" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Install a new package with WinGet .PARAMETER ID Package ID .PARAMETER Exact Search by exact package name .PARAMETER Source Package Source .PARAMETER Version Package Version #> } Function Get-WinGetPackage { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$ID, [Parameter()] [switch]$Exact, [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$Source ) BEGIN { $__PARAMETERMAP = @{ ID = @{ OriginalName = '--id='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Exact = @{ OriginalName = '--exact'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } Source = @{ OriginalName = '--source='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) $language = (Get-UICulture).Name $languageData = $( $hash = @{} $(try { # We have to trim the leading BOM for .NET's XML parser to correctly read Microsoft's own files - go figure ([xml](((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/winget-cli/master/Localization/Resources/$language/winget.resw" -ErrorAction Stop ).Content -replace "\uFEFF", ""))).root.data } catch { # Fall back to English if a locale file doesn't exist ( ('SearchName','Name'), ('SearchID','Id'), ('SearchVersion','Version'), ('AvailableHeader','Available'), ('SearchSource','Source'), ('ShowVersion','Version'), ('GetManifestResultVersionNotFound','No version found matching:'), ('InstallerFailedWithCode','Installer failed with exit code:'), ('UninstallFailedWithCode','Uninstall failed with exit code:'), ('AvailableUpgrades','upgrades available.') ) | ForEach-Object {[pscustomobject]@{name = $_[0]; value = $_[1]}} }) | ForEach-Object { # Convert the array into a hashtable $hash[$_.name] = $_.value } $hash ) $nameHeader = $output -Match "^$($languageData.SearchName)" if ($nameHeader) { $headerLine = $output.IndexOf(($nameHeader | Select-Object -First 1)) if ($headerLine -ne -1) { $idIndex = $output[$headerLine].IndexOf(($languageData.SearchID)) $versionIndex = $output[$headerLine].IndexOf(($languageData.SearchVersion)) $availableIndex = $output[$headerLine].IndexOf(($languageData.AvailableHeader)) $sourceIndex = $output[$headerLine].IndexOf(($languageData.SearchSource)) # Stop gathering version data at the 'Available' column if it exists, if not continue on to the 'Source' column (if it exists) $versionEndIndex = $( if ($availableIndex -ne -1) { $availableIndex } else { $sourceIndex } ) # Only attempt to parse output if it contains a 'version' column if ($versionIndex -ne -1) { # The -replace cleans up errant characters that come from WinGet's poor treatment of truncated columnar output ($output | Select-String -Pattern $languageData.AvailableUpgrades,'--include-unknown' -NotMatch) -replace '[^i\p{IsBasicLatin}]',' ' | Select-Object -Skip ($headerLine+2) | ForEach-Object { Remove-Variable -Name 'package' -ErrorAction SilentlyContinue $package = [ordered]@{ ID = $_.SubString($idIndex,$versionIndex-$idIndex).Trim() } if ($package) { # I'm so sorry, blame WinGet # If neither the 'Available' or 'Source' column exist, gather version data to the end of the string $package.Version = $( if ($versionEndIndex -ne -1) { $_.SubString($versionIndex,$versionEndIndex-$versionIndex) } else { $_.SubString($versionIndex) } ).Trim() -replace '[^\.\d]' # Only attempt to add 'Available Version' data if the column exists if ($availableIndex -ne -1) { $package.Available = $( if ($sourceIndex -ne -1) { $_.SubString($availableIndex,$sourceIndex-$availableIndex) } else { $_.SubString($availableIndex) } ).Trim() -replace '[^\.\d]' } # If the 'Source' column was included in the output, include it in our output, too if (($sourceIndex -ne -1) -And ($_.Length -ge $sourceIndex)) { $package.Source = $_.SubString($sourceIndex).Trim() -split ' ' | Select-Object -Last 1 } [pscustomobject]$package } } } } } } } } } PROCESS { $__commandArgs = @( "list" "--accept-source-agreements" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Get a list of installed WinGet packages .PARAMETER ID Package ID .PARAMETER Exact Search by exact package name .PARAMETER Source Package Source #> } Function Find-WinGetPackage { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$ID, [Parameter()] [switch]$Exact, [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$Source ) BEGIN { $__PARAMETERMAP = @{ ID = @{ OriginalName = '--id='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Exact = @{ OriginalName = '--exact'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } Source = @{ OriginalName = '--source='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) $language = (Get-UICulture).Name $languageData = $( $hash = @{} $(try { # We have to trim the leading BOM for .NET's XML parser to correctly read Microsoft's own files - go figure ([xml](((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/winget-cli/master/Localization/Resources/$language/winget.resw" -ErrorAction Stop ).Content -replace "\uFEFF", ""))).root.data } catch { # Fall back to English if a locale file doesn't exist ( ('SearchName','Name'), ('SearchID','Id'), ('SearchVersion','Version'), ('AvailableHeader','Available'), ('SearchSource','Source'), ('ShowVersion','Version'), ('GetManifestResultVersionNotFound','No version found matching:'), ('InstallerFailedWithCode','Installer failed with exit code:'), ('UninstallFailedWithCode','Uninstall failed with exit code:'), ('AvailableUpgrades','upgrades available.') ) | ForEach-Object {[pscustomobject]@{name = $_[0]; value = $_[1]}} }) | ForEach-Object { # Convert the array into a hashtable $hash[$_.name] = $_.value } $hash ) $nameHeader = $output -Match "^$($languageData.SearchName)" if ($nameHeader) { $headerLine = $output.IndexOf(($nameHeader | Select-Object -First 1)) if ($headerLine -ne -1) { $idIndex = $output[$headerLine].IndexOf(($languageData.SearchID)) $versionIndex = $output[$headerLine].IndexOf(($languageData.SearchVersion)) $availableIndex = $output[$headerLine].IndexOf(($languageData.AvailableHeader)) $sourceIndex = $output[$headerLine].IndexOf(($languageData.SearchSource)) # Stop gathering version data at the 'Available' column if it exists, if not continue on to the 'Source' column (if it exists) $versionEndIndex = $( if ($availableIndex -ne -1) { $availableIndex } else { $sourceIndex } ) # Only attempt to parse output if it contains a 'version' column if ($versionIndex -ne -1) { # The -replace cleans up errant characters that come from WinGet's poor treatment of truncated columnar output ($output | Select-String -Pattern $languageData.AvailableUpgrades,'--include-unknown' -NotMatch) -replace '[^i\p{IsBasicLatin}]',' ' | Select-Object -Skip ($headerLine+2) | ForEach-Object { Remove-Variable -Name 'package' -ErrorAction SilentlyContinue $package = [ordered]@{ ID = $_.SubString($idIndex,$versionIndex-$idIndex).Trim() } if ($package) { # I'm so sorry, blame WinGet # If neither the 'Available' or 'Source' column exist, gather version data to the end of the string $package.Version = $( if ($versionEndIndex -ne -1) { $_.SubString($versionIndex,$versionEndIndex-$versionIndex) } else { $_.SubString($versionIndex) } ).Trim() -replace '[^\.\d]' # Only attempt to add 'Available Version' data if the column exists if ($availableIndex -ne -1) { $package.Available = $( if ($sourceIndex -ne -1) { $_.SubString($availableIndex,$sourceIndex-$availableIndex) } else { $_.SubString($availableIndex) } ).Trim() -replace '[^\.\d]' } # If the 'Source' column was included in the output, include it in our output, too if (($sourceIndex -ne -1) -And ($_.Length -ge $sourceIndex)) { $package.Source = $_.SubString($sourceIndex).Trim() -split ' ' | Select-Object -Last 1 } [pscustomobject]$package } } } } } } } } } PROCESS { $__commandArgs = @( "search" "--accept-source-agreements" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Find a list of available WinGet packages .PARAMETER ID Package ID .PARAMETER Exact Search by exact package name .PARAMETER Source Package Source #> } Function Update-WinGetPackage { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$ID, [Parameter()] [switch]$Exact, [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$Source, [Parameter()] [switch]$All ) BEGIN { $__PARAMETERMAP = @{ ID = @{ OriginalName = '--id='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Exact = @{ OriginalName = '--exact'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } Source = @{ OriginalName = '--source='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } All = @{ OriginalName = '--all'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) $language = (Get-UICulture).Name $languageData = $( $hash = @{} $(try { # We have to trim the leading BOM for .NET's XML parser to correctly read Microsoft's own files - go figure ([xml](((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/winget-cli/master/Localization/Resources/$language/winget.resw" -ErrorAction Stop ).Content -replace "\uFEFF", ""))).root.data } catch { # Fall back to English if a locale file doesn't exist ( ('SearchName','Name'), ('SearchID','Id'), ('SearchVersion','Version'), ('AvailableHeader','Available'), ('SearchSource','Source'), ('ShowVersion','Version'), ('GetManifestResultVersionNotFound','No version found matching:'), ('InstallerFailedWithCode','Installer failed with exit code:'), ('UninstallFailedWithCode','Uninstall failed with exit code:'), ('AvailableUpgrades','upgrades available.') ) | ForEach-Object {[pscustomobject]@{name = $_[0]; value = $_[1]}} }) | ForEach-Object { # Convert the array into a hashtable $hash[$_.name] = $_.value } $hash ) if ($output) { if ($output -match $languageData.InstallerFailedWithCode) { # Only show output that matches or comes after the 'failed' keyword Write-Error ($output[$output.IndexOf($($output -match $languageData.InstallerFailedWithCode | Select-Object -First 1))..($output.Length-1)] -join "`r`n") } else { $output | ForEach-Object { if ($_ -match 'Found .+ \[(?<id>[\S]+)\] Version (?<version>[\S]+)' -and $Matches.id -and $Matches.version) { [pscustomobject]@{ ID = $Matches.id Version = $Matches.version } } } } } } } } } PROCESS { $__commandArgs = @( "upgrade" "--accept-source-agreements" "--silent" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Updates an installed package to the latest version .PARAMETER ID Package ID .PARAMETER Exact Search by exact package name .PARAMETER Source Package Source .PARAMETER All Upgrade all packages #> } Function Uninstall-WinGetPackage { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$ID, [Parameter()] [switch]$Exact, [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$Source ) BEGIN { $__PARAMETERMAP = @{ ID = @{ OriginalName = '--id='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Exact = @{ OriginalName = '--exact'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } Source = @{ OriginalName = '--source='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) $language = (Get-UICulture).Name $languageData = $( $hash = @{} $(try { # We have to trim the leading BOM for .NET's XML parser to correctly read Microsoft's own files - go figure ([xml](((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/winget-cli/master/Localization/Resources/$language/winget.resw" -ErrorAction Stop ).Content -replace "\uFEFF", ""))).root.data } catch { # Fall back to English if a locale file doesn't exist ( ('SearchName','Name'), ('SearchID','Id'), ('SearchVersion','Version'), ('AvailableHeader','Available'), ('SearchSource','Source'), ('ShowVersion','Version'), ('GetManifestResultVersionNotFound','No version found matching:'), ('InstallerFailedWithCode','Installer failed with exit code:'), ('UninstallFailedWithCode','Uninstall failed with exit code:'), ('AvailableUpgrades','upgrades available.') ) | ForEach-Object {[pscustomobject]@{name = $_[0]; value = $_[1]}} }) | ForEach-Object { # Convert the array into a hashtable $hash[$_.name] = $_.value } $hash ) if ($output) { if ($output -match $languageData.UninstallFailedWithCode) { # Only show output that matches or comes after the 'failed' keyword Write-Error ($output[$output.IndexOf($($output -match $languageData.UninstallFailedWithCode | Select-Object -First 1))..($output.Length-1)] -join "`r`n") } } } } } } PROCESS { $__commandArgs = @( "uninstall" "--accept-source-agreements" "--silent" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Uninstall an existing package with WinGet .PARAMETER ID Package ID .PARAMETER Exact Search by exact package name .PARAMETER Source Package Source #> } Function Get-WinGetPackageInfo { [CmdletBinding(DefaultParameterSetName='Default')] param( [Parameter(Position=0,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [Parameter(ParameterSetName="Default")] [Parameter(ParameterSetName="Versions")] [string]$ID, [Parameter()] [Parameter(ParameterSetName="Default")] [Parameter(ParameterSetName="Versions")] [switch]$Exact, [Parameter(ValueFromPipelineByPropertyName=$true)] [Parameter(ParameterSetName="Default")] [Parameter(ParameterSetName="Versions")] [string]$Version, [Parameter(ValueFromPipelineByPropertyName=$true)] [Parameter(ParameterSetName="Default")] [Parameter(ParameterSetName="Versions")] [string]$Source, [Parameter(ParameterSetName='Versions')] [switch]$Versions ) BEGIN { $__PARAMETERMAP = @{ ID = @{ OriginalName = '--id='; OriginalPosition = '0'; Position = '0'; ParameterType = [string]; NoGap = $True } Exact = @{ OriginalName = '--exact'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } Version = @{ OriginalName = '--version='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Source = @{ OriginalName = '--source='; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [string]; NoGap = $True } Versions = @{ OriginalName = '--versions'; OriginalPosition = '0'; Position = '2147483647'; ParameterType = [switch]; NoGap = $False } } $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ( $output ) $packageInfo = @{} $output | Select-String -AllMatches -Pattern '^\s*([\w\s]+):\s(.+)$' | ForEach-Object -MemberName Matches | ForEach-Object{ $match = ($_.Groups | Select-Object -Skip 1).Value $packageInfo.add($match[0],$match[1]) } $packageInfo } } Versions = @{ StreamOutput = $False; Handler = { param ($output) $language = (Get-UICulture).Name $languageData = $( $hash = @{} $(try { # We have to trim the leading BOM for .NET's XML parser to correctly read Microsoft's own files - go figure ([xml](((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/winget-cli/master/Localization/Resources/$language/winget.resw" -ErrorAction Stop ).Content -replace "\uFEFF", ""))).root.data } catch { # Fall back to English if a locale file doesn't exist ( ('SearchName','Name'), ('SearchID','Id'), ('SearchVersion','Version'), ('AvailableHeader','Available'), ('SearchSource','Source'), ('ShowVersion','Version'), ('GetManifestResultVersionNotFound','No version found matching:'), ('InstallerFailedWithCode','Installer failed with exit code:'), ('UninstallFailedWithCode','Uninstall failed with exit code:'), ('AvailableUpgrades','upgrades available.') ) | ForEach-Object {[pscustomobject]@{name = $_[0]; value = $_[1]}} }) | ForEach-Object { # Convert the array into a hashtable $hash[$_.name] = $_.value } $hash ) if ($output) { if ($output | Select-String -Pattern $languageData.GetManifestResultVersionNotFound) { # Only show output that matches or comes after the 'failed' keyword Write-Error ($output[$output.IndexOf($($output | Select-String -Pattern $languageData.GetManifestResultVersionNotFound | Select-Object -First 1))..($output.Length-1)] -join "`r`n") } else { $versionHeader = $output -Match "^$($languageData.ShowVersion)" if ($versionHeader) { $headerLine = $output.IndexOf(($versionHeader | Select-Object -First 1)) if ($headerLine -ne -1) { $output | Select-Object -Skip ($headerLine+2) } } } } } } } } PROCESS { $__commandArgs = @( "show" "--accept-source-agreements" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Shows information on a specific WinGet package .PARAMETER ID Package ID .PARAMETER Exact Search by exact package name .PARAMETER Version Package Version .PARAMETER Source Package Source .PARAMETER Versions Show available versions of the package #> } Function Get-WinGetPackageUpdate { [CmdletBinding()] param( ) BEGIN { $__PARAMETERMAP = @{} $__outputHandlers = @{ Default = @{ StreamOutput = $False; Handler = { param ($output) $language = (Get-UICulture).Name $languageData = $( $hash = @{} $(try { # We have to trim the leading BOM for .NET's XML parser to correctly read Microsoft's own files - go figure ([xml](((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/winget-cli/master/Localization/Resources/$language/winget.resw" -ErrorAction Stop ).Content -replace "\uFEFF", ""))).root.data } catch { # Fall back to English if a locale file doesn't exist ( ('SearchName','Name'), ('SearchID','Id'), ('SearchVersion','Version'), ('AvailableHeader','Available'), ('SearchSource','Source'), ('ShowVersion','Version'), ('GetManifestResultVersionNotFound','No version found matching:'), ('InstallerFailedWithCode','Installer failed with exit code:'), ('UninstallFailedWithCode','Uninstall failed with exit code:'), ('AvailableUpgrades','upgrades available.') ) | ForEach-Object {[pscustomobject]@{name = $_[0]; value = $_[1]}} }) | ForEach-Object { # Convert the array into a hashtable $hash[$_.name] = $_.value } $hash ) $nameHeader = $output -Match "^$($languageData.SearchName)" if ($nameHeader) { $headerLine = $output.IndexOf(($nameHeader | Select-Object -First 1)) if ($headerLine -ne -1) { $idIndex = $output[$headerLine].IndexOf(($languageData.SearchID)) $versionIndex = $output[$headerLine].IndexOf(($languageData.SearchVersion)) $availableIndex = $output[$headerLine].IndexOf(($languageData.AvailableHeader)) $sourceIndex = $output[$headerLine].IndexOf(($languageData.SearchSource)) # Stop gathering version data at the 'Available' column if it exists, if not continue on to the 'Source' column (if it exists) $versionEndIndex = $( if ($availableIndex -ne -1) { $availableIndex } else { $sourceIndex } ) # Only attempt to parse output if it contains a 'version' column if ($versionIndex -ne -1) { # The -replace cleans up errant characters that come from WinGet's poor treatment of truncated columnar output ($output | Select-String -Pattern $languageData.AvailableUpgrades,'--include-unknown' -NotMatch) -replace '[^i\p{IsBasicLatin}]',' ' | Select-Object -Skip ($headerLine+2) | ForEach-Object { Remove-Variable -Name 'package' -ErrorAction SilentlyContinue $package = [ordered]@{ ID = $_.SubString($idIndex,$versionIndex-$idIndex).Trim() } if ($package) { # I'm so sorry, blame WinGet # If neither the 'Available' or 'Source' column exist, gather version data to the end of the string $package.Version = $( if ($versionEndIndex -ne -1) { $_.SubString($versionIndex,$versionEndIndex-$versionIndex) } else { $_.SubString($versionIndex) } ).Trim() -replace '[^\.\d]' # Only attempt to add 'Available Version' data if the column exists if ($availableIndex -ne -1) { $package.Available = $( if ($sourceIndex -ne -1) { $_.SubString($availableIndex,$sourceIndex-$availableIndex) } else { $_.SubString($availableIndex) } ).Trim() -replace '[^\.\d]' } # If the 'Source' column was included in the output, include it in our output, too if (($sourceIndex -ne -1) -And ($_.Length -ge $sourceIndex)) { $package.Source = $_.SubString($sourceIndex).Trim() -split ' ' | Select-Object -Last 1 } [pscustomobject]$package } } } } } } } } } PROCESS { $__commandArgs = @( "upgrade" ) $__boundparms = $PSBoundParameters $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $PSBoundParameters[$_.Name]}).ForEach({$PSBoundParameters[$_.Name] = [switch]::new($false)}) if ($PSBoundParameters["Debug"]){wait-debugger} foreach ($paramName in $PSBoundParameters.Keys|Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { $value = $PSBoundParameters[$paramName] $param = $__PARAMETERMAP[$paramName] if ($param) { if ( $value -is [switch] ) { $__commandArgs += if ( $value.IsPresent ) { $param.OriginalName } else { $param.DefaultMissingValue } } elseif ( $param.NoGap ) { $__commandArgs += "{0}""{1}""" -f $param.OriginalName, $value } else { $__commandArgs += $param.OriginalName; $__commandArgs += $value |Foreach-Object {$_}} } } $__commandArgs = $__commandArgs|Where-Object {$_} if ($PSBoundParameters["Debug"]){wait-debugger} if ( $PSBoundParameters["Verbose"]) { Write-Verbose -Verbose -Message WinGet $__commandArgs | Write-Verbose -Verbose } $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] if (! $__handlerInfo ) { $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present } $__handler = $__handlerInfo.Handler if ( $PSCmdlet.ShouldProcess("WinGet")) { if ( $__handlerInfo.StreamOutput ) { & "WinGet" $__commandArgs | & $__handler } else { $result = & "WinGet" $__commandArgs & $__handler $result } } } # end PROCESS <# .DESCRIPTION Get a list of installed WinGet packages #> } Export-ModuleMember -Function Get-WinGetSource, Register-WinGetSource, Unregister-WinGetSource, Install-WinGetPackage, Get-WinGetPackage, Find-WinGetPackage, Update-WinGetPackage, Uninstall-WinGetPackage, Get-WinGetPackageInfo, Get-WinGetPackageUpdate |