From 655bbb788d8b69cc9de0c9ad9dd34e75884e3b7b Mon Sep 17 00:00:00 2001 From: Ethan Bergstrom <13603162+ethanbergstrom@users.noreply.github.com> Date: Sat, 12 Mar 2022 19:49:55 -0600 Subject: [PATCH] Package metadata and AllVersions support (#9) --- .github/workflows/CI.yml | 2 +- CHANGELOG.md | 6 +++ Install-WinGet.ps1 | 49 ---------------------- README.md | 5 ++- Test/WinGet.Unit.Tests.ps1 | 48 ++++++++++++++++----- src/WinGet.psd1 | 4 +- src/WinGet.psm1 | 1 + src/private/ConvertTo-SoftwareIdentity.ps1 | 3 ++ src/private/Find-WinGetPackage.ps1 | 20 ++++++++- src/private/Test-PackageVersion.ps1 | 1 - src/public/Get-InstalledPackage.ps1 | 7 ++-- src/public/Install-Package.ps1 | 14 +------ 12 files changed, 78 insertions(+), 82 deletions(-) delete mode 100644 Install-WinGet.ps1 diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1f5be50..890c6f4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -34,7 +34,7 @@ jobs: path: C:\Users\runneradmin\Documents\PowerShell\Modules\WinGet\ - name: Install WinGet shell: pwsh - run: .\Install-WinGet.ps1 + run: Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/ethanbergstrom/Cobalt/master/Install-WinGet.ps1')) - name: Install Cobalt run: Install-Module Cobalt -Force - name: Test with Pester diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bbd335..09f2339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased +## 0.0.7 - 2022-03-12 - Additional Package Details +### Added +* Include package summary and download URL in package data +* Support for the Find-Package `AllVersions` parameter +* Support for the `RequiredVersion`, `MinimumVersion`, and `MaximumVersion` parameters across multiple cmdlets + ## 0.0.6 - 2022-02-06 - Force dependency checks ### Fixed * Installed package version checks diff --git a/Install-WinGet.ps1 b/Install-WinGet.ps1 deleted file mode 100644 index 23ea06e..0000000 --- a/Install-WinGet.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -Install-Module NtObjectManager -Force -Import-Module appx -UseWindowsPowerShell - -# GitHub release information -$appxPackageName = 'Microsoft.DesktopAppInstaller' -$msWinGetLatestReleaseURL = 'https://github.com/microsoft/winget-cli/releases/latest' -$msWinGetMSIXBundlePath = ".\$appxPackageName.msixbundle" -$msWinGetLicensePath = ".\$appxPackageName.license.xml" - -# Workaround for no Microsoft Store on Windows Server - I dont know a great way to source this information dynamically -$architecture = 'x64' -$msStoreDownloadAPIURL = 'https://store.rg-adguard.net/api/GetFiles' -$msWinGetStoreURL = 'https://www.microsoft.com/en-us/p/app-installer/9nblggh4nns1' -$msVCLibPattern = "*Microsoft.VCLibs*UWPDesktop*$architecture*appx*" -$msVCLibDownloadPath = '.\Microsoft.VCLibs.UWPDesktop.appx' -$msWinGetExe = 'winget' -$wingetExecAliasPath = "C:\Windows\System32\$msWinGetExe.exe" - -$msWinGetLatestRelease = Invoke-WebRequest -Uri $msWinGetLatestReleaseURL - -# Download the latest MSIX bundle and matching license from GitHub -$msWinGetLatestRelease.links | - Where-Object href -like '*msixbundle' | - Select-Object -Property @{ - Name = 'URI'; - Expression = {$msWinGetLatestRelease.BaseResponse.headers.Server.Product.Name+$_.href} - } | ForEach-Object {Invoke-WebRequest -Uri $_.URI -OutFile $msWinGetMSIXBundlePath} - -$msWinGetLatestRelease.links | - Where-Object href -Like '*License*xml' | - Select-Object -Property @{ - Name = 'URI'; - Expression = {$msWinGetLatestRelease.BaseResponse.headers.Server.Product.Name+$_.href} - } | ForEach-Object {Invoke-WebRequest -Uri $_.URI -OutFile $msWinGetLicensePath} - -# Download the VC++ redistrubable for UWP apps from the Microsoft Store -(Invoke-WebRequest -Uri $msStoreDownloadAPIURL -Method Post -Form @{type='url'; url=$msWinGetStoreURL; ring='Retail'; lang='en-US'}).links | - Where-Object OuterHTML -Like $msVCLibPattern | - Sort-Object outerHTML -Descending | - Select-Object -First 1 -ExpandProperty href | - ForEach-Object {Invoke-WebRequest -Uri $_ -OutFile $msVCLibDownloadPath} - -# Install the WinGet and it's VC++ .msix with the downloaded license file -Add-AppProvisionedPackage -Online -PackagePath $msWinGetMSIXBundlePath -DependencyPackagePath $msVCLibDownloadPath -LicensePath $msWinGetLicensePath - -# Force the creation of the execution alias with NtObjectManager, since one isn't generated automatically in the current user session -$appxPackage = Get-AppxPackage Microsoft.DesktopAppInstaller -$wingetTarget = Join-Path -Path $appxPackage.InstallLocation -ChildPath ((Get-AppxPackageManifest $appxPackage).Package.Applications.Application | Where-Object Id -eq $msWinGetExe | Select-Object -ExpandProperty Executable) -NtObjectManager\Set-ExecutionAlias -Path $wingetExecAliasPath -PackageName ($appxPackage.PackageFamilyName) -EntryPoint "$($appxPackage.PackageFamilyName)!$msWinGetExe" -Target $wingetTarget -AppType Desktop -Version 3 diff --git a/README.md b/README.md index f9e6df3..d8a53ef 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ Find-Package OpenJS.NodeJS -Provider WinGet Find-Package Mozilla.Firefox -Provider WinGet ``` +### Find all available versions of a package +```PowerShell +Find-Package Mozilla.Firefox -Provider WinGet -AllVersions +``` ### Install a package ```PowerShell @@ -166,7 +170,6 @@ If using the 'latest' functionality, best practice is to either: WinGet is still in a preview period, with many features not implemented that are required for a PackageManagement provider to be fully implemented. Unsupported features currently include: -* Searching for packages by version range * Passing install arguments to packages * Saving a package diff --git a/Test/WinGet.Unit.Tests.ps1 b/Test/WinGet.Unit.Tests.ps1 index a9bb737..e3406de 100644 --- a/Test/WinGet.Unit.Tests.ps1 +++ b/Test/WinGet.Unit.Tests.ps1 @@ -9,7 +9,8 @@ BeforeAll { Describe 'basic package search operations' { Context 'without additional arguments' { BeforeAll { - $package = 'CPUID.CPU-Z' + $package = 'CPUID.HWMonitor' + $version = '1.44' } It 'gets a list of latest installed packages' { @@ -18,26 +19,36 @@ Describe 'basic package search operations' { It 'searches for the latest version of a package' { Find-Package -Provider $WinGet -Name $package | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty } + It 'returns all available versions of a package' { + Find-Package -Provider $WinGet -Name $package -AllVersions | Where-Object {$_.Version -eq $version} | Should -Not -BeNullOrEmpty + } + It 'returns additional package metadata' { + Find-Package -Provider $WinGet -Name $package | Select-Object -ExpandProperty FullPath | Should -Not -BeNullOrEmpty + } + It 'searches for all versions of a package' { + Find-Package -Provider $WinGet -Name $package -AllVersions | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty + } } } Describe 'DSC-compliant package installation and uninstallation' { Context 'without additional arguments' { BeforeAll { - $package = 'CPUID.CPU-Z' + $package = 'CPUID.HWMonitor' + $version = '1.44' } - It 'searches for the latest version of a package' { - Find-Package -Provider $WinGet -Name $package | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty + It 'searches for a specific version of a package' { + Find-Package -Provider $WinGet -Name $package -RequiredVersion $version | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty } - It 'silently installs the latest version of a package' { - Install-Package -Provider $WinGet -Name $package -Force | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty + It 'silently installs a specific version of a package' { + Install-Package -Provider $WinGet -Name $package -RequiredVersion $version -Force | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty } It 'finds the locally installed package just installed' { - Get-Package -Provider $WinGet -Name $package | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty + Get-Package -Provider $WinGet -Name $package -RequiredVersion $version | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty } It 'silently uninstalls the locally installed package just installed' { - Uninstall-Package -Provider $WinGet -Name $package | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty + Uninstall-Package -Provider $WinGet -Name $package -RequiredVersion $version | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty } } } @@ -45,7 +56,7 @@ Describe 'DSC-compliant package installation and uninstallation' { Describe 'pipeline-based package installation and uninstallation' { Context 'without additional arguments' { BeforeAll { - $package = 'CPUID.CPU-Z' + $package = 'CPUID.HWMonitor' } It 'searches for and silently installs the latest version of a package' { @@ -57,11 +68,26 @@ Describe 'pipeline-based package installation and uninstallation' { } } +Describe 'version tests' { + Context 'without additional arguments' { + BeforeAll { + $package = 'CPUID.HWMonitor' + $minVersion = '1.43' + $maxVersion = '1.44' + } + + It 'retrieves and correctly filters versions within a range' { + Find-Package -Provider $WinGet -Name $package -MaximumVersion $maxVersion -MinimumVersion $minVersion -AllVersions | Where-Object {$_.Name -contains $package} | Should -HaveCount 2 + } + } +} + Describe "multi-source support" { BeforeAll { $altSourceName = 'AltWinGetSource' $altSourceLocation = 'https://winget.azureedge.net/cache' - $package = 'CPUID.CPU-Z' + $package = 'CPUID.HWMonitor' + $version = '1.44' Unregister-PackageSource -Name $altSourceName -Provider $WinGet -ErrorAction SilentlyContinue } @@ -76,7 +102,7 @@ Describe "multi-source support" { Register-PackageSource -Name $altSourceName -Provider $WinGet -Location $altSourceLocation | Where-Object {$_.Name -eq $altSourceName} | Should -Not -BeNullOrEmpty } It 'searches for and installs the latest version of a package from an alternate source' { - Find-Package -Provider $WinGet -Name $package -source $altSourceName | Install-Package -Force | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty + Find-Package -Provider $WinGet -Name $package -Source $altSourceName -RequiredVersion $version | Install-Package -Force | Where-Object {$_.Name -contains $package} | Should -Not -BeNullOrEmpty } It 'unregisters an alternative package source' { Unregister-PackageSource -Name $altSourceName -Provider $WinGet diff --git a/src/WinGet.psd1 b/src/WinGet.psd1 index 04319bb..63b081c 100644 --- a/src/WinGet.psd1 +++ b/src/WinGet.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'WinGet.psm1' - ModuleVersion = '0.0.6' + ModuleVersion = '0.0.7' GUID = '468ef37a-2557-4c10-92ec-783ec1e41639' Author = 'Ethan Bergstrom' Copyright = '' @@ -19,7 +19,7 @@ }, @{ ModuleName='Cobalt' - ModuleVersion='0.0.10' + ModuleVersion='0.2.0' } ) PrivateData = @{ diff --git a/src/WinGet.psm1 b/src/WinGet.psm1 index 0c2332c..af427fc 100644 --- a/src/WinGet.psm1 +++ b/src/WinGet.psm1 @@ -5,6 +5,7 @@ $script:AcceptLicense = "AcceptLicense" $script:Force = "Force" $script:PackageSource = "WinGet" +$script:AllVersions = "AllVersions" # Utility variables # Fast Package References are passed between cmdlets in the format of '##' diff --git a/src/private/ConvertTo-SoftwareIdentity.ps1 b/src/private/ConvertTo-SoftwareIdentity.ps1 index 13466e2..9f75863 100644 --- a/src/private/ConvertTo-SoftwareIdentity.ps1 +++ b/src/private/ConvertTo-SoftwareIdentity.ps1 @@ -24,6 +24,7 @@ function ConvertTo-SoftwareIdentity { ) if ($packageSource) { Write-Debug "Package identified: $($package.ID), $($package.version), $($packageSource)" + $metadata = Cobalt\Get-WinGetPackageInfo -ID $package.ID -Version $package.Version -Source $packageSource $swid = @{ FastPackageReference = $package.ID+"#"+ $package.version+"#"+$packageSource Name = $package.ID @@ -31,6 +32,8 @@ function ConvertTo-SoftwareIdentity { versionScheme = "MultiPartNumeric" FromTrustedSource = $true Source = $packageSource + Summary = $metadata.Description + FullPath = $metadata.'Download URL' } New-SoftwareIdentity @swid } diff --git a/src/private/Find-WinGetPackage.ps1 b/src/private/Find-WinGetPackage.ps1 index 7cd8400..6117a5c 100644 --- a/src/private/Find-WinGetPackage.ps1 +++ b/src/private/Find-WinGetPackage.ps1 @@ -63,6 +63,22 @@ function Find-WinGetPackage { # Convert the PSCustomObject output from Cobalt into PackageManagement SWIDs, then filter results by any version requirements # We have to specify the source when converting to SWIDs, because WinGet doesn't return source information when the source is specified - Cobalt\Find-WinGetPackage @WinGetParams | ConvertTo-SoftwareIdentity -Source $selectedSource | - Where-Object {Test-PackageVersion -Package $_ -RequiredVersion $RequiredVersion -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion -Debug} + Cobalt\Find-WinGetPackage @WinGetParams | ForEach-Object { + # If we need to retrieve all versions, perform an additional query to get all available versions, and create a package object for each version + if ($RequiredVersion -Or $minimumVersion -Or $maximumVersion -Or $options.ContainsKey($script:AllVersions)) { + $package = $_ + $package | Get-WinGetPackageInfo -Versions -Source $selectedSource | Select-Object -Property @{ + Name = 'ID' + Expression = {$package.ID} + },@{ + Name = 'Version' + Expression = {$_} + },@{ + Name = 'Source' + Expression = {$package.Source} + } + } else { + $_ + } + } | Where-Object {Test-PackageVersion -Package $_ -RequiredVersion $RequiredVersion -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion} | ConvertTo-SoftwareIdentity -Source $selectedSource } diff --git a/src/private/Test-PackageVersion.ps1 b/src/private/Test-PackageVersion.ps1 index 7281474..e440700 100644 --- a/src/private/Test-PackageVersion.ps1 +++ b/src/private/Test-PackageVersion.ps1 @@ -4,7 +4,6 @@ function Test-PackageVersion { [OutputType([bool])] param ( [Parameter(Mandatory=$true)] - [Microsoft.PackageManagement.MetaProvider.PowerShell.SoftwareIdentity] $Package, [Parameter()] diff --git a/src/public/Get-InstalledPackage.ps1 b/src/public/Get-InstalledPackage.ps1 index f81a94e..71534a4 100644 --- a/src/public/Get-InstalledPackage.ps1 +++ b/src/public/Get-InstalledPackage.ps1 @@ -29,7 +29,8 @@ function Get-InstalledPackage { # Convert the PSCustomObject output from Cobalt into PackageManagement SWIDs, then filter results by version requirements # This provides wildcard search behavior for locally installed packages, which WinGet lacks - Cobalt\Get-WinGetPackage | ConvertTo-SoftwareIdentity | - Where-Object {-Not $Name -Or ($_.Name -Like $Name)} | - Where-Object {Test-PackageVersion -Package $_ -RequiredVersion $RequiredVersion -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion} + Cobalt\Get-WinGetPackage | + Where-Object {-Not $Name -Or ($_.ID -Like $Name)} | + Where-Object {Test-PackageVersion -Package $_ -RequiredVersion $RequiredVersion -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion} | + ConvertTo-SoftwareIdentity } diff --git a/src/public/Install-Package.ps1 b/src/public/Install-Package.ps1 index 397e956..5b089ae 100644 --- a/src/public/Install-Package.ps1 +++ b/src/public/Install-Package.ps1 @@ -35,18 +35,8 @@ function Install-Package { Source = $Matches.source } - # Convert the PSCustomObject output from Cobalt into PackageManagement SWIDs, then validate what WinGet installed matched what we requested - $swid = $( - $result = Cobalt\Install-WinGetPackage @WinGetParams - # If Cobalt didn't return anything, something went wrong and we need to throw our own exception - if (-Not $result) { - ThrowError -ExceptionName 'System.OperationCanceledException' ` - -ExceptionMessage $LocalizedData.WinGetFailure ` - -ErrorID 'JobFailure' ` - -ErrorCategory InvalidOperation ` - } - ConvertTo-SoftwareIdentity -InputObject $result -Source $WinGetParams.Source - ) | Where-Object {Test-PackageVersion -Package $_ -RequiredVersion $WinGetParams.version -ErrorAction SilentlyContinue} + # Validate what WinGet installed matched what we requested, then convert the PSCustomObject output from Cobalt into PackageManagement SWIDs + $swid = Cobalt\Install-WinGetPackage @WinGetParams | Where-Object {Test-PackageVersion -Package $_ -RequiredVersion $WinGetParams.version -ErrorAction SilentlyContinue} | ConvertTo-SoftwareIdentity -Source $WinGetParams.Source if (-Not $swid) { # Cobalt returned something, but not in the format we expected. Something is amiss.