Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement export WinGetPackage DSC resource #5074

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@
@{

# Script module or binary module file associated with this manifest.
RootModule = 'Microsoft.WinGet.DSC.psm1'
RootModule = 'Microsoft.WinGet.DSC.psm1'

# Version number of this module.
ModuleVersion = '0.0.1'
ModuleVersion = '0.0.1'

# Supported PSEditions
CompatiblePSEditions = 'Core'

# ID used to uniquely identify this module
GUID = '8c9326eb-595a-40eb-8696-b289e8085cad'
GUID = '8c9326eb-595a-40eb-8696-b289e8085cad'

# Author of this module
Author = 'Microsoft Corporation'
Author = 'Microsoft Corporation'

# Company or vendor of this module
CompanyName = 'Microsoft Corporation'
CompanyName = 'Microsoft Corporation'

# Copyright statement for this module
Copyright = '(c) Microsoft Corporation. All rights reserved.'
Copyright = '(c) Microsoft Corporation. All rights reserved.'

# Description of the functionality provided by this module
Description = 'PowerShell Module with DSC resources related to WinGet configurations'
Description = 'PowerShell Module with DSC resources related to WinGet configurations'

# Minimum version of the PowerShell engine required by this module
PowerShellVersion = '7.2'
PowerShellVersion = '7.2'

# Name of the PowerShell host required by this module
# PowerShellHostName = ''
Expand All @@ -49,7 +49,7 @@
# ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @('Microsoft.WinGet.Client')
RequiredModules = @('Microsoft.WinGet.Client')

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()
Expand Down Expand Up @@ -94,37 +94,39 @@
# FileList = @()

# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PrivateData = @{

PSData = @{

# Tags applied to this module. These help with module discovery in online galleries.
Tags = @(
Tags = @(
'PSEdition_Core',
'Windows',
'WindowsPackageManager'
)

# A URL to the license for this module.
# LicenseUri = ''
LicenseUri = 'https://github.com/microsoft/winget-cli/blob/master/LICENSE'

# A URL to the main website for this project.
ProjectUri = 'https://github.com/microsoft/winget-cli'
ProjectUri = 'https://github.com/microsoft/winget-cli'

# A URL to an icon representing this module.
IconUri = 'https://aka.ms/winget-icon'
IconUri = 'https://aka.ms/winget-icon'

# ReleaseNotes of this module
# ReleaseNotes = ''

# Prerelease string of this module
Prerelease = 'alpha'
Prerelease = 'alpha'

# Flag to indicate whether the module requires explicit user acceptance for install/update/save
# RequireLicenseAcceptance = $false

# External dependent modules of this module
# ExternalModuleDependencies = @()

DscCapabilities = ('Get', 'Set', 'Test', 'Export')

} # End of PSData hashtable

Expand All @@ -136,5 +138,5 @@
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''

}
}

52 changes: 39 additions & 13 deletions src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class WinGetUserSettings
{
$userSettings = Get-WinGetUserSetting
$result = @{
SID = ''
SID = ''
Settings = $userSettings
}
return $result
Expand Down Expand Up @@ -135,7 +135,7 @@ class WinGetAdminSettings
# Get admin setting values.

$result = @{
SID = ''
SID = ''
Settings = $settingsJson.adminSettings
}
return $result
Expand Down Expand Up @@ -216,10 +216,12 @@ class WinGetSource

$currentSource = $null

try {
try
{
$currentSource = Get-WinGetSource -Name $this.Name
}
catch {
catch
{
}

$result = [WinGetSource]::new()
Expand Down Expand Up @@ -295,7 +297,7 @@ class WinGetSource
if ($addSource)
{
$hashArgs = @{
Name = $this.Name
Name = $this.Name
Argument = $this.Argument
}

Expand Down Expand Up @@ -396,11 +398,13 @@ class WinGetPackageManager
if ($this.UseLatest)
{
$hashArgs.Add("Latest", $true)
} elseif ($this.UseLatestPreRelease)
}
elseif ($this.UseLatestPreRelease)
{
$hashArgs.Add("Latest", $true)
$hashArgs.Add("IncludePrerelease", $true)
} elseif (-not [string]::IsNullOrWhiteSpace($this.Version))
}
elseif (-not [string]::IsNullOrWhiteSpace($this.Version))
{
$hashArgs.Add("Version", $this.Version)
}
Expand All @@ -426,11 +430,13 @@ class WinGetPackageManager
if ($this.UseLatest)
{
$hashArgs.Add("Latest", $true)
} elseif ($this.UseLatestPreRelease)
}
elseif ($this.UseLatestPreRelease)
{
$hashArgs.Add("Latest", $true)
$hashArgs.Add("IncludePrerelease", $true)
} elseif (-not [string]::IsNullOrWhiteSpace($this.Version))
}
elseif (-not [string]::IsNullOrWhiteSpace($this.Version))
{
$hashArgs.Add("Version", $this.Version)
}
Expand Down Expand Up @@ -482,7 +488,7 @@ class WinGetPackage
$result = [WinGetPackage]::new()

$hashArgs = @{
Id = $this.Id
Id = $this.Id
MatchOption = $this.MatchOption
}

Expand Down Expand Up @@ -523,9 +529,9 @@ class WinGetPackage
if (-not $this.TestAgainstCurrent($currentPackage))
{
$hashArgs = @{
Id = $this.Id
Id = $this.Id
MatchOption = $this.MatchOption
Mode = $this.InstallMode
Mode = $this.InstallMode
}

if ($this.Ensure -eq [WinGetEnsure]::Present)
Expand Down Expand Up @@ -553,7 +559,7 @@ class WinGetPackage
$this.TryUpdate($hashArgs)
break
}
{'Greater' -or 'Unknown'}
{ 'Greater' -or 'Unknown' }
{
# The installed package has a greater version or unknown. Uninstall and install.
$this.Uninstall()
Expand All @@ -579,6 +585,26 @@ class WinGetPackage
}
}
}

static [WinGetPackage[]] Export()
{
$packages = Get-WingetPackage
$out = [List[WinGetPackage]]::new()
foreach ($package in $packages)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For our existing export CLI command, we only export packages that we can install later. This would require filtering the packages to those that have an available version. While we could do that in the DSC, I think it would be better to add a filter parameter to Get-WingetPackage for that purpose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What filter parameter are you thinking about?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like Get-WingetPackage -Export, but there might be a more idiomatic name for such a parameter. It would filter to only the packages that have an available version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a new cmdlet Export-WinGetPackage would also fit the use case...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have Export-WinGetPackage in the WinGet Client module that behaves logically the same as winget download <package>.

Copy link
Contributor Author

@Gijsreyn Gijsreyn Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Is there something that needs to be changed in this PR, or is it better to abandon it with all the possibilities already flying around?

{
$in = [WinGetPackage]@{
Ensure = [WinGetEnsure]::Present
Id = $package.Id
Source = $package.Source
Version = $package.InstalledVersion
UseLatest = -not $package.IsUpdateAvailable
}

$out.Add($in)
}

return $out
}

[bool] hidden TestAgainstCurrent([WinGetPackage]$currentPackage)
{
Expand Down
33 changes: 22 additions & 11 deletions src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
'Invoke-Pester' should be called in an admin PowerShell window.
#>

# TODO: Might need using statement for export

BeforeAll {
Install-Module -Name PSDesiredStateConfiguration -Force -SkipPublisherCheck
Import-Module Microsoft.WinGet.Client
Import-Module Microsoft.WinGet.DSC

# Helper function for calling Invoke-DscResource on the Microsoft.WinGet.DSC module.
function InvokeWinGetDSC() {
function InvokeWinGetDSC()
{
param (
[Parameter()]
[string]$Name,
Expand All @@ -30,12 +33,12 @@ BeforeAll {
}
}

Describe 'List available DSC resources'{
It 'Shows DSC Resources'{
Describe 'List available DSC resources' {
It 'Shows DSC Resources' {
$expectedDSCResources = "WinGetAdminSettings", "WinGetPackage", "WinGetPackageManager", "WinGetSource", "WinGetUserSettings"
$availableDSCResources = (Get-DscResource -Module Microsoft.WinGet.DSC).Name
$availableDSCResources.length | Should -Be 5
$availableDSCResources | Where-Object {$expectedDSCResources -notcontains $_} | Should -BeNullOrEmpty -ErrorAction Stop
$availableDSCResources | Where-Object { $expectedDSCResources -notcontains $_ } | Should -BeNullOrEmpty -ErrorAction Stop
}
}

Expand All @@ -45,9 +48,9 @@ Describe 'WinGetAdminSettings' {
$initialAdminSettings = (Get-WinGetSetting).adminSettings
$adminSettingsHash = @{
BypassCertificatePinningForMicrosoftStore = !$initialAdminSettings.BypassCertificatePinningForMicrosoftStore;
InstallerHashOverride = !$initialAdminSettings.InstallerHashOverride;
LocalManifestFiles = !$initialAdminSettings.LocalManifestFiles;
LocalArchiveMalwareScanOverride = !$initialAdminSettings.LocalArchiveMalwareScanOverride;
InstallerHashOverride = !$initialAdminSettings.InstallerHashOverride;
LocalManifestFiles = !$initialAdminSettings.LocalManifestFiles;
LocalArchiveMalwareScanOverride = !$initialAdminSettings.LocalArchiveMalwareScanOverride;
}
}

Expand Down Expand Up @@ -103,7 +106,7 @@ Describe 'WinGetUserSettings' {

$userSettingsHash = @{
experimentalFeatures = @{ directMSI = $true };
installBehavior = @{ Preferences = @{ Scope = 'User' }}
installBehavior = @{ Preferences = @{ Scope = 'User' } }
}
}

Expand Down Expand Up @@ -143,7 +146,7 @@ Describe 'WinGetSource' {
}

It 'Test WinGet source' {
$result = InvokeWinGetDSC -Name WinGetSource -Method Test -Property @{ Ensure='Present'; Name = $testSourceName; Argument = $testSourceArg; Type = $testSourceType }
$result = InvokeWinGetDSC -Name WinGetSource -Method Test -Property @{ Ensure = 'Present'; Name = $testSourceName; Argument = $testSourceArg; Type = $testSourceType }
$result.InDesiredState | Should -Be $false
}

Expand Down Expand Up @@ -205,7 +208,15 @@ Describe 'WinGetPackage' {
$result.Ensure | Should -Be 'Present'
$result.UseLatest | Should -Be $true
$result.Version | Should -Not -Be $testPackageVersion
}
}

It 'Export WinGetPackage(s)' {
$result = [WinGetPackage]::Export()

# Verify if export contains at least 1 package
$result | Should -not -BeNullOrEmpty
$result.Count | Should -BeGreaterThan 0
}

It 'Uninstall WinGetPackage' {
InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Id = $testPackageId; UseLatest = $true }
Expand All @@ -221,7 +232,7 @@ Describe 'WinGetPackage' {
}

AfterAll {
InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Ensure = 'Absent'; Id = $testPackageId}
InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Ensure = 'Absent'; Id = $testPackageId }
}
}

Expand Down
Loading