winget-cli icon indicating copy to clipboard operation
winget-cli copied to clipboard

Add ability to list installed packages by `--installer-type` with `winget list`

Open o-l-a-v opened this issue 2 years ago • 2 comments

Description of the new feature / enhancement

Just now I wondered what portable packages I've installed with Winget, and found that winget list doesn't really have such filtering capabilities.

Here's the current options with winget list:

  • https://learn.microsoft.com/en-us/windows/package-manager/winget/list
  • https://github.com/microsoft/winget-cli/blob/master/doc/windows/package-manager/winget/list.md

Here's the supported installer types:

  • https://learn.microsoft.com/en-us/windows/package-manager/winget/#supported-installer-formats

Here's an example portable package:

  • https://github.com/microsoft/winget-pkgs/blob/master/manifests/g/Gyan/FFmpeg/6.1/Gyan.FFmpeg.installer.yaml

Proposed technical implementation details

Add option to specify --installer-type with winget list.

o-l-a-v avatar Dec 20 '23 19:12 o-l-a-v

Referencing a related issue:

  • #1155

mdanish-kh avatar Dec 20 '23 20:12 mdanish-kh

Here's a powershell script which takes a stab this, with a bunch of help from github copilot:

# Check if the Microsoft.WinGet.Client module is installed, and install it if not
if (-not (Get-Module -ListAvailable -Name Microsoft.WinGet.Client)) {
    Write-Host "Microsoft.WinGet.Client module not found. Installing..."
    Install-Module -Name Microsoft.WinGet.Client -Scope CurrentUser -Force -AllowClobber
}

Import-Module Microsoft.WinGet.Client

# Function to get the installer type of a package
function Get-InstallerType {
    param (
        [string]$packageId
    )
    # Run the winget show command and capture the output
    $output = winget show $packageId
    # Parse the output to find the installer type
    $installerType = $output | Select-String -Pattern "Installer type" | ForEach-Object {
        $_ -replace "Installer type\s*:\s*", ""
    }
    return $installerType
}

# Function to get information about non-standard IDs from the registry
function Get-NonStandardPackageInfo {
    param (
        [string]$packageId
    )

    $installerType = "Unknown"
    # Extract the unique identifier at the end of the packageId
    if ($packageId -match 'ARP\\Machine\\X64\\{(.+?)}') {
        $uniqueId = $matches[1]
    } elseif ($packageId -match 'ARP\\Machine\\X86\\(.+?)') {
        $uniqueId = $matches[1]
    } else {
        $uniqueId = $packageId -replace '.*\\', ''
    }

    # Add braces if the unique identifier is a GUID
    if ($uniqueId -match '^[0-9a-fA-F-]{36}$') {
        $uniqueId = "{$uniqueId}"
    }

    $registryPaths = @(
        "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$uniqueId",
        "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$uniqueId",
        "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$uniqueId"
    )

    foreach ($registryPath in $registryPaths) {
        try {
            $packageInfo = Get-ItemProperty -Path $registryPath -ErrorAction Stop
            if ($packageInfo) {
                $installerType = "MSI"  # Default to MSI for ARP entries, adjust as needed
                break
            }
        } catch {
            Write-Host "Registry path not found: $registryPath"
        }
    }

    return $installerType
}

# Function to get information about MSIX packages from the registry
function Get-MSIXPackageInfo {
    param (
        [string]$packageId
    )

    $installerType = "Unknown"
    $uniqueId = $packageId -replace 'MSIX\\', ''

    $registryPath = "HKLM:\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\$uniqueId"

    try {
        $packageInfo = Get-ItemProperty -Path $registryPath -ErrorAction Stop
        if ($packageInfo) {
            $installerType = "MSIX"
        }
    } catch {
        Write-Host "Registry path not found: $registryPath"
    }

    return $installerType
}

# Get the list of installed packages
$installedPackages = Get-WinGetPackage

# Loop through each installed package
foreach ($package in $installedPackages) {
    Write-Host "Processing package: $($package.Id)"

    # Determine the installer type
    if ($package.Id -match "^ARP") {
        $installerType = Get-NonStandardPackageInfo -packageId $package.Id
    } elseif ($package.Id -match "^MSIX") {
        $installerType = Get-MSIXPackageInfo -packageId $package.Id
    } else {
        try {
            # Get the installer type for the package and trim the result
            $installerType = (Get-InstallerType -packageId $package.Id).Trim()
        } catch {
            # Log the error and set the installer type to "Error"
            Write-Host "Error processing package $($package.Id): $_"
            $installerType = "Error"
        }
    }

    Write-Host "Installer Type: $installerType"

    # Print the package details
    Write-Output "Name: $($package.Name), Id: $($package.Id), Installer Type: $installerType"
}

jcrben avatar Oct 14 '24 05:10 jcrben