From ab60ff8681e8673bd5cd6bd9c06e61817824069b Mon Sep 17 00:00:00 2001 From: Reece Dunham Date: Thu, 10 Jan 2019 07:59:12 -0500 Subject: [PATCH 01/21] Secure the links --- CODE_OF_CONDUCT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index dddfa22d..90768d12 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -3,6 +3,6 @@ This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments. -[conduct-code]: http://opensource.microsoft.com/codeofconduct/ -[conduct-FAQ]: http://opensource.microsoft.com/codeofconduct/faq/ +[conduct-code]: https://opensource.microsoft.com/codeofconduct/ +[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/ [conduct-email]: mailto:opencode@microsoft.com From 0d77cf13c7d91083d4ed3ee49ca33cc80fe268e3 Mon Sep 17 00:00:00 2001 From: Thomas Pouget Abadie Date: Fri, 11 Jan 2019 09:47:22 +0100 Subject: [PATCH 02/21] Throw error when Test-ModuleManifest fails in Update-ModuleManifest --- Tests/PSGetUpdateModuleManifest.Tests.ps1 | 21 ++++++++++++++++++- .../psgetfunctions/Update-ModuleManifest.ps1 | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Tests/PSGetUpdateModuleManifest.Tests.ps1 b/Tests/PSGetUpdateModuleManifest.Tests.ps1 index 9c146236..e22a8c51 100644 --- a/Tests/PSGetUpdateModuleManifest.Tests.ps1 +++ b/Tests/PSGetUpdateModuleManifest.Tests.ps1 @@ -686,6 +686,25 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { -expectedFullyQualifiedErrorId "InvalidPackageManagementProviders,Update-ModuleManifest" } + # Purpose: Validate Update-ModuleManifest will throw errors when an invalid RootModule is provided + # + # Action: + # Update-ModuleManifest -Path [Path] -RootModule [InvalidRootModule] + # + # Expected Result: Update-ModuleManifest should throw errors about the invalid RootModule + # + It UpdateModuleManifestWithInvalidRootModule { + + New-ModuleManifest -path $script:testManifestPath + + $InvalidRootModule = "\/" + AssertFullyQualifiedErrorIdEquals -scriptblock {Update-ModuleManifest -Path $script:testManifestPath -RootModule $InvalidRootModule} ` + -expectedFullyQualifiedErrorId "UpdateManifestFileFail,Update-ModuleManifest" + + $newModuleInfo = Test-ModuleManifest -Path $script:testManifestPath + Assert ($newModuleInfo.RootModule -contains $InvalidRootModule -eq $False) 'Module Manifest should not contain an invalid root module' + }` + -Skip:$($PSVersionTable.PSVersion -lt '5.1.0') # Purpose: Validate Update-ModuleManifest will throw errors if the original manifest fail the Test-ModuleManifest # @@ -969,4 +988,4 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { Assert ($newModuleInfo.CompatiblePSEditions -contains $CompatiblePSEditions[1]) "CompatiblePSEditions should include $($CompatiblePSEditions[1])" } ` -Skip:$($PSVersionTable.PSVersion -lt '5.1.0') -} \ No newline at end of file +} diff --git a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 index daa73d03..d552306a 100644 --- a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 @@ -922,7 +922,7 @@ function Update-ModuleManifest #Verify the new module manifest is valid $testModuleInfo = Microsoft.PowerShell.Core\Test-ModuleManifest -Path $tempPath ` - -Verbose:$VerbosePreference ` + -Verbose:$VerbosePreference ` -ErrorAction Stop } #Catch the exceptions from Test-ModuleManifest catch From e454bd670fc844f3e75377ec2280cf9678b76d78 Mon Sep 17 00:00:00 2001 From: Edwin Young Date: Mon, 14 Jan 2019 10:56:09 -0800 Subject: [PATCH 03/21] filter out hidden files from nupkg (#396) --- Tests/PSGetPublishModule.Tests.ps1 | 75 +++++++++++++------ .../public/psgetfunctions/Publish-Module.ps1 | 13 +++- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/Tests/PSGetPublishModule.Tests.ps1 b/Tests/PSGetPublishModule.Tests.ps1 index 25befe51..c62a12cb 100644 --- a/Tests/PSGetPublishModule.Tests.ps1 +++ b/Tests/PSGetPublishModule.Tests.ps1 @@ -121,7 +121,7 @@ Describe PowerShell.PSGet.PublishModuleTests -Tags 'BVT','InnerLoop' { #Copy module to $script:ProgramFilesModulesPath Copy-Item $script:PublishModuleBase $script:ProgramFilesModulesPath -Recurse -Force - Publish-Module -Name $script:PublishModuleName -ReleaseNotes "$script:PublishModuleName release notes" -Tags PSGet -LicenseUri "https://$script:PublishModuleName.com/license" -ProjectUri "https://$script:PublishModuleName.com" -WarningAction SilentlyContinue + Publish-Module -Name $script:PublishModuleName -ReleaseNotes "$script:PublishModuleName release notes" -Tags PSGet -LicenseUri "https://$script:PublishModuleName.com/license" -ProjectUri "https://$script:PublishModuleName.com" -WarningAction SilentlyContinue $psgetItemInfo = Find-Module $script:PublishModuleName -RequiredVersion $version # This version can be 1.0 or 1.0.0, depending on the version of NuGet.exe or dotnet command. @@ -201,6 +201,39 @@ Describe PowerShell.PSGet.PublishModuleTests -Tags 'BVT','InnerLoop' { Assert ($psgetItemInfo.Name -eq $script:PublishModuleName) "Publish-Module should publish a module with valid module path, $($psgetItemInfo.Name)" } + # Purpose: Check that files with hidden attributes (like .git dirs) are not published + # + # Action: Publish-Module -Path -NuGetApiKey + # + # Expected Result: published module should not contain these files + # + It "PublishModuleLeavesOutHiddenFiles" { + + # create module dir containing a hidden dir with a normal file inside, and a normal file + $version = "1.0" + New-ModuleManifest -Path (Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module" -NestedModules "$script:PublishModuleName.psm1" + New-Item -Type Directory -Path $script:PublishModuleBase -Name ".git" + New-Item -Type File -Path $script:PublishModuleBase\.git\dummy + New-Item -Type File -Path $script:PublishModuleBase -Name "normal" + $hiddenDir = Get-Item $script:PublishModuleBase\.git -force + $hiddenDir.Attributes = "hidden" + + # publish it, then save the published one and inspect it + Publish-Module -Path $script:PublishModuleBase -NuGetApiKey $script:ApiKey -ReleaseNotes "$script:PublishModuleName release notes" -Tags PSGet -LicenseUri "https://$script:PublishModuleName.com/license" -ProjectUri "https://$script:PublishModuleName.com" -WarningAction SilentlyContinue + New-Item -type directory -path $script:PublishModuleBase -Name saved + Save-Module $script:PublishModuleName -RequiredVersion $version -Path $script:PublishModuleBase\saved + + # it should contain the normal file, but not the hidden dir + $contents = (dir -rec -force $script:PublishModuleBase | Out-String) + $basedir = "$script:PublishModuleBase\saved\$script:PublishModuleName" + if($PSVersionTable.PSVersion -gt '5.0.0') { + $basedir = join-path $basedir "1.0.0" + } + + Assert ((Test-Path $basedir\.git) -eq $false) ".git dir shouldn't be included ($contents)" + Assert ((Test-Path $basedir\normal) -eq $true) "normal file should be included ($contents)" + } + # Purpose: Publish a module with -Path # # Action: Publish-Module -Path -NuGetApiKey @@ -273,11 +306,11 @@ Describe PowerShell.PSGet.PublishModuleTests -Tags 'BVT','InnerLoop' { $moduleBaseWithoutVersion = $script:PublishModuleBase $moduleBase = Join-Path -Path $script:PublishModuleBase -ChildPath $version1 - $null = New-Item -ItemType Directory -Path $moduleBase -Force + $null = New-Item -ItemType Directory -Path $moduleBase -Force New-ModuleManifest -Path (Join-Path -Path $moduleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version1 -Description "$script:PublishModuleName module" $moduleBase = Join-Path -Path $script:PublishModuleBase -ChildPath $version2 - $null = New-Item -ItemType Directory -Path $moduleBase -Force + $null = New-Item -ItemType Directory -Path $moduleBase -Force New-ModuleManifest -Path (Join-Path -Path $moduleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version2 -Description "$script:PublishModuleName module" AssertFullyQualifiedErrorIdEquals -scriptblock {Publish-Module -Path $moduleBaseWithoutVersion -WarningAction SilentlyContinue}` @@ -923,7 +956,7 @@ Describe PowerShell.PSGet.PublishModuleTests -Tags 'BVT','InnerLoop' { $ProjectUri = 'https://contoso.com/' $IconUri = 'https://contoso.com/icon' $ReleaseNotes = 'Test module for external module dependecies' - New-ModuleManifest -Path (Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module" + New-ModuleManifest -Path (Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module" #Copy module to $script:ProgramFilesModulesPath Copy-Item $script:PublishModuleBase $script:ProgramFilesModulesPath -Recurse -Force @@ -941,7 +974,7 @@ Describe PowerShell.PSGet.PublishModuleTests -Tags 'BVT','InnerLoop' { # Purpose: Test Publish-Module cmdlet gives warnings if Cmdlets/Functions/DscResourcesToExport has "*" in manifest # - # Action: Publish-Module + # Action: Publish-Module # # Expected Result: Publish operation should succeed but throw warnings # @@ -959,17 +992,17 @@ Describe PowerShell.PSGet.PublishModuleTests -Tags 'BVT','InnerLoop' { using System; using System.Management.Automation; namespace PSGetTestModule - { + { [Cmdlet("Test","PSGetTestCmdlet")] public class PSGetTestCmdlet : PSCmdlet - { + { [Parameter] public int a { get; set; - } + } protected override void ProcessRecord() - { + { String s = "Value is :" + a; WriteObject(s); } @@ -1205,7 +1238,7 @@ Describe PowerShell.PSGet.PublishModuleTests -Tags 'BVT','InnerLoop' { # Purpose: Validate that Publish-Module prompts to upgrade NuGet.exe if local NuGet.exe file is less than minimum required version # - # Action: Publish-Module + # Action: Publish-Module # # Expected Result: Publish operation should fail, NuGet.exe should not upgrade to latest version # @@ -1372,7 +1405,7 @@ Describe PowerShell.PSGet.PublishModuleTests.P1 -Tags 'P1','OuterLoop' { Publish-Module -Path $script:PublishModuleBase -NuGetApiKey $script:ApiKey -Force -WarningAction SilentlyContinue $psgetItemInfo = Find-Module $script:PublishModuleName -RequiredVersion $version - Assert ($psgetItemInfo.Name -eq $script:PublishModuleName) "Publish-Module should publish a module with lower version, $($psgetItemInfo.Name)" + Assert ($psgetItemInfo.Name -eq $script:PublishModuleName) "Publish-Module should publish a module with lower version, $($psgetItemInfo.Name)" } It "PublishModuleWithoutForceAndLowerVersion" { @@ -1553,7 +1586,7 @@ Describe PowerShell.PSGet.PublishModuleTests.P1 -Tags 'P1','OuterLoop' { # It "PublishInvalidModule" { $tempmodulebase = Join-Path (Join-Path $script:TempPath "$(Get-Random)") "InvalidModule" - $null = New-Item $tempmodulebase -Force -ItemType Directory + $null = New-Item $tempmodulebase -Force -ItemType Directory try { @@ -1779,7 +1812,7 @@ Describe PowerShell.PSGet.PublishModuleTests.P1 -Tags 'P1','OuterLoop' { $null = New-Item -Path $DepModuleBase -ItemType Directory -Force New-ModuleManifest -Path (Join-Path $DepModuleBase "$_.psd1") ` -ModuleVersion '1.0' ` - -Description "$_ module" + -Description "$_ module" } @@ -1809,7 +1842,7 @@ Describe PowerShell.PSGet.PublishModuleTests.P1 -Tags 'P1','OuterLoop' { } } } -"@ +"@ ($psd1Text -replace '__VERSION__',$version) | Out-File -FilePath (Join-Path -Path $ModuleBase -ChildPath "$ModuleName.psd1") -Force @@ -1870,20 +1903,20 @@ Describe PowerShell.PSGet.PublishModuleTests.P2 -Tags 'P2','OuterLoop' { $RequiredModules1 = @('RequiredModule1', @{ModuleName = 'RequiredModule2'; ModuleVersion = '1.5'; }) - $RequiredModules2 = @('RequiredModule1', + $RequiredModules2 = @('RequiredModule1', @{ModuleName = 'RequiredModule2'; ModuleVersion = '2.0'; }) - $NestedRequiredModules1 = @('NestedRequiredModule1', + $NestedRequiredModules1 = @('NestedRequiredModule1', @{ModuleName = 'NestedRequiredModule2'; ModuleVersion = '1.5'; }) - $NestedRequiredModules2 = @('NestedRequiredModule1', + $NestedRequiredModules2 = @('NestedRequiredModule1', @{ModuleName = 'NestedRequiredModule2'; ModuleVersion = '2.0'; }) if($PSVersionTable.PSVersion -ge '5.0.0') { $DepencyModuleNames += @("RequiredModule3", "RequiredModule4", - "RequiredModule5"<#, + "RequiredModule5"<#, "NestedRequiredModule3", "NestedRequiredModule4", "NestedRequiredModule5"#> @@ -1991,7 +2024,7 @@ Describe PowerShell.PSGet.PublishModuleTests.P2 -Tags 'P2','OuterLoop' { $null = New-Item -Path $DepModuleBase -ItemType Directory -Force New-ModuleManifest -Path (Join-Path $DepModuleBase "$_.psd1") ` -ModuleVersion '1.0.0' ` - -Description "$_ module" + -Description "$_ module" } $psd1Text = @" @@ -2020,7 +2053,7 @@ Describe PowerShell.PSGet.PublishModuleTests.P2 -Tags 'P2','OuterLoop' { } } } -"@ +"@ ($psd1Text -replace '__VERSION__',$version) | Out-File -FilePath (Join-Path -Path $ModuleBase -ChildPath "$ModuleName.psd1") -Force @@ -2097,4 +2130,4 @@ Describe PowerShell.PSGet.PublishModuleTests.P2 -Tags 'P2','OuterLoop' { } } } -} \ No newline at end of file +} diff --git a/src/PowerShellGet/public/psgetfunctions/Publish-Module.ps1 b/src/PowerShellGet/public/psgetfunctions/Publish-Module.ps1 index 1a8ec5d3..4a3d1f4b 100644 --- a/src/PowerShellGet/public/psgetfunctions/Publish-Module.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Publish-Module.ps1 @@ -365,7 +365,18 @@ function Publish-Module { } $null = Microsoft.PowerShell.Management\New-Item -Path $tempModulePathForFormatVersion -ItemType Directory -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false - Microsoft.PowerShell.Management\Copy-Item -Path "$Path\*" -Destination $tempModulePathForFormatVersion -Force -Recurse -Confirm:$false -WhatIf:$false + + # Copy-Item -Recurse -Force includes hidden items like .git directories, which we don't want + # This finds all the items without force (leaving out hidden files and dirs) then copies them + Microsoft.PowerShell.Management\Get-ChildItem $Path -recurse | + Microsoft.PowerShell.Management\Copy-Item -Force -Confirm:$false -WhatIf:$false -Destination { + if ($_.PSIsContainer) { + Join-Path $tempModulePathForFormatVersion $_.Parent.FullName.substring($path.length) + } + else { + join-path $tempModulePathForFormatVersion $_.FullName.Substring($path.Length) + } + } try { $manifestPath = Join-PathUtility -Path $tempModulePathForFormatVersion -ChildPath "$moduleName.psd1" -PathType File From e2c81de04a376af6368615446bde9d6cbe33ebf0 Mon Sep 17 00:00:00 2001 From: Scott Matthews Date: Mon, 14 Jan 2019 18:58:53 +0000 Subject: [PATCH 04/21] Adding support for PowerShellGet switches (#347) --- .../MSFT_PSModule/MSFT_PSModule.psm1 | 654 ++++++++++++++++++ .../MSFT_PSModule/MSFT_PSModule.schema.mfl | 24 + .../MSFT_PSModule/MSFT_PSModule.schema.mof | 21 + .../MSFT_PSModule/MSFT_PSModule.strings.psd1 | 37 + PowerShellGet/DSCResources/OneGetHelper.psm1 | 301 ++++++++ .../DSCResources/OneGetHelper.strings.psd1 | 22 + 6 files changed, 1059 insertions(+) create mode 100644 PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 create mode 100644 PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl create mode 100644 PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof create mode 100644 PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 create mode 100644 PowerShellGet/DSCResources/OneGetHelper.psm1 create mode 100644 PowerShellGet/DSCResources/OneGetHelper.strings.psd1 diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 new file mode 100644 index 00000000..c6cf548f --- /dev/null +++ b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 @@ -0,0 +1,654 @@ +# +# Copyright (c) Microsoft Corporation. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +Import-LocalizedData -BindingVariable LocalizedData -filename MSFT_PSModule.strings.psd1 +Import-Module -Name "$PSScriptRoot\..\OneGetHelper.psm1" + +#DSC Resource for the $CurrentProviderName +$CurrentProviderName="PowerShellGet" + + +#Return the current state of the resource +function Get-TargetResource +{ + <# + .SYNOPSIS + + This DSC resource provides a mechanism to download PowerShell modules from the PowerShell + Gallery and install it on your computer. + + Get-TargetResource returns the current state of the resource. + + .PARAMETER Name + Specifies the name of the PowerShell module to be installed or uninstalled. + + .PARAMETER Repository + Specifies the name of the module source repository where the module can be found. + + .PARAMETER RequiredVersion + Provides the version of the module you want to install or uninstall. + + .PARAMETER MaximumVersion + Provides the maximum version of the module you want to install or uninstall. + + .PARAMETER MinimumVersion + Provides the minimum version of the module you want to install or uninstall. + + .PARAMETER Force + Forces the installation of modules. If a module of the same name and version already exists on the computer, + this parameter overwrites the existing module with one of the same name that was found by the command. + + .PARAMETER AllowClobber + Allows the installation of modules regardless of if other existing module on the computer have cmdlets + of the same name + + .PARAMETER SkipPublisherCheck + Allows the installation of modules that have not been catalog signed + #> + + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [parameter(Mandatory = $true)] + [System.String] + $Name, + + [System.String] + $Repository="PSGallery", + + [System.String] + $RequiredVersion, + + [System.String] + $MaximumVersion, + + [System.String] + $MinimumVersion, + + [Switch] + $Force, + + [Switch] + $AllowClobber, + + [Switch] + $SkipPublisherCheck + ) + + #Initialize the $Ensure variable + $ensure = 'Absent' + + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + + #Get the module with the right version and repository properties + $modules = Get-RightModule @extractedArguments -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + #If the module is found, the count > 0 + if ($modules.count -gt 0) + { + $ensure = 'Present' + + Write-Verbose -Message ($localizedData.ModuleFound -f $($Name)) + } + else + { + Write-Verbose -Message ($localizedData.ModuleNotFound -f $($Name)) + } + + Write-Debug -Message "Ensure of $($Name) module is $($ensure)" + + if ($ensure -eq 'Absent') + { + return @{ + Ensure = $ensure + Name = $Name + } + } + else + { + #Find a module with the latest version and return its properties + $latestModule = $modules[0] + + foreach ($module in $modules) + { + if ($module.Version -gt $latestModule.Version) + { + $latestModule = $module + } + } + + #Check if the repository matches + $repositoryName = Get-ModuleRepositoryName -Module $latestModule -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + $installationPolicy = Get-InstallationPolicy -RepositoryName $repositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + return @{ + Ensure = $ensure + Name = $Name + Repository = $repositoryName + Description = $latestModule.Description + Guid = $latestModule.Guid + ModuleBase = $latestModule.ModuleBase + ModuleType = $latestModule.ModuleType + Author = $latestModule.Author + InstalledVersion = $latestModule.Version + InstallationPolicy=if($installationPolicy) {"Trusted"}else{"Untrusted"} + } + } +} + +function Test-TargetResource +{ + <# + .SYNOPSIS + + This DSC resource provides a mechanism to download PowerShell modules from the PowerShell + Gallery and install it on your computer. + + Test-TargetResource validates whether the resource is currently in the desired state. + + .PARAMETER Ensure + Determines whether the module to be installed or uninstalled. + + .PARAMETER Name + Specifies the name of the PowerShell module to be installed or uninstalled. + + .PARAMETER Repository + Specifies the name of the module source repository where the module can be found. + + .PARAMETER InstallationPolicy + Determines whether you trust the source repository where the module resides. + + .PARAMETER RequiredVersion + Provides the version of the module you want to install or uninstall. + + .PARAMETER MaximumVersion + Provides the maximum version of the module you want to install or uninstall. + + .PARAMETER MinimumVersion + Provides the minimum version of the module you want to install or uninstall. + + .PARAMETER Force + Forces the installation of modules. If a module of the same name and version already exists on the computer, + this parameter overwrites the existing module with one of the same name that was found by the command. + + .PARAMETER AllowClobber + Allows the installation of modules regardless of if other existing module on the computer have cmdlets + of the same name + + .PARAMETER SkipPublisherCheck + Allows the installation of modules that have not been catalog signed + #> + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [ValidateSet("Present","Absent")] + [System.String] + $Ensure="Present", + + [parameter(Mandatory = $true)] + [System.String] + $Name, + + [System.String] + $Repository="PSGallery", + + [ValidateSet("Trusted","Untrusted")] + [System.String] + $InstallationPolicy="Untrusted", + + [System.String] + $RequiredVersion, + + [System.String] + $MaximumVersion, + + [System.String] + $MinimumVersion, + + [Switch] + $Force, + + [Switch] + $AllowClobber, + + [Switch] + $SkipPublisherCheck + ) + + Write-Debug -Message "Calling Test-TargetResource" + + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + + $status = Get-TargetResource @extractedArguments + + #The ensure returned from Get-TargetResource is not equal to the desired $Ensure + # + if ($status.Ensure -ieq $Ensure) + { + Write-Verbose -Message ($localizedData.InDesiredState -f $Name) + return $true + } + else + { + Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name) + return $false + } +} + +function Set-TargetResource +{ + <# + .SYNOPSIS + + This DSC resource provides a mechanism to download PowerShell modules from the PowerShell + Gallery and install it on your computer. + + Set-TargetResource sets the resource to the desired state. "Make it so". + + .PARAMETER Ensure + Determines whether the module to be installed or uninstalled. + + .PARAMETER Name + Specifies the name of the PowerShell module to be installed or uninstalled. + + .PARAMETER Repository + Specifies the name of the module source repository where the module can be found. + + .PARAMETER InstallationPolicy + Determines whether you trust the source repository where the module resides. + + .PARAMETER RequiredVersion + Provides the version of the module you want to install or uninstall. + + .PARAMETER MaximumVersion + Provides the maximum version of the module you want to install or uninstall. + + .PARAMETER MinimumVersion + Provides the minimum version of the module you want to install or uninstall. + + .PARAMETER Force + Forces the installation of modules. If a module of the same name and version already exists on the computer, + this parameter overwrites the existing module with one of the same name that was found by the command. + + .PARAMETER AllowClobber + Allows the installation of modules regardless of if other existing module on the computer have cmdlets + of the same name + + .PARAMETER SkipPublisherCheck + Allows the installation of modules that have not been catalog signed + #> + + [CmdletBinding()] + param + ( + [ValidateSet("Present","Absent")] + [System.String] + $Ensure="Present", + + [parameter(Mandatory = $true)] + [System.String] + $Name, + + [System.String] + $Repository="PSGallery", + + [ValidateSet("Trusted","Untrusted")] + [System.String] + $InstallationPolicy="Untrusted", + + [System.String] + $RequiredVersion, + + [System.String] + $MaximumVersion, + + [System.String] + $MinimumVersion, + + [Switch] + $Force, + + [Switch] + $AllowClobber, + + [Switch] + $SkipPublisherCheck + ) + + + #Validate the repository argument + if ($PSBoundParameters.ContainsKey("Repository")) + { + ValidateArgument -Argument $Repository -Type "PackageSource" -ProviderName $CurrentProviderName -Verbose + } + + if($Ensure -ieq "Present") + { + + #Version check + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ("MinimumVersion","MaximumVersion", "RequiredVersion") + + ValidateVersionArgument @extractedArguments + + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ("Name","Repository", "MinimumVersion", "MaximumVersion","RequiredVersion") + + Write-Verbose -Message ($localizedData.StartFindmodule -f $($Name)) + + + $modules = Find-Module @extractedArguments -ErrorVariable ev + + + if (-not $modules) + { + + ThrowError -ExceptionName "System.InvalidOperationException" ` + -ExceptionMessage ($localizedData.ModuleNotFoundInRepository -f $Name, $ev.Exception) ` + -ErrorId "ModuleNotFoundInRepository" ` + -ErrorCategory InvalidOperation + } + + $trusted = $null + $moduleFound = $null + + foreach ($m in $modules) + { + #Check for the installation policy + $trusted = Get-InstallationPolicy -RepositoryName $m.Repository -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + #Stop the loop if found a trusted repository + if ($trusted) + { + $moduleFound = $m + break; + } + } + + + #The respository is trusted, so we install it + if ($trusted) + { + Write-Verbose -Message ($localizedData.StartInstallModule -f $Name, $moduleFound.Version.toString(), $moduleFound.Repository ) + + #Extract the installation options + $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("Force","AllowClobber", "SkipPublisherCheck") + + $moduleFound | Install-Module -ErrorVariable ev + } + #The repository is untrusted but user's installation policy is trusted, so we install it with a warning + elseif ($InstallationPolicy -ieq 'Trusted') + { + Write-Warning -Message ($localizedData.InstallationPolicyWarning -f $Name, $modules[0].Repository, $InstallationPolicy) + + # Extract installation options (Force implied by InstallationPolicy) + $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("AllowClobber", "SkipPublisherCheck") + + #if all the repositories are untrusted, we choose the first one + $modules[0] | Install-Module @extractedSwitches -Force -ErrorVariable ev + } + #Both user and repository is untrusted + else + { + ThrowError -ExceptionName "System.InvalidOperationException" ` + -ExceptionMessage ($localizedData.InstallationPolicyFailed -f $InstallationPolicy, "Untrusted") ` + -ErrorId "InstallationPolicyFailed" ` + -ErrorCategory InvalidOperation + } + + if ($ev) + { + ThrowError -ExceptionName "System.InvalidOperationException" ` + -ExceptionMessage ($localizedData.FailtoInstall -f $Name, $ev.Exception) ` + -ErrorId "FailtoInstall" ` + -ErrorCategory InvalidOperation + } + else + { + Write-Verbose -Message ($localizedData.InstalledSuccess -f $($Name)) + } + } + #Ensure=Absent + else + { + + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + + + #Get the module with the right version and repository properties + $modules = Get-RightModule @extractedArguments -ErrorVariable ev + + if ((-not $modules) -or $ev) + { + ThrowError -ExceptionName "System.InvalidOperationException" ` + -ExceptionMessage ($localizedData.ModuleWithRightPropertyNotFound -f $Name, $ev.Exception) ` + -ErrorId "ModuleWithRightPropertyNotFound" ` + -ErrorCategory InvalidOperation + } + + foreach ($module in $modules) + { + #Get the path where the module is installed + $path=$module.ModuleBase + + Write-Verbose -Message ($localizedData.StartUnInstallModule -f $($Name)) + + #There is no Uninstall-Module cmdlet exists, so we will remove the ModuleBase folder as an uninstall operation + Microsoft.PowerShell.Management\Remove-Item -Path $path -Force -Recurse -ErrorVariable ev + + if($ev) + { + ThrowError -ExceptionName "System.InvalidOperationException" ` + -ExceptionMessage ($localizedData.FailtoUninstall -f $module.Name, $ev.Exception) ` + -ErrorId "FailtoUninstall" ` + -ErrorCategory InvalidOperation + } + else + { + Write-Verbose -Message ($localizedData.UnInstalledSuccess -f $($module.Name)) + } + + }#foreach + + } #Ensure=Absent +} + + +Function Get-RightModule +{ + <# + .SYNOPSIS + + This is a helper function. It returns the modules that meet the specified versions and the repository requirements + + .PARAMETER Name + Specifies the name of the PowerShell module. + + .PARAMETER RequiredVersion + Provides the version of the module you want to install or uninstall. + + .PARAMETER MaximumVersion + Provides the maximum version of the module you want to install or uninstall. + + .PARAMETER MinimumVersion + Provides the minimum version of the module you want to install or uninstall. + + .PARAMETER Repository + Specifies the name of the module source repository where the module can be found. + #> + + param + ( + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [System.String] + $RequiredVersion, + + [System.String] + $MinimumVersion, + + [System.String] + $MaximumVersion, + + [System.String] + $Repository + ) + + + Write-Verbose -Message ($localizedData.StartGetModule -f $($Name)) + + $modules = Microsoft.PowerShell.Core\Get-Module -Name $Name -ListAvailable -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + if (-not $modules) + { + return $null + } + + # + #As Get-Module does not take RequiredVersion, MinimumVersion, MaximumVersion, or Repository, below we need to check + #whether the modules are containing the right version and repository location. + + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ("MaximumVersion","MinimumVersion", "RequiredVersion") + + $returnVal =@() + + foreach ($m in $modules) + { + $versionMatch = $false + $installedVersion = $m.Version + + #Case 1 - a user provides none of RequiredVersion, MinimumVersion, MaximumVersion + + if ($extractedArguments.Count -eq 0) + { + $versionMatch = $true + } + # + #Case 2 - a user provides RequiredVersion + # + elseif ($extractedArguments.ContainsKey("RequiredVersion")) + { + #Check if it matches with the installedversion + $versionMatch = ($installedVersion -eq [System.Version]$RequiredVersion) + } + else + { + #Case 3 - a user provides MinimumVersion + if ($extractedArguments.ContainsKey("MinimumVersion")) + { + $versionMatch = ($installedVersion -ge [System.Version]$extractedArguments['MinimumVersion']) + } + # + #Case 4 - a user provides MaximumVersion + # + if ($extractedArguments.ContainsKey("MaximumVersion")) + { + $isLessThanMax = ($installedVersion -le [System.Version]$extractedArguments['MaximumVersion']) + + if ($extractedArguments.ContainsKey("MinimumVersion")) + { + $versionMatch = $versionMatch -and $isLessThanMax + } + else + { + $versionMatch = $isLessThanMax + } + } + #Case 5 - Both MinimumVersion and MaximumVersion are provided. it's covered by the above + + #Do not return $false yet to allow the foreach to continue + if (-not $versionMatch) + { + Write-Verbose -Message ($localizedData.VersionMismatch -f $($Name), $($installedVersion)) + $versionMatch = $false + } + } + + #Case 6 - Version matches but need to check if the module is from the right repository + # + if ($versionMatch) + { + #a user does not provide Repository, we are good + if (-not $PSBoundParameters.ContainsKey("Repository")) + { + Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") + $returnVal+=$m + + } + else + { + #Check if the Repository matches + $sourceName = Get-ModuleRepositoryName -Module $m + + if ($Repository -ieq $sourceName) + { + Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") + $returnVal+=$m + } + else + { + Write-Verbose -Message ($localizedData.RepositoryMismatch -f $($Name), $($sourceName)) + } + } + } + + } #foreach + + return $returnVal +} + +Function Get-ModuleRepositoryName +{ + <# + .SYNOPSIS + + This is a helper function that returns the module's repository name + + .PARAMETER Module + Specifies the name of the PowerShell module. + #> + Param + ( + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Object]$Module + ) + + + #RepositorySourceLocation property is supported in PS V5 only. To work with the earlier PS version, we need to do a different way. + #PSGetModuleInfo.xml exists for any PS modules downloaded through PSModule provider. + + $psGetModuleInfoFileName = "PSGetModuleInfo.xml" + + $psGetModuleInfoPath = Microsoft.PowerShell.Management\Join-Path -Path $Module.ModuleBase -ChildPath $psGetModuleInfoFileName + + Write-Verbose -Message ($localizedData.FoundModulePath -f $($psGetModuleInfoPath)) + + if (Microsoft.PowerShell.Management\Test-path -Path $psGetModuleInfoPath) + { + $psGetModuleInfo = Microsoft.PowerShell.Utility\Import-Clixml -Path $psGetModuleInfoPath + + return $psGetModuleInfo.Repository + } +} + +Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl new file mode 100644 index 00000000..d7ad6743 --- /dev/null +++ b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl @@ -0,0 +1,24 @@ +#pragma namespace("\\\\.\\root\\default") +instance of __namespace{ name="MS_409";}; +#pragma namespace("\\\\.\\root\\default\\MS_409") + +[AMENDMENT, LOCALE("MS_409")] +class MSFT_PSModule : OMI_BaseResource +{ + [Key,Description("Name of the module\n") : Amended] String Name; + [Description("Whether the module is to be installed or uninstalled.\nPresent {default} \nAbsent \n") : Amended] String Ensure; + [Description("The name of the module source where the module can be found.\n") : Amended] String Repository; + [Description("Whether the package is trusted or untrusted.\nTrusted {default} \nUntrusted \n") : Amended] String InstallationPolicy; + [Description("The required version of the module.\n") : Amended] String RequiredVersion; + [Description("The minimum version of the module.\n") : Amended] String MinimumVersion; + [Description("The maximum version of the module.\n") : Amended] String MaximumVersion; + [Description("Forces the installation of the module.\n" : Amended] Boolean Force; + [Description("Allows installation when existing cmdlets of the same name exist.\n" : Amended] Boolean AllowClobber; + [Description("Allows installation when module is not signed.\n" : Amended] Boolean SkipPublisherCheck; + [Description("The brief description of the module.\n") : Amended] string Description; + [Description("The version of the module that is installed.\n") : Amended] String InstalledVersion; + [Description("The identifier of the module.\n") : Amended] String Guid; + [Description("The base location where the module is installed.\n") : Amended] String ModuleBase; + [Description("The type of the module.\n") : Amended] String ModuleType; + [Description("The author of the module.\n") : Amended] String Author; +}; diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof new file mode 100644 index 00000000..04029309 --- /dev/null +++ b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof @@ -0,0 +1,21 @@ + +[ClassVersion("1.0.0.0"),FriendlyName("PSModule")] +class MSFT_PSModule : OMI_BaseResource +{ + [Key] String Name; + [Write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] String Ensure; + [Write] String Repository; + [Write,ValueMap{"Trusted", "Untrusted"},Values{"Trusted", "Untrusted"}] String InstallationPolicy; + [Write] String RequiredVersion; + [Write] String MaximumVersion; + [Write] String MinimumVersion; + [Write] Boolean Force; + [Write] Boolean AllowClobber; + [Write] Boolean SkipPublisherCheck; + [Read] string Description; + [Read] String InstalledVersion; + [Read] String Guid; + [Read] String ModuleBase; + [Read] String ModuleType; + [Read] String Author; +}; diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 new file mode 100644 index 00000000..7bae9ca6 --- /dev/null +++ b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 @@ -0,0 +1,37 @@ +# +# Copyright (c) Microsoft Corporation. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +ConvertFrom-StringData @' +###PSLOC + FailtoUninstall=Failed to uninstall the module '{0}'. Message: {1} + FailtoInstall=Failed to install the module '{0}'. Message: {1} + InDesiredState=Resource '{0}' is in the desired state + NotInDesiredState=Resource '{0}' is not in the desired state + ModuleFound=Module '{0}' found in the node + ModuleNotFound=Module '{0}' not found in the node + ModuleWithRightPropertyNotFound=Module '{0}' with the right version or other properties not found in the node. Message: {1} + ModuleNotFoundInRepository=Module '{0}' with the right version or other properties not found in the repository. Message: {1} + StartGetModule=Begin invoking get-module '{0}' + StartFindModule=Begin invoking find-module '{0}' + StartInstallModule=Begin invoking install-module '{0}' version '{1}' from '{2}' repository + StartUnInstallModule=Begin invoking uninstall of the module '{0}' + InstalledSuccess=Successfully installed the module '{0}' + UnInstalledSuccess=Successfully uninstalled the module '{0}' + VersionMismatch=The installed Module '{0}' has the version: '{1}' + RepositoryMismatch=The installed Module '{0}' is from '{1}' repository + FoundModulePath=Found the module path:'{0}' + MultipleModuleFound=Total: '{0}' modules found with the same name. Please use RequiredVersion for filtering. Message: {1} + InstallationPolicyWarning=You are installing the module '{0}' from an untrusted repository' {1}'. Your current InstallationPolicy is '{2}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. + InstallationPolicyFailed=Failed in the installation policy. Your current InstallationPolicy is '{0}' and the repository is '{1}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. +###PSLOC + +'@ + diff --git a/PowerShellGet/DSCResources/OneGetHelper.psm1 b/PowerShellGet/DSCResources/OneGetHelper.psm1 new file mode 100644 index 00000000..b6409bb5 --- /dev/null +++ b/PowerShellGet/DSCResources/OneGetHelper.psm1 @@ -0,0 +1,301 @@ +# +# Copyright (c) Microsoft Corporation. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +#Helper functions for PackageManagement DSC Resouces + + +Import-LocalizedData -BindingVariable LocalizedData -filename OneGetHelper.strings.psd1 + + + Function ExtractArguments +{ + <# + .SYNOPSIS + + This is a helper function that extract the parameters from a given table. + + .PARAMETER FunctionBoundParameters + Specifies the hashtable containing a set of parameters to be extracted + + .PARAMETER ArgumentNames + Specifies A list of arguments you want to extract + #> + + Param + ( + [parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $FunctionBoundParameters, + + #A list of arguments you want to extract + [parameter(Mandatory = $true)] + [System.String[]]$ArgumentNames + ) + + Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + + $returnValue=@{} + + foreach ($arg in $ArgumentNames) + { + if($FunctionBoundParameters.ContainsKey($arg)) + { + #Found an argument we are looking for, so we add it to return collection + $returnValue.Add($arg,$FunctionBoundParameters[$arg]) + } + } + + return $returnValue + } + +function ThrowError +{ + <# + .SYNOPSIS + + This is a helper function that throws an error. + + .PARAMETER ExceptionName + Specifies the type of errors, e.g. System.ArgumentException + + .PARAMETER ExceptionMessage + Specifies the exception message + + .PARAMETER ErrorId + Specifies an identifier of the error + + .PARAMETER ErrorCategory + Specifies the error category, e.g., InvalidArgument defined in System.Management.Automation. + + #> + + param + ( + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ExceptionName, + + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ExceptionMessage, + + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ErrorId, + + [parameter(Mandatory = $true)] + [ValidateNotNull()] + [System.Management.Automation.ErrorCategory] + $ErrorCategory + ) + + Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + + $exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage; + $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, $ErrorId, $ErrorCategory, $null) + throw $errorRecord +} + +Function ValidateArgument +{ + <# + .SYNOPSIS + + This is a helper function that validates the arguments. + + .PARAMETER Argument + Specifies the argument to be validated. + + .PARAMETER Type + Specifies the type of argument. + #> + + [CmdletBinding()] + param + ( + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$Argument, + + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String]$Type, + + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String]$ProviderName + ) + + Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + + switch ($Type) + { + + "SourceUri" + { + # Checks whether given URI represents specific scheme + # Most common schemes: file, http, https, ftp + $scheme =@('http', 'https', 'file', 'ftp') + + $newUri = $Argument -as [System.URI] + $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newuri.Scheme)) + + if ($returnValue -eq $false) + { + ThrowError -ExceptionName "System.ArgumentException" ` + -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` + -ErrorId "InValidUri" ` + -ErrorCategory InvalidArgument + } + + #Check whether it's a valid uri. Wait for the response within 2mins. + <#$result = Invoke-WebRequest $newUri -TimeoutSec 120 -UseBasicParsing -ErrorAction SilentlyContinue + + if ($null -eq (([xml]$result.Content).service )) + { + ThrowError -ExceptionName "System.ArgumentException" ` + -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` + -ErrorId "InValidUri" ` + -ErrorCategory InvalidArgument + }#> + + } + "DestinationPath" + { + $returnValue = Test-Path -Path $Argument + if ($returnValue -eq $false) + { + ThrowError -ExceptionName "System.ArgumentException" ` + -ExceptionMessage ($LocalizedData.PathDoesNotExist -f $Argument)` + -ErrorId "PathDoesNotExist" ` + -ErrorCategory InvalidArgument + } + } + "PackageSource" + { + #Argument can be either the package source Name or source Uri. + + #Check if the source is a uri + $uri = $Argument -as [System.URI] + + if($uri -and $uri.AbsoluteURI) + { + # Check if it's a valid Uri + ValidateArgument -Argument $Argument -Type "SourceUri" -ProviderName $ProviderName + } + else + { + #Check if it's a registered package source name + $source = PackageManagement\Get-PackageSource -Name $Argument -ProviderName $ProviderName -verbose -ErrorVariable ev + if ((-not $source) -or $ev) + { + #We do not need to throw error here as Get-PackageSource does already + Write-Verbose -Message ($LocalizedData.SourceNotFound -f $source) + } + } + } + default + { + ThrowError -ExceptionName "System.ArgumentException" ` + -ExceptionMessage ($LocalizedData.UnexpectedArgument -f $Type)` + -ErrorId "UnexpectedArgument" ` + -ErrorCategory InvalidArgument + } + } +} + +Function ValidateVersionArgument +{ + <# + .SYNOPSIS + + This is a helper function that does the version validation. + + .PARAMETER RequiredVersion + Provides the required version. + + .PARAMETER MaximumVersion + Provides the maximum version. + + .PARAMETER MinimumVersion + Provides the minimum version. + #> + + [CmdletBinding()] + param + ( + [string]$RequiredVersion, + [string]$MinimumVersion, + [string]$MaximumVersion + + ) + + Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + + $isValid = $false + + #Case 1: No further check required if a user provides either none or one of these: minimumVersion, maximumVersion, and requiredVersion + if ($PSBoundParameters.Count -le 1) + { + return $true + } + + #Case 2: #If no RequiredVersion is provided + if (-not $PSBoundParameters.ContainsKey('RequiredVersion')) + { + #If no RequiredVersion, both MinimumVersion and MaximumVersion are provided. Otherwise fall into the Case #1 + $isValid = $PSBoundParameters['MinimumVersion'] -le $PSBoundParameters['MaximumVersion'] + } + + #Case 3: RequiredVersion is provided. + # In this case MinimumVersion and/or MaximumVersion also are provided. Otherwise fall in to Case #1. + # This is an invalid case. When RequiredVersion is provided, others are not allowed. so $isValid is false, which is already set in the init + + if ($isValid -eq $false) + { + ThrowError -ExceptionName "System.ArgumentException" ` + -ExceptionMessage ($LocalizedData.VersionError)` + -ErrorId "VersionError" ` + -ErrorCategory InvalidArgument + } +} + +Function Get-InstallationPolicy +{ + <# + .SYNOPSIS + + This is a helper function that retrives the InstallationPolicy from the given repository. + + .PARAMETER RepositoryName + Provides the repository Name. + + #> + + Param + ( + [parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String]$RepositoryName + ) + + Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + + $repositoryobj = PackageManagement\Get-PackageSource -Name $RepositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + if ($repositoryobj) + { + return $repositoryobj.IsTrusted + } +} diff --git a/PowerShellGet/DSCResources/OneGetHelper.strings.psd1 b/PowerShellGet/DSCResources/OneGetHelper.strings.psd1 new file mode 100644 index 00000000..37420224 --- /dev/null +++ b/PowerShellGet/DSCResources/OneGetHelper.strings.psd1 @@ -0,0 +1,22 @@ +# +# Copyright (c) Microsoft Corporation. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# culture="en-US" +ConvertFrom-StringData @' +###PSLOC + InValidUri=InValid Uri: '{0}'. A sample valid uri: https://www.powershellgallery.com/api/v2/. + PathDoesNotExist=Path: '{0}' does not exist + VersionError=MinimumVersion should be less than the maximumVersion. The MinimumVersion or maximumVersion cannot be used with the RequiredVersion in the same command. + UnexpectedArgument=Unexpected argument type: '{0}' + SourceNotFound=Source '{0}' not found. Please make sure you register it. + CallingFunction="Call a function '{0}'". +###PSLOC +'@ From c1f05eb39fa9438b0b81097dde4be4ff7d4a5ac5 Mon Sep 17 00:00:00 2001 From: Outek Date: Thu, 17 Jan 2019 18:53:29 +0100 Subject: [PATCH 05/21] File renaming to PowerShellGet and Code cleanup, added some best practices (#400) --- .../MSFT_PSModule/MSFT_PSModule.psm1 | 487 +++++++++--------- .../MSFT_PSModule/MSFT_PSModule.strings.psd1 | 37 -- .../en-US/MSFT_PSModule.strings.psd1 | 36 ++ .../en-US/PowerShellGet.strings.psd1 | 22 + .../DSCResources/OneGetHelper.strings.psd1 | 22 - ...etHelper.psm1 => PowerShellGetHelper.psm1} | 209 ++++---- 6 files changed, 393 insertions(+), 420 deletions(-) delete mode 100644 PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 create mode 100644 PowerShellGet/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 create mode 100644 PowerShellGet/DSCResources/MSFT_PSModule/en-US/PowerShellGet.strings.psd1 delete mode 100644 PowerShellGet/DSCResources/OneGetHelper.strings.psd1 rename PowerShellGet/DSCResources/{OneGetHelper.psm1 => PowerShellGetHelper.psm1} (50%) diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 index c6cf548f..df602590 100644 --- a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 +++ b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 @@ -10,49 +10,51 @@ # THE SOFTWARE. # -Import-LocalizedData -BindingVariable LocalizedData -filename MSFT_PSModule.strings.psd1 -Import-Module -Name "$PSScriptRoot\..\OneGetHelper.psm1" +Import-LocalizedData -BindingVariable LocalizedData -filename MSFT_PSModule.strings.psd1 +$script:localizedData = Get-LocalizedData ` + -ResourceName 'MSFT_PSModule' ` + -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) -#DSC Resource for the $CurrentProviderName -$CurrentProviderName="PowerShellGet" +Import-Module -Name "$PSScriptRoot\..\PowerShellGetHelper.psm1" +# DSC Resource for the $CurrentProviderName. +$CurrentProviderName = "PowerShellGet" -#Return the current state of the resource +# Return the current state of the resource. function Get-TargetResource { <# .SYNOPSIS + This DSC resource provides a mechanism to download PowerShell modules from the PowerShell + Gallery and install it on your computer. - This DSC resource provides a mechanism to download PowerShell modules from the PowerShell - Gallery and install it on your computer. - - Get-TargetResource returns the current state of the resource. + Get-TargetResource returns the current state of the resource. .PARAMETER Name - Specifies the name of the PowerShell module to be installed or uninstalled. + Specifies the name of the PowerShell module to be installed or uninstalled. .PARAMETER Repository - Specifies the name of the module source repository where the module can be found. + Specifies the name of the module source repository where the module can be found. .PARAMETER RequiredVersion - Provides the version of the module you want to install or uninstall. + Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion - Provides the maximum version of the module you want to install or uninstall. + Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion - Provides the minimum version of the module you want to install or uninstall. + Provides the minimum version of the module you want to install or uninstall. .PARAMETER Force - Forces the installation of modules. If a module of the same name and version already exists on the computer, - this parameter overwrites the existing module with one of the same name that was found by the command. - + Forces the installation of modules. If a module of the same name and version already exists on the computer, + this parameter overwrites the existing module with one of the same name that was found by the command. + .PARAMETER AllowClobber - Allows the installation of modules regardless of if other existing module on the computer have cmdlets - of the same name - + Allows the installation of modules regardless of if other existing module on the computer have cmdlets + of the same name. + .PARAMETER SkipPublisherCheck - Allows the installation of modules that have not been catalog signed + Allows the installation of modules that have not been catalog signed. #> [CmdletBinding()] @@ -64,8 +66,8 @@ function Get-TargetResource $Name, [System.String] - $Repository="PSGallery", - + $Repository = "PSGallery", + [System.String] $RequiredVersion, @@ -80,44 +82,44 @@ function Get-TargetResource [Switch] $AllowClobber, - + [Switch] $SkipPublisherCheck ) - #Initialize the $Ensure variable + # Initialize the $Ensure variable. $ensure = 'Absent' - + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") - #Get the module with the right version and repository properties + # Get the module with the right version and repository properties. $modules = Get-RightModule @extractedArguments -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - #If the module is found, the count > 0 + # If the module is found, the count > 0 if ($modules.count -gt 0) { $ensure = 'Present' - Write-Verbose -Message ($localizedData.ModuleFound -f $($Name)) + Write-Verbose -Message ($localizedData.ModuleFound -f $($Name)) } else { Write-Verbose -Message ($localizedData.ModuleNotFound -f $($Name)) } - + Write-Debug -Message "Ensure of $($Name) module is $($ensure)" if ($ensure -eq 'Absent') { - return @{ - Ensure = $ensure - Name = $Name - } + $returnValue = @{ + Ensure = $ensure + Name = $Name + } } else { - #Find a module with the latest version and return its properties + # Find a module with the latest version and return its properties. $latestModule = $modules[0] foreach ($module in $modules) @@ -128,87 +130,88 @@ function Get-TargetResource } } - #Check if the repository matches + # Check if the repository matches. $repositoryName = Get-ModuleRepositoryName -Module $latestModule -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - $installationPolicy = Get-InstallationPolicy -RepositoryName $repositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - - return @{ - Ensure = $ensure - Name = $Name - Repository = $repositoryName - Description = $latestModule.Description - Guid = $latestModule.Guid - ModuleBase = $latestModule.ModuleBase - ModuleType = $latestModule.ModuleType - Author = $latestModule.Author - InstalledVersion = $latestModule.Version - InstallationPolicy=if($installationPolicy) {"Trusted"}else{"Untrusted"} - } + $installationPolicy = Get-InstallationPolicy -RepositoryName $repositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + $returnValue = @{ + Ensure = $ensure + Name = $Name + Repository = $repositoryName + Description = $latestModule.Description + Guid = $latestModule.Guid + ModuleBase = $latestModule.ModuleBase + ModuleType = $latestModule.ModuleType + Author = $latestModule.Author + InstalledVersion = $latestModule.Version + InstallationPolicy = if ($installationPolicy) {"Trusted"}else {"Untrusted"} + } } + return $returnValue } function Test-TargetResource { <# .SYNOPSIS + This DSC resource provides a mechanism to download PowerShell modules from the PowerShell + Gallery and install it on your computer. - This DSC resource provides a mechanism to download PowerShell modules from the PowerShell - Gallery and install it on your computer. - - Test-TargetResource validates whether the resource is currently in the desired state. + Test-TargetResource validates whether the resource is currently in the desired state. .PARAMETER Ensure - Determines whether the module to be installed or uninstalled. + Determines whether the module to be installed or uninstalled. .PARAMETER Name - Specifies the name of the PowerShell module to be installed or uninstalled. + Specifies the name of the PowerShell module to be installed or uninstalled. .PARAMETER Repository - Specifies the name of the module source repository where the module can be found. + Specifies the name of the module source repository where the module can be found. .PARAMETER InstallationPolicy - Determines whether you trust the source repository where the module resides. + Determines whether you trust the source repository where the module resides. .PARAMETER RequiredVersion - Provides the version of the module you want to install or uninstall. + Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion - Provides the maximum version of the module you want to install or uninstall. + Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion - Provides the minimum version of the module you want to install or uninstall. + Provides the minimum version of the module you want to install or uninstall. .PARAMETER Force - Forces the installation of modules. If a module of the same name and version already exists on the computer, - this parameter overwrites the existing module with one of the same name that was found by the command. - + Forces the installation of modules. If a module of the same name and version already exists on the computer, + this parameter overwrites the existing module with one of the same name that was found by the command. + .PARAMETER AllowClobber - Allows the installation of modules regardless of if other existing module on the computer have cmdlets - of the same name - + Allows the installation of modules regardless of if other existing module on the computer have cmdlets + of the same name. + .PARAMETER SkipPublisherCheck - Allows the installation of modules that have not been catalog signed + Allows the installation of modules that have not been catalog signed. #> + [CmdletBinding()] [OutputType([System.Boolean])] param ( - [ValidateSet("Present","Absent")] + [ValidateSet("Present", "Absent")] [System.String] - $Ensure="Present", + $Ensure = "Present", [parameter(Mandatory = $true)] [System.String] $Name, [System.String] - $Repository="PSGallery", + $Repository = "PSGallery", - [ValidateSet("Trusted","Untrusted")] + [ValidateSet("Trusted", "Untrusted")] [System.String] - $InstallationPolicy="Untrusted", - + $InstallationPolicy = "Untrusted", + [System.String] $RequiredVersion, @@ -220,10 +223,10 @@ function Test-TargetResource [Switch] $Force, - + [Switch] $AllowClobber, - + [Switch] $SkipPublisherCheck ) @@ -231,84 +234,82 @@ function Test-TargetResource Write-Debug -Message "Calling Test-TargetResource" $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") $status = Get-TargetResource @extractedArguments - #The ensure returned from Get-TargetResource is not equal to the desired $Ensure - # + # The ensure returned from Get-TargetResource is not equal to the desired $Ensure. if ($status.Ensure -ieq $Ensure) { - Write-Verbose -Message ($localizedData.InDesiredState -f $Name) - return $true + Write-Verbose -Message ($localizedData.InDesiredState -f $Name) + return $true } else { - Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name) + Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name) return $false - } + } } - + function Set-TargetResource { <# .SYNOPSIS + This DSC resource provides a mechanism to download PowerShell modules from the PowerShell + Gallery and install it on your computer. - This DSC resource provides a mechanism to download PowerShell modules from the PowerShell - Gallery and install it on your computer. - - Set-TargetResource sets the resource to the desired state. "Make it so". + Set-TargetResource sets the resource to the desired state. "Make it so". .PARAMETER Ensure - Determines whether the module to be installed or uninstalled. + Determines whether the module to be installed or uninstalled. .PARAMETER Name - Specifies the name of the PowerShell module to be installed or uninstalled. + Specifies the name of the PowerShell module to be installed or uninstalled. .PARAMETER Repository - Specifies the name of the module source repository where the module can be found. + Specifies the name of the module source repository where the module can be found. .PARAMETER InstallationPolicy - Determines whether you trust the source repository where the module resides. + Determines whether you trust the source repository where the module resides. .PARAMETER RequiredVersion - Provides the version of the module you want to install or uninstall. + Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion - Provides the maximum version of the module you want to install or uninstall. + Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion - Provides the minimum version of the module you want to install or uninstall. + Provides the minimum version of the module you want to install or uninstall. .PARAMETER Force - Forces the installation of modules. If a module of the same name and version already exists on the computer, - this parameter overwrites the existing module with one of the same name that was found by the command. - + Forces the installation of modules. If a module of the same name and version already exists on the computer, + this parameter overwrites the existing module with one of the same name that was found by the command. + .PARAMETER AllowClobber - Allows the installation of modules regardless of if other existing module on the computer have cmdlets - of the same name - + Allows the installation of modules regardless of if other existing module on the computer have cmdlets + of the same name. + .PARAMETER SkipPublisherCheck - Allows the installation of modules that have not been catalog signed + Allows the installation of modules that have not been catalog signed. #> [CmdletBinding()] param ( - [ValidateSet("Present","Absent")] + [ValidateSet("Present", "Absent")] [System.String] - $Ensure="Present", + $Ensure = "Present", [parameter(Mandatory = $true)] [System.String] $Name, [System.String] - $Repository="PSGallery", + $Repository = "PSGallery", - [ValidateSet("Trusted","Untrusted")] + [ValidateSet("Trusted", "Untrusted")] [System.String] - $InstallationPolicy="Untrusted", + $InstallationPolicy = "Untrusted", [System.String] $RequiredVersion, @@ -329,169 +330,159 @@ function Set-TargetResource $SkipPublisherCheck ) - - #Validate the repository argument + # Validate the repository argument if ($PSBoundParameters.ContainsKey("Repository")) { - ValidateArgument -Argument $Repository -Type "PackageSource" -ProviderName $CurrentProviderName -Verbose + ValidateArgument -Argument $Repository -Type "PackageSource" -ProviderName $CurrentProviderName -Verbose } - if($Ensure -ieq "Present") - { - - #Version check + if ($Ensure -ieq "Present") + { + # Version check $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("MinimumVersion","MaximumVersion", "RequiredVersion") + -ArgumentNames ("MinimumVersion", "MaximumVersion", "RequiredVersion") ValidateVersionArgument @extractedArguments $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name","Repository", "MinimumVersion", "MaximumVersion","RequiredVersion") - + -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + Write-Verbose -Message ($localizedData.StartFindmodule -f $($Name)) - - - $modules = Find-Module @extractedArguments -ErrorVariable ev + $modules = Find-Module @extractedArguments -ErrorVariable ev if (-not $modules) - { - - ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.ModuleNotFoundInRepository -f $Name, $ev.Exception) ` - -ErrorId "ModuleNotFoundInRepository" ` - -ErrorCategory InvalidOperation + { + ThrowError -ExceptionName "System.InvalidOperationException" ` + -ExceptionMessage ($localizedData.ModuleNotFoundInRepository -f $Name, $ev.Exception) ` + -ErrorId "ModuleNotFoundInRepository" ` + -ErrorCategory InvalidOperation } - + $trusted = $null $moduleFound = $null foreach ($m in $modules) { - #Check for the installation policy - $trusted = Get-InstallationPolicy -RepositoryName $m.Repository -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - - #Stop the loop if found a trusted repository + # Check for the installation policy. + $trusted = Get-InstallationPolicy -RepositoryName $m.Repository -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + + # Stop the loop if found a trusted repository. if ($trusted) - { + { $moduleFound = $m break; - } + } } - - #The respository is trusted, so we install it + # The respository is trusted, so we install it. if ($trusted) { - Write-Verbose -Message ($localizedData.StartInstallModule -f $Name, $moduleFound.Version.toString(), $moduleFound.Repository ) - - #Extract the installation options - $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("Force","AllowClobber", "SkipPublisherCheck") - + Write-Verbose -Message ($localizedData.StartInstallModule -f $Name, $moduleFound.Version.toString(), $moduleFound.Repository) + + # Extract the installation options. + $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("Force", "AllowClobber", "SkipPublisherCheck") + $moduleFound | Install-Module -ErrorVariable ev } - #The repository is untrusted but user's installation policy is trusted, so we install it with a warning + # The repository is untrusted but user's installation policy is trusted, so we install it with a warning. elseif ($InstallationPolicy -ieq 'Trusted') - { + { Write-Warning -Message ($localizedData.InstallationPolicyWarning -f $Name, $modules[0].Repository, $InstallationPolicy) - # Extract installation options (Force implied by InstallationPolicy) + # Extract installation options (Force implied by InstallationPolicy). $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("AllowClobber", "SkipPublisherCheck") - - #if all the repositories are untrusted, we choose the first one + + # If all the repositories are untrusted, we choose the first one. $modules[0] | Install-Module @extractedSwitches -Force -ErrorVariable ev } - #Both user and repository is untrusted + # Both user and repository is untrusted else { ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.InstallationPolicyFailed -f $InstallationPolicy, "Untrusted") ` - -ErrorId "InstallationPolicyFailed" ` - -ErrorCategory InvalidOperation - } + -ExceptionMessage ($localizedData.InstallationPolicyFailed -f $InstallationPolicy, "Untrusted") ` + -ErrorId "InstallationPolicyFailed" ` + -ErrorCategory InvalidOperation + } - if ($ev) + if ($ev) { ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.FailtoInstall -f $Name, $ev.Exception) ` - -ErrorId "FailtoInstall" ` - -ErrorCategory InvalidOperation + -ExceptionMessage ($localizedData.FailtoInstall -f $Name, $ev.Exception) ` + -ErrorId "FailtoInstall" ` + -ErrorCategory InvalidOperation } else { Write-Verbose -Message ($localizedData.InstalledSuccess -f $($Name)) } - } - #Ensure=Absent - else - { - + } + # Ensure=Absent + else + { + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") - - #Get the module with the right version and repository properties + # Get the module with the right version and repository properties. $modules = Get-RightModule @extractedArguments -ErrorVariable ev if ((-not $modules) -or $ev) - { + { ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.ModuleWithRightPropertyNotFound -f $Name, $ev.Exception) ` - -ErrorId "ModuleWithRightPropertyNotFound" ` - -ErrorCategory InvalidOperation + -ExceptionMessage ($localizedData.ModuleWithRightPropertyNotFound -f $Name, $ev.Exception) ` + -ErrorId "ModuleWithRightPropertyNotFound" ` + -ErrorCategory InvalidOperation } - + foreach ($module in $modules) - { - #Get the path where the module is installed - $path=$module.ModuleBase + { + # Get the path where the module is installed. + $path = $module.ModuleBase Write-Verbose -Message ($localizedData.StartUnInstallModule -f $($Name)) - - #There is no Uninstall-Module cmdlet exists, so we will remove the ModuleBase folder as an uninstall operation + + # There is no Uninstall-Module cmdlet exists, so we will remove the ModuleBase folder as an uninstall operation. Microsoft.PowerShell.Management\Remove-Item -Path $path -Force -Recurse -ErrorVariable ev - - if($ev) - { + + if ($ev) + { ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.FailtoUninstall -f $module.Name, $ev.Exception) ` - -ErrorId "FailtoUninstall" ` - -ErrorCategory InvalidOperation + -ExceptionMessage ($localizedData.FailtoUninstall -f $module.Name, $ev.Exception) ` + -ErrorId "FailtoUninstall" ` + -ErrorCategory InvalidOperation } else { - Write-Verbose -Message ($localizedData.UnInstalledSuccess -f $($module.Name)) + Write-Verbose -Message ($localizedData.UnInstalledSuccess -f $($module.Name)) } - - }#foreach - - } #Ensure=Absent + } # foreach + } # Ensure=Absent } - Function Get-RightModule { <# .SYNOPSIS - - This is a helper function. It returns the modules that meet the specified versions and the repository requirements + This is a helper function. It returns the modules that meet the specified versions and the repository requirements. .PARAMETER Name - Specifies the name of the PowerShell module. + Specifies the name of the PowerShell module. .PARAMETER RequiredVersion - Provides the version of the module you want to install or uninstall. + Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion - Provides the maximum version of the module you want to install or uninstall. + Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion - Provides the minimum version of the module you want to install or uninstall. - + Provides the minimum version of the module you want to install or uninstall. + .PARAMETER Repository - Specifies the name of the module source repository where the module can be found. + Specifies the name of the module source repository where the module can be found. #> + [CmdletBinding()] param ( [parameter(Mandatory = $true)] @@ -512,54 +503,49 @@ Function Get-RightModule $Repository ) - Write-Verbose -Message ($localizedData.StartGetModule -f $($Name)) - + $modules = Microsoft.PowerShell.Core\Get-Module -Name $Name -ListAvailable -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - + if (-not $modules) - { + { return $null } - # - #As Get-Module does not take RequiredVersion, MinimumVersion, MaximumVersion, or Repository, below we need to check - #whether the modules are containing the right version and repository location. - - $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("MaximumVersion","MinimumVersion", "RequiredVersion") + # As Get-Module does not take RequiredVersion, MinimumVersion, MaximumVersion, or Repository, below we need to check + # whether the modules are containing the right version and repository location. - $returnVal =@() + $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ("MaximumVersion", "MinimumVersion", "RequiredVersion") + $returnVal = @() foreach ($m in $modules) - { + { $versionMatch = $false $installedVersion = $m.Version - #Case 1 - a user provides none of RequiredVersion, MinimumVersion, MaximumVersion - + # Case 1 - a user provides none of RequiredVersion, MinimumVersion, MaximumVersion if ($extractedArguments.Count -eq 0) { - $versionMatch = $true + $versionMatch = $true } - # - #Case 2 - a user provides RequiredVersion - # + + # Case 2 - a user provides RequiredVersion elseif ($extractedArguments.ContainsKey("RequiredVersion")) { - #Check if it matches with the installedversion + # Check if it matches with the installedversion $versionMatch = ($installedVersion -eq [System.Version]$RequiredVersion) - } + } else - { - #Case 3 - a user provides MinimumVersion + { + + # Case 3 - a user provides MinimumVersion if ($extractedArguments.ContainsKey("MinimumVersion")) { $versionMatch = ($installedVersion -ge [System.Version]$extractedArguments['MinimumVersion']) } - # - #Case 4 - a user provides MaximumVersion - # + + # Case 4 - a user provides MaximumVersion if ($extractedArguments.ContainsKey("MaximumVersion")) { $isLessThanMax = ($installedVersion -le [System.Version]$extractedArguments['MaximumVersion']) @@ -573,72 +559,67 @@ Function Get-RightModule $versionMatch = $isLessThanMax } } - #Case 5 - Both MinimumVersion and MaximumVersion are provided. it's covered by the above - - #Do not return $false yet to allow the foreach to continue + + # Case 5 - Both MinimumVersion and MaximumVersion are provided. It's covered by the above. + # Do not return $false yet to allow the foreach to continue if (-not $versionMatch) { - Write-Verbose -Message ($localizedData.VersionMismatch -f $($Name), $($installedVersion)) + Write-Verbose -Message ($localizedData.VersionMismatch -f $($Name), $($installedVersion)) $versionMatch = $false - } + } } - #Case 6 - Version matches but need to check if the module is from the right repository - # + # Case 6 - Version matches but need to check if the module is from the right repository. if ($versionMatch) - { - #a user does not provide Repository, we are good + { + # a user does not provide Repository, we are good if (-not $PSBoundParameters.ContainsKey("Repository")) - { - Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") - $returnVal+=$m - + { + Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") + $returnVal += $m } else { - #Check if the Repository matches + # Check if the Repository matches $sourceName = Get-ModuleRepositoryName -Module $m if ($Repository -ieq $sourceName) { - Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") - $returnVal+=$m + Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") + $returnVal += $m } else { - Write-Verbose -Message ($localizedData.RepositoryMismatch -f $($Name), $($sourceName)) - } - } + Write-Verbose -Message ($localizedData.RepositoryMismatch -f $($Name), $($sourceName)) + } + } } - - } #foreach - - return $returnVal + } # foreach + return $returnVal } - + Function Get-ModuleRepositoryName { <# .SYNOPSIS - - This is a helper function that returns the module's repository name + This is a helper function that returns the module's repository name. .PARAMETER Module - Specifies the name of the PowerShell module. + Specifies the name of the PowerShell module. #> - Param + + [CmdletBinding()] + param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [System.Object]$Module + [System.Object] + $Module ) - - #RepositorySourceLocation property is supported in PS V5 only. To work with the earlier PS version, we need to do a different way. - #PSGetModuleInfo.xml exists for any PS modules downloaded through PSModule provider. - + # RepositorySourceLocation property is supported in PS V5 only. To work with the earlier PS version, we need to do a different way. + # PSGetModuleInfo.xml exists for any PS modules downloaded through PSModule provider. $psGetModuleInfoFileName = "PSGetModuleInfo.xml" - $psGetModuleInfoPath = Microsoft.PowerShell.Management\Join-Path -Path $Module.ModuleBase -ChildPath $psGetModuleInfoFileName Write-Verbose -Message ($localizedData.FoundModulePath -f $($psGetModuleInfoPath)) @@ -648,7 +629,7 @@ Function Get-ModuleRepositoryName $psGetModuleInfo = Microsoft.PowerShell.Utility\Import-Clixml -Path $psGetModuleInfoPath return $psGetModuleInfo.Repository - } + } } Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 b/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 deleted file mode 100644 index 7bae9ca6..00000000 --- a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.strings.psd1 +++ /dev/null @@ -1,37 +0,0 @@ -# -# Copyright (c) Microsoft Corporation. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -ConvertFrom-StringData @' -###PSLOC - FailtoUninstall=Failed to uninstall the module '{0}'. Message: {1} - FailtoInstall=Failed to install the module '{0}'. Message: {1} - InDesiredState=Resource '{0}' is in the desired state - NotInDesiredState=Resource '{0}' is not in the desired state - ModuleFound=Module '{0}' found in the node - ModuleNotFound=Module '{0}' not found in the node - ModuleWithRightPropertyNotFound=Module '{0}' with the right version or other properties not found in the node. Message: {1} - ModuleNotFoundInRepository=Module '{0}' with the right version or other properties not found in the repository. Message: {1} - StartGetModule=Begin invoking get-module '{0}' - StartFindModule=Begin invoking find-module '{0}' - StartInstallModule=Begin invoking install-module '{0}' version '{1}' from '{2}' repository - StartUnInstallModule=Begin invoking uninstall of the module '{0}' - InstalledSuccess=Successfully installed the module '{0}' - UnInstalledSuccess=Successfully uninstalled the module '{0}' - VersionMismatch=The installed Module '{0}' has the version: '{1}' - RepositoryMismatch=The installed Module '{0}' is from '{1}' repository - FoundModulePath=Found the module path:'{0}' - MultipleModuleFound=Total: '{0}' modules found with the same name. Please use RequiredVersion for filtering. Message: {1} - InstallationPolicyWarning=You are installing the module '{0}' from an untrusted repository' {1}'. Your current InstallationPolicy is '{2}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. - InstallationPolicyFailed=Failed in the installation policy. Your current InstallationPolicy is '{0}' and the repository is '{1}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. -###PSLOC - -'@ - diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 b/PowerShellGet/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 new file mode 100644 index 00000000..0eb5b1f7 --- /dev/null +++ b/PowerShellGet/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 @@ -0,0 +1,36 @@ +# +# Copyright (c) Microsoft Corporation. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# culture = "en-US" +ConvertFrom-StringData -StringData @' +###PSLOC + FailtoUninstall = Failed to uninstall the module '{0}'. Message: {1} + FailtoInstall = Failed to install the module '{0}'. Message: {1} + InDesiredState = Resource '{0}' is in the desired state. + NotInDesiredState = Resource '{0}' is not in the desired state. + ModuleFound = Module '{0}' found in the node. + ModuleNotFound = Module '{0}' not found in the node. + ModuleWithRightPropertyNotFound = Module '{0}' with the right version or other properties not found in the node. Message: {1} + ModuleNotFoundInRepository = Module '{0}' with the right version or other properties not found in the repository. Message: {1} + StartGetModule = Begin invoking Get-Module '{0}' + StartFindModule = Begin invoking Find-Module '{0}' + StartInstallModule = Begin invoking Install-Module '{0}' version '{1}' from '{2}' repository. + StartUnInstallModule = Begin invoking uninstall of the module '{0}' + InstalledSuccess = Successfully installed the module '{0}' + UnInstalledSuccess = Successfully uninstalled the module '{0}' + VersionMismatch = The installed Module '{0}' has the version: '{1}' + RepositoryMismatch = The installed Module '{0}' is from '{1}' repository. + FoundModulePath = Found the module path: '{0}' + MultipleModuleFound = Total: '{0}' modules found with the same name. Please use -RequiredVersion for filtering. Message: {1} + InstallationPolicyWarning = You are installing the module '{0}' from an untrusted repository' {1}'. Your current InstallationPolicy is '{2}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. + InstallationPolicyFailed = Failed in the installation policy. Your current InstallationPolicy is '{0}' and the repository is '{1}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. +###PSLOC +'@ diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/en-US/PowerShellGet.strings.psd1 b/PowerShellGet/DSCResources/MSFT_PSModule/en-US/PowerShellGet.strings.psd1 new file mode 100644 index 00000000..ec703201 --- /dev/null +++ b/PowerShellGet/DSCResources/MSFT_PSModule/en-US/PowerShellGet.strings.psd1 @@ -0,0 +1,22 @@ +# +# Copyright (c) Microsoft Corporation. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# culture = "en-US" +ConvertFrom-StringData -StringData @' +###PSLOC + InValidUri = InValid Uri: '{0}'. A sample valid uri: https://www.powershellgallery.com/api/v2/. + PathDoesNotExist = Path: '{0}' does not exist. + VersionError = MinimumVersion should be less than the MaximumVersion. The MinimumVersion or MaximumVersion cannot be used with the RequiredVersion in the same command. + UnexpectedArgument = Unexpected argument type: '{0}'. + SourceNotFound = Source '{0}' not found. Please make sure you register it. + CallingFunction = "Call a function '{0}'". +###PSLOC +'@ diff --git a/PowerShellGet/DSCResources/OneGetHelper.strings.psd1 b/PowerShellGet/DSCResources/OneGetHelper.strings.psd1 deleted file mode 100644 index 37420224..00000000 --- a/PowerShellGet/DSCResources/OneGetHelper.strings.psd1 +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) Microsoft Corporation. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# culture="en-US" -ConvertFrom-StringData @' -###PSLOC - InValidUri=InValid Uri: '{0}'. A sample valid uri: https://www.powershellgallery.com/api/v2/. - PathDoesNotExist=Path: '{0}' does not exist - VersionError=MinimumVersion should be less than the maximumVersion. The MinimumVersion or maximumVersion cannot be used with the RequiredVersion in the same command. - UnexpectedArgument=Unexpected argument type: '{0}' - SourceNotFound=Source '{0}' not found. Please make sure you register it. - CallingFunction="Call a function '{0}'". -###PSLOC -'@ diff --git a/PowerShellGet/DSCResources/OneGetHelper.psm1 b/PowerShellGet/DSCResources/PowerShellGetHelper.psm1 similarity index 50% rename from PowerShellGet/DSCResources/OneGetHelper.psm1 rename to PowerShellGet/DSCResources/PowerShellGetHelper.psm1 index b6409bb5..0c22eba6 100644 --- a/PowerShellGet/DSCResources/OneGetHelper.psm1 +++ b/PowerShellGet/DSCResources/PowerShellGetHelper.psm1 @@ -9,85 +9,84 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # -#Helper functions for PackageManagement DSC Resouces +# Helper functions for PackageManagement DSC Resouces +# Import Localization Strings +$script:localizedData = Get-LocalizedData ` + -ResourceName 'PowerShellGet' ` + -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) -Import-LocalizedData -BindingVariable LocalizedData -filename OneGetHelper.strings.psd1 - - - Function ExtractArguments +Function ExtractArguments { <# .SYNOPSIS - - This is a helper function that extract the parameters from a given table. + This is a helper function that extract the parameters from a given table. .PARAMETER FunctionBoundParameters - Specifies the hashtable containing a set of parameters to be extracted + Specifies the hashtable containing a set of parameters to be extracted. .PARAMETER ArgumentNames - Specifies A list of arguments you want to extract + Specifies a list of arguments you want to extract. #> - - Param + + [CmdletBinding()] + param ( [parameter(Mandatory = $true)] [System.Collections.Hashtable] $FunctionBoundParameters, - #A list of arguments you want to extract [parameter(Mandatory = $true)] - [System.String[]]$ArgumentNames + [System.String[]] + $ArgumentNames ) - Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) - $returnValue=@{} + $returnValue = @{} foreach ($arg in $ArgumentNames) { - if($FunctionBoundParameters.ContainsKey($arg)) + if ($FunctionBoundParameters.ContainsKey($arg)) { - #Found an argument we are looking for, so we add it to return collection - $returnValue.Add($arg,$FunctionBoundParameters[$arg]) + # Found an argument we are looking for, so we add it to return collection. + $returnValue.Add($arg, $FunctionBoundParameters[$arg]) } } - return $returnValue - } +} -function ThrowError +Function ThrowError { <# .SYNOPSIS - - This is a helper function that throws an error. + This is a helper function that throws an error. .PARAMETER ExceptionName - Specifies the type of errors, e.g. System.ArgumentException + Specifies the type of errors, e.g. System.ArgumentException. .PARAMETER ExceptionMessage - Specifies the exception message + Specifies the exception message. .PARAMETER ErrorId - Specifies an identifier of the error + Specifies an identifier of the error. .PARAMETER ErrorCategory - Specifies the error category, e.g., InvalidArgument defined in System.Management.Automation. - + Specifies the error category, e.g., InvalidArgument defined in System.Management.Automation. #> + [CmdletBinding()] param - ( + ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [System.String] + [System.String] $ExceptionName, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $ExceptionMessage, + $ExceptionMessage, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -99,11 +98,12 @@ function ThrowError [System.Management.Automation.ErrorCategory] $ErrorCategory ) - - Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) - - $exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage; - $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, $ErrorId, $ErrorCategory, $null) + + Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) + + $exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage; + $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, $ErrorId, $ErrorCategory, $null) + throw $errorRecord } @@ -111,14 +111,13 @@ Function ValidateArgument { <# .SYNOPSIS - - This is a helper function that validates the arguments. + This is a helper function that validates the arguments. .PARAMETER Argument - Specifies the argument to be validated. + Specifies the argument to be validated. .PARAMETER Type - Specifies the type of argument. + Specifies the type of argument. #> [CmdletBinding()] @@ -126,148 +125,143 @@ Function ValidateArgument ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$Argument, + [System.String] + $Argument, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String]$Type, + [System.String] + $Type, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String]$ProviderName + [System.String] + $ProviderName ) - Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) switch ($Type) { - "SourceUri" { # Checks whether given URI represents specific scheme - # Most common schemes: file, http, https, ftp - $scheme =@('http', 'https', 'file', 'ftp') + # Most common schemes: file, http, https, ftp + $scheme = @('http', 'https', 'file', 'ftp') $newUri = $Argument -as [System.URI] - $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newuri.Scheme)) + $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newuri.Scheme)) if ($returnValue -eq $false) - { - ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` - -ErrorId "InValidUri" ` - -ErrorCategory InvalidArgument - } - - #Check whether it's a valid uri. Wait for the response within 2mins. - <#$result = Invoke-WebRequest $newUri -TimeoutSec 120 -UseBasicParsing -ErrorAction SilentlyContinue - - if ($null -eq (([xml]$result.Content).service )) { ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` - -ErrorId "InValidUri" ` - -ErrorCategory InvalidArgument - }#> - + -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` + -ErrorId "InValidUri" ` + -ErrorCategory InvalidArgument + } } "DestinationPath" { $returnValue = Test-Path -Path $Argument + if ($returnValue -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.PathDoesNotExist -f $Argument)` - -ErrorId "PathDoesNotExist" ` - -ErrorCategory InvalidArgument + -ExceptionMessage ($LocalizedData.PathDoesNotExist -f $Argument)` + -ErrorId "PathDoesNotExist" ` + -ErrorCategory InvalidArgument } } "PackageSource" - { - #Argument can be either the package source Name or source Uri. + { + # Argument can be either the package source Name or source Uri. - #Check if the source is a uri - $uri = $Argument -as [System.URI] + # Check if the source is a Uri. + $uri = $Argument -as [System.URI] - if($uri -and $uri.AbsoluteURI) + if ($uri -and $uri.AbsoluteURI) { - # Check if it's a valid Uri + # Check if it's a valid Uri. ValidateArgument -Argument $Argument -Type "SourceUri" -ProviderName $ProviderName } else { - #Check if it's a registered package source name + # Check if it's a registered package source name. $source = PackageManagement\Get-PackageSource -Name $Argument -ProviderName $ProviderName -verbose -ErrorVariable ev + if ((-not $source) -or $ev) { - #We do not need to throw error here as Get-PackageSource does already - Write-Verbose -Message ($LocalizedData.SourceNotFound -f $source) + # We do not need to throw error here as Get-PackageSource does already. + Write-Verbose -Message ($LocalizedData.SourceNotFound -f $source) } } } default { ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.UnexpectedArgument -f $Type)` - -ErrorId "UnexpectedArgument" ` - -ErrorCategory InvalidArgument + -ExceptionMessage ($LocalizedData.UnexpectedArgument -f $Type)` + -ErrorId "UnexpectedArgument" ` + -ErrorCategory InvalidArgument } - } + } } Function ValidateVersionArgument { <# .SYNOPSIS - - This is a helper function that does the version validation. + This is a helper function that does the version validation. .PARAMETER RequiredVersion - Provides the required version. + Provides the required version. .PARAMETER MaximumVersion - Provides the maximum version. + Provides the maximum version. .PARAMETER MinimumVersion - Provides the minimum version. + Provides the minimum version. #> [CmdletBinding()] param ( - [string]$RequiredVersion, - [string]$MinimumVersion, - [string]$MaximumVersion + [System.String] + $RequiredVersion, + [System.String] + $MinimumVersion, + + [System.String] + $MaximumVersion ) - - Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + + Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) $isValid = $false - - #Case 1: No further check required if a user provides either none or one of these: minimumVersion, maximumVersion, and requiredVersion + + # Case 1: No further check required if a user provides either none or one of these: minimumVersion, maximumVersion, and requiredVersion. if ($PSBoundParameters.Count -le 1) { return $true } - #Case 2: #If no RequiredVersion is provided + # Case 2: #If no RequiredVersion is provided. if (-not $PSBoundParameters.ContainsKey('RequiredVersion')) { - #If no RequiredVersion, both MinimumVersion and MaximumVersion are provided. Otherwise fall into the Case #1 + # If no RequiredVersion, both MinimumVersion and MaximumVersion are provided. Otherwise fall into the Case #1. $isValid = $PSBoundParameters['MinimumVersion'] -le $PSBoundParameters['MaximumVersion'] } - - #Case 3: RequiredVersion is provided. + + # Case 3: RequiredVersion is provided. # In this case MinimumVersion and/or MaximumVersion also are provided. Otherwise fall in to Case #1. - # This is an invalid case. When RequiredVersion is provided, others are not allowed. so $isValid is false, which is already set in the init + # This is an invalid case. When RequiredVersion is provided, others are not allowed. so $isValid is false, which is already set in the init. if ($isValid -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.VersionError)` - -ErrorId "VersionError" ` - -ErrorCategory InvalidArgument + -ExceptionMessage ($LocalizedData.VersionError)` + -ErrorId "VersionError" ` + -ErrorCategory InvalidArgument } } @@ -275,19 +269,18 @@ Function Get-InstallationPolicy { <# .SYNOPSIS - - This is a helper function that retrives the InstallationPolicy from the given repository. + This is a helper function that retrives the InstallationPolicy from the given repository. .PARAMETER RepositoryName - Provides the repository Name. - + Provides the repository Name. #> - Param + [CmdletBinding()] + param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [System.String]$RepositoryName + [System.String] $RepositoryName ) Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) @@ -295,7 +288,7 @@ Function Get-InstallationPolicy $repositoryobj = PackageManagement\Get-PackageSource -Name $RepositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if ($repositoryobj) - { + { return $repositoryobj.IsTrusted - } + } } From c3c8a455aa20ba8ffc6332eb72080c49c974c5bc Mon Sep 17 00:00:00 2001 From: Anthony Howell Date: Sat, 19 Jan 2019 02:05:02 -0800 Subject: [PATCH 06/21] Now removes the PSGet_ prepended to line 2 in the updated manifest. (#403) --- .../public/psgetfunctions/Update-ModuleManifest.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 index d552306a..d11efb9b 100644 --- a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 @@ -941,6 +941,9 @@ function Update-ModuleManifest $newContent = Microsoft.PowerShell.Management\Get-Content -Path $tempPath + #Remove the PSGet_ prepended to the original manifest name due to the temp file name + $newContent[1] = $newContent[1] -replace "'PSGet_", "'" + try { #Ask for confirmation of the new manifest before replacing the original one From 5a51ee68a0565f4c32e2f3c8b61eb9674f804e90 Mon Sep 17 00:00:00 2001 From: Edwin Young Date: Wed, 23 Jan 2019 15:44:43 -0800 Subject: [PATCH 07/21] Added basic test case to demonstrate repository / problem --- Tests/PSGetUpdateModule.Tests.ps1 | 54 +++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/Tests/PSGetUpdateModule.Tests.ps1 b/Tests/PSGetUpdateModule.Tests.ps1 index 899f0bf8..3c961f69 100644 --- a/Tests/PSGetUpdateModule.Tests.ps1 +++ b/Tests/PSGetUpdateModule.Tests.ps1 @@ -77,6 +77,48 @@ function SuiteCleanup { } } +Describe UpdateModuleFromAlternateRepo -Tags 'BVT' { + BeforeAll { + SuiteSetup + } + + AfterAll { + SuiteCleanup + } + + AfterEach { + PSGetTestUtils\Uninstall-Module ContosoServer + PSGetTestUtils\Uninstall-Module ContosoClient + } + + It "Check situation" { + $withSlash = "https://www.poshtestgallery.com/api/v2/" + $noSlash = "https://www.poshtestgallery.com/api/v2" + Write-Host (Get-PSRepository | Out-String) + (Get-PSRepository PSGallery).SourceLocation | Should Be $withSlash + + Install-Module ContosoServer -RequiredVersion 1.0 + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $withSlash + Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + + # now update where PSGallery Source Location is + Set-PSGallerySourceLocation -Location $noSlash + Write-Host (Get-PSRepository | Out-String) + (Get-PSRepository PSGallery).SourceLocation | Should Be $noSlash + + # reload powershellget to force-update cached repository info + Import-Module PowerShellGet -Force + + # now try and update module isntalled using other SourceLocation + Update-Module ContosoServer -RequiredVersion 2.0 -ErrorAction Stop + Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $noSlash + + (Get-InstalledModule ContosoServer).Version | Should Be 2.0 + + } +} + Describe PowerShell.PSGet.UpdateModuleTests -Tags 'BVT','InnerLoop' { BeforeAll { @@ -350,7 +392,7 @@ Describe PowerShell.PSGet.UpdateModuleTests -Tags 'BVT','InnerLoop' { } Describe PowerShell.PSGet.UpdateModuleTests.P1 -Tags 'P1','OuterLoop' { - # Not executing these tests on MacOS as + # Not executing these tests on MacOS as # the total execution time is exceeding allowed 50 min in TravisCI daily builds. if($IsMacOS) { return @@ -376,13 +418,13 @@ Describe PowerShell.PSGet.UpdateModuleTests.P1 -Tags 'P1','OuterLoop' { # Expected Result: both modules should be refreshed # It "UpdateMultipleModulesWithWildcard" { - + Install-Module ContosoClient -RequiredVersion 1.0 - + $contosoClientDetails = Get-InstalledModule -Name ContosoClient Install-Module ContosoServer -RequiredVersion 1.0 - + $MyError = $null $DateTimeBeforeUpdate = Get-Date @@ -391,7 +433,7 @@ Describe PowerShell.PSGet.UpdateModuleTests.P1 -Tags 'P1','OuterLoop' { Assert ($MyError.Count -eq 0) "There should not be any error when updating multiple modules with wildcard in name, $MyError" $res = Get-InstalledModule -Name ContosoServer -MinimumVersion "1.1" Assert ($res -and ($res.Name -eq "ContosoServer") -and ($res.Version -gt [Version]"1.0")) "Update-Module should update when wildcard specified in name" - + $res = Get-InstalledModule -Name ContosoClient -MinimumVersion "1.1" Assert ($res -and ($res.Name -eq "ContosoClient") -and ($res.Version -gt [Version]"1.0")) "Update-Module should update when wildcard specified in name" @@ -457,7 +499,7 @@ Describe PowerShell.PSGet.UpdateModuleTests.P1 -Tags 'P1','OuterLoop' { Describe PowerShell.PSGet.UpdateModuleTests.P2 -Tags 'P2','OuterLoop' { - # Not executing these tests on MacOS as + # Not executing these tests on MacOS as # the total execution time is exceeding allowed 50 min in TravisCI daily builds. if($IsMacOS) { return From 4cc6d800319f96e5fb48c3ae9f582cad3568812c Mon Sep 17 00:00:00 2001 From: Edwin Young Date: Wed, 23 Jan 2019 16:52:06 -0800 Subject: [PATCH 08/21] Proposed fix for location issues --- Tests/PSGetUpdateModule.Tests.ps1 | 38 ++++++++++++++++--- .../private/functions/Get-SourceName.ps1 | 19 ++++------ .../functions/Test-EquivalentLocation.ps1 | 18 +++++++++ 3 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 src/PowerShellGet/private/functions/Test-EquivalentLocation.ps1 diff --git a/Tests/PSGetUpdateModule.Tests.ps1 b/Tests/PSGetUpdateModule.Tests.ps1 index 3c961f69..c3fb18a5 100644 --- a/Tests/PSGetUpdateModule.Tests.ps1 +++ b/Tests/PSGetUpdateModule.Tests.ps1 @@ -91,19 +91,19 @@ Describe UpdateModuleFromAlternateRepo -Tags 'BVT' { PSGetTestUtils\Uninstall-Module ContosoClient } - It "Check situation" { + It "Check that removing a slash from a repo doesn't break update" { $withSlash = "https://www.poshtestgallery.com/api/v2/" $noSlash = "https://www.poshtestgallery.com/api/v2" - Write-Host (Get-PSRepository | Out-String) + #Write-Host (Get-PSRepository | Out-String) (Get-PSRepository PSGallery).SourceLocation | Should Be $withSlash Install-Module ContosoServer -RequiredVersion 1.0 (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $withSlash - Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) # now update where PSGallery Source Location is Set-PSGallerySourceLocation -Location $noSlash - Write-Host (Get-PSRepository | Out-String) + #Write-Host (Get-PSRepository | Out-String) (Get-PSRepository PSGallery).SourceLocation | Should Be $noSlash # reload powershellget to force-update cached repository info @@ -111,11 +111,39 @@ Describe UpdateModuleFromAlternateRepo -Tags 'BVT' { # now try and update module isntalled using other SourceLocation Update-Module ContosoServer -RequiredVersion 2.0 -ErrorAction Stop - Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $noSlash (Get-InstalledModule ContosoServer).Version | Should Be 2.0 + } + + It "Check that adding a slash to a repo doesn't break update" { + $withSlash = "https://www.poshtestgallery.com/api/v2/" + $noSlash = "https://www.poshtestgallery.com/api/v2" + #Write-Host (Get-PSRepository | Out-String) + + Set-PSGallerySourceLocation -Location $noSlash + + (Get-PSRepository PSGallery).SourceLocation | Should Be $noSlash + + Install-Module ContosoServer -RequiredVersion 1.0 + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $noSlash + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + # now update where PSGallery Source Location is + Set-PSGallerySourceLocation -Location $withSlash + #Write-Host (Get-PSRepository | Out-String) + (Get-PSRepository PSGallery).SourceLocation | Should Be $withSlash + + # reload powershellget to force-update cached repository info + Import-Module PowerShellGet -Force + + # now try and update module isntalled using other SourceLocation + Update-Module ContosoServer -RequiredVersion 2.0 -ErrorAction Stop + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $withSlash + + (Get-InstalledModule ContosoServer).Version | Should Be 2.0 } } diff --git a/src/PowerShellGet/private/functions/Get-SourceName.ps1 b/src/PowerShellGet/private/functions/Get-SourceName.ps1 index 0cbc0ce3..89130a75 100644 --- a/src/PowerShellGet/private/functions/Get-SourceName.ps1 +++ b/src/PowerShellGet/private/functions/Get-SourceName.ps1 @@ -1,10 +1,9 @@ -function Get-SourceName -{ +function Get-SourceName { [CmdletBinding()] [OutputType("string")] Param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Location @@ -12,14 +11,12 @@ function Get-SourceName Set-ModuleSourcesVariable - foreach($psModuleSource in $script:PSGetModuleSources.Values) - { - if(($psModuleSource.Name -eq $Location) -or - ($psModuleSource.SourceLocation -eq $Location) -or - ((Get-Member -InputObject $psModuleSource -Name $script:ScriptSourceLocation) -and - ($psModuleSource.ScriptSourceLocation -eq $Location))) - { + foreach ($psModuleSource in $script:PSGetModuleSources.Values) { + if (($psModuleSource.Name -eq $Location) -or + (Test-EquivalentLocation -LocationA $psModuleSource.SourceLocation -LocationB $Location) -or + ((Get-Member -InputObject $psModuleSource -Name $script:ScriptSourceLocation) -and + (Test-EquivalentLocation -LocationA $psModuleSource.ScriptSourceLocation -LocationB $Location))) { return $psModuleSource.Name } } -} \ No newline at end of file +} diff --git a/src/PowerShellGet/private/functions/Test-EquivalentLocation.ps1 b/src/PowerShellGet/private/functions/Test-EquivalentLocation.ps1 new file mode 100644 index 00000000..14962a8c --- /dev/null +++ b/src/PowerShellGet/private/functions/Test-EquivalentLocation.ps1 @@ -0,0 +1,18 @@ + +# Compare 2 strings, ignoring any trailing slashes or backslashes. +# This is not exactly the same as URL or path equivalence but it should work in practice +function Test-EquivalentLocation { + [CmdletBinding()] + [OutputType("bool")] + param( + [Parameter(Mandatory = $false)] + [string]$LocationA, + + [Parameter(Mandatory = $false)] + [string]$LocationB + ) + + $LocationA = $LocationA.TrimEnd("\/") + $LocationB = $LocationB.TrimEnd("\/") + return $LocationA -eq $LocationB +} From 437b0ba3bc6f263a2b9bd846df033daf12cbb344 Mon Sep 17 00:00:00 2001 From: Edwin Young Date: Thu, 24 Jan 2019 10:56:21 -0800 Subject: [PATCH 09/21] do-nothing update to tickle appveyor --- Tests/PSGetUpdateModule.Tests.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/PSGetUpdateModule.Tests.ps1 b/Tests/PSGetUpdateModule.Tests.ps1 index c3fb18a5..f6224a72 100644 --- a/Tests/PSGetUpdateModule.Tests.ps1 +++ b/Tests/PSGetUpdateModule.Tests.ps1 @@ -113,7 +113,6 @@ Describe UpdateModuleFromAlternateRepo -Tags 'BVT' { Update-Module ContosoServer -RequiredVersion 2.0 -ErrorAction Stop #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $noSlash - (Get-InstalledModule ContosoServer).Version | Should Be 2.0 } @@ -142,7 +141,6 @@ Describe UpdateModuleFromAlternateRepo -Tags 'BVT' { Update-Module ContosoServer -RequiredVersion 2.0 -ErrorAction Stop #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $withSlash - (Get-InstalledModule ContosoServer).Version | Should Be 2.0 } } From 24cdf8205ea873f77ee752c1dcc7b8baf3a1a720 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 29 Jan 2019 13:24:59 +1100 Subject: [PATCH 10/21] Fix for not being able to register PSGallery repository behind web proxy (#410) --- src/PowerShellGet/private/functions/Resolve-Location.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PowerShellGet/private/functions/Resolve-Location.ps1 b/src/PowerShellGet/private/functions/Resolve-Location.ps1 index 07ccc9cf..208c54b4 100644 --- a/src/PowerShellGet/private/functions/Resolve-Location.ps1 +++ b/src/PowerShellGet/private/functions/Resolve-Location.ps1 @@ -62,7 +62,7 @@ function Resolve-Location Write-Debug -Message "Ping-Endpoint: location=$Location, statuscode=$statusCode, resolvedLocation=$resolvedLocation" - if((($statusCode -eq 200) -or ($statusCode -eq 401)) -and $resolvedLocation) + if((($statusCode -eq 200) -or ($statusCode -eq 401) -or ($statusCode -eq 407)) -and $resolvedLocation) { return $resolvedLocation } From 3f6f7355df6d79f2f889e4751b3535f33a0be01a Mon Sep 17 00:00:00 2001 From: pougetat Date: Mon, 4 Feb 2019 22:17:43 +0100 Subject: [PATCH 11/21] Allow Update-ModuleManifest to take an empty array of functions or commandlets to export (#414) --- Tests/PSGetUpdateModuleManifest.Tests.ps1 | 18 +++++++++++++++++- .../psgetfunctions/Update-ModuleManifest.ps1 | 9 +++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Tests/PSGetUpdateModuleManifest.Tests.ps1 b/Tests/PSGetUpdateModuleManifest.Tests.ps1 index e22a8c51..341714ae 100644 --- a/Tests/PSGetUpdateModuleManifest.Tests.ps1 +++ b/Tests/PSGetUpdateModuleManifest.Tests.ps1 @@ -152,7 +152,7 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { # Action: # Update-ModuleManifest -Path [Path] # - # Expected Result: The updated manifest should have the same DefaultCommandPreifx as before, + # Expected Result: The updated manifest should have the same DefaultCommandPrefix as before, # CmdletsToExport, FunctionsToExport, AliasesToExport, DSCResourcesToExport should not have prefixes affixed # It UpdateModuleManifestWithDefaultCommandPrefix { @@ -197,6 +197,22 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { } } + # Purpose: Update a module manifest with an empty array of commandlets and functions to export + # + # Action: Update-ModuleManifest -Path [path] -CmdletsToExport "" -functions "" + # + # Expected Result: The updated module manifest should have no commandlets or functions to export + # + It "UpdateModuleManifestWithEmptyFunctionsAndCmdletsToExport" { + New-ModuleManifest -Path $script:testManifestPath -Confirm:$false -CmdletsToExport "commandlet1","commandlet2" ` + -FunctionsToExport "function1","function2" -AliasesToExport "alias1","alias2" + Update-ModuleManifest -Path $script:testManifestPath -CmdletsToExport "" -FunctionsToExport "" -AliasesToExport "" + $updatedModuleInfo = Test-ModuleManifest -Path $script:testManifestPath + + AssertEquals $updatedModuleInfo.FunctionsToExport.Count 0 "FunctionsToExport count should be 0" + AssertEquals $updatedModuleInfo.CmdletsToExport.Count 0 "CmdletsToExport count should be 0" + AssertEquals $updatedModuleInfo.AliasesToExport.Count 0 "AliasesToExport count should be 0" + } # Purpose: Update a module manifest with same parameters diff --git a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 index d11efb9b..3d395011 100644 --- a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 @@ -124,12 +124,10 @@ function Update-ModuleManifest $ModuleList, [Parameter()] - [ValidateNotNullOrEmpty()] [string[]] $FunctionsToExport, [Parameter()] - [ValidateNotNullOrEmpty()] [string[]] $AliasesToExport, @@ -139,7 +137,6 @@ function Update-ModuleManifest $VariablesToExport, [Parameter()] - [ValidateNotNullOrEmpty()] [string[]] $CmdletsToExport, @@ -497,7 +494,7 @@ function Update-ModuleManifest $params.Add("ModuleList",$ModuleManifestHashtable.ModuleList) } - if($FunctionsToExport) + if($FunctionsToExport -or $FunctionsToExport -is [array]) { $params.Add("FunctionsToExport",$FunctionsToExport) } @@ -523,7 +520,7 @@ function Update-ModuleManifest } } - if($AliasesToExport) + if($AliasesToExport -or $AliasesToExport -is [array]) { $params.Add("AliasesToExport",$AliasesToExport) } @@ -568,7 +565,7 @@ function Update-ModuleManifest } } - if($CmdletsToExport) + if($CmdletsToExport -or $CmdletsToExport -is [array]) { $params.Add("CmdletsToExport", $CmdletsToExport) } From 73e6f87c85371b97e8bbc3111034417fbe3fee6d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 6 Feb 2019 21:00:53 +0100 Subject: [PATCH 12/21] Updated CI pipeline for testing PSModule DSC resources (#413) --- .gitignore | 4 + CHANGELOG.md | 30 + DSC/DSC.psd1 | 63 ++ .../MSFT_PSModule/MSFT_PSModule.psm1 | 201 ++--- .../MSFT_PSModule/MSFT_PSModule.schema.mfl | 0 .../MSFT_PSModule/MSFT_PSModule.schema.mof | 0 .../en-US/MSFT_PSModule.strings.psd1 | 0 .../PowerShellGet.LocalizationHelper.psm1 | 250 ++++++ .../PowerShellGet.ResourceHelper.psm1 | 100 ++- .../PowerShellGet.ResourceHelper.strings.psd1 | 0 .../MSFT_PSModule.Integration.Tests.ps1 | 499 ++++++++++++ .../Integration/MSFT_PSModule.config.ps1 | 280 +++++++ DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 | 715 ++++++++++++++++++ ...PowerShellGet.LocalizationHelper.Tests.ps1 | 199 +++++ .../PowerShellGet.ResourceHelper.Tests.ps1 | 302 ++++++++ appveyor.yml | 19 + tools/build.psm1 | 110 ++- 17 files changed, 2595 insertions(+), 177 deletions(-) create mode 100644 DSC/DSC.psd1 rename {PowerShellGet => DSC}/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 (87%) rename {PowerShellGet => DSC}/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl (100%) rename {PowerShellGet => DSC}/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof (100%) rename {PowerShellGet => DSC}/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 (100%) create mode 100644 DSC/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1 rename PowerShellGet/DSCResources/PowerShellGetHelper.psm1 => DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 (83%) rename PowerShellGet/DSCResources/MSFT_PSModule/en-US/PowerShellGet.strings.psd1 => DSC/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 (100%) create mode 100644 DSC/Tests/Integration/MSFT_PSModule.Integration.Tests.ps1 create mode 100644 DSC/Tests/Integration/MSFT_PSModule.config.ps1 create mode 100644 DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 create mode 100644 DSC/Tests/Unit/PowerShellGet.LocalizationHelper.Tests.ps1 create mode 100644 DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 diff --git a/.gitignore b/.gitignore index a3493a82..a4b9e6c6 100644 --- a/.gitignore +++ b/.gitignore @@ -269,3 +269,7 @@ FakesAssemblies/ *.xproj *.xproj.user *.suo + +# Ignore DscResource.Tests repository when it is cloned +# when running DSC resource unit tests locally. +DSC/DscResource.Tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d6b1f9..5e30659f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,34 @@ # Changelog + +## Unreleased + +* Update the AppVeyor CI test pipeline with a new job to run tests for + the DSC resources, primarily for the resource `PSModule`. + The new job uses the test framework used for the DSC Resource Kit, + the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) repository. +* Update .gitignore to ignore the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) + test framework. When running unit test locally it is cloned into the + local repository folder. +* Added module PowerShellGet.LocalizationHelper containing localization + helper functions for DSC resources, and unit tests for the helper + functions. +* Moved helper functions for the DSC resource `PSModule` to the module + PowerShellGet.ResourceHelper. Added improved localization support, and + code formatting against workspace VS Code settings. +* Changes to PSModule. + * Added improved localization support. + * Changed type on the parameters that had `[Switch]` to correctly use + `Systen.Boolean` to match the schema.mof. + * Code formatting against workspace VS Code settings. + * Added unit tests. + * Added integration tests + * It is now possible to install a module and passing in `AllowClobber` + when the modules package source is trusted (it already worked in + other scenarios). +* Changed the AppVeyor CI build pipeline so it added the DSC resource + `PSModule` and dependent helper modules (the `Modules` folder) to the + AppVeyor artifact. + ## 2.0.4 Bug Fix * Remove PSGallery availability checks (#374) diff --git a/DSC/DSC.psd1 b/DSC/DSC.psd1 new file mode 100644 index 00000000..9e56bc3b --- /dev/null +++ b/DSC/DSC.psd1 @@ -0,0 +1,63 @@ +<# + This is a dummy PowerShell manifest file so that the DscResource.Tests + test framework recognize the module folder as correct (expected) folder + and file structure. + THIS FILE IS NOT USE DURING DEPLOYMENT. +#> +@{ + # Version number of this module. + moduleVersion = '0.0.0.1' + + # ID used to uniquely identify this module + GUID = 'e102ebd2-bdc3-4d0f-bc93-4b8cc3eb7074' + + # Author of this module + Author = 'Microsoft Corporation' + + # Company or vendor of this module + CompanyName = 'Microsoft Corporation' + + # Copyright statement for this module + Copyright = '(c) 2019 Microsoft Corporation. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'Module with DSC Resources for deployment of PowerShell modules.' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '5.0' + + # Minimum version of the common language runtime (CLR) required by this module + CLRVersion = '4.0' + + # Functions to export from this module + FunctionsToExport = @() + + # Cmdlets to export from this module + CmdletsToExport = @() + + RequiredAssemblies = @() + + <# + 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 = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResource') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/PowerShell/PowerShellGet' + + # ReleaseNotes of this module + ReleaseNotes = '' + + } # End of PSData hashtable + + } # End of PrivateData hashtable +} diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 b/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 similarity index 87% rename from PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 rename to DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 index df602590..230c2a13 100644 --- a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 +++ b/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 @@ -10,23 +10,29 @@ # THE SOFTWARE. # -Import-LocalizedData -BindingVariable LocalizedData -filename MSFT_PSModule.strings.psd1 -$script:localizedData = Get-LocalizedData ` - -ResourceName 'MSFT_PSModule' ` - -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) +$resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent -Import-Module -Name "$PSScriptRoot\..\PowerShellGetHelper.psm1" +# Import localization helper functions. +$helperName = 'PowerShellGet.LocalizationHelper' +$dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" +Import-Module -Name $dscResourcesFolderFilePath + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_PSModule' -ScriptRoot $PSScriptRoot + +# Import resource helper functions. +$helperName = 'PowerShellGet.ResourceHelper' +$dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" +Import-Module -Name $dscResourcesFolderFilePath # DSC Resource for the $CurrentProviderName. $CurrentProviderName = "PowerShellGet" # Return the current state of the resource. -function Get-TargetResource -{ +function Get-TargetResource { <# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell - Gallery and install it on your computer. + Gallery and install it on your computer. Get-TargetResource returns the current state of the resource. @@ -77,13 +83,13 @@ function Get-TargetResource [System.String] $MinimumVersion, - [Switch] + [System.Boolean] $Force, - - [Switch] + + [System.Boolean] $AllowClobber, - [Switch] + [System.Boolean] $SkipPublisherCheck ) @@ -97,35 +103,29 @@ function Get-TargetResource $modules = Get-RightModule @extractedArguments -ErrorAction SilentlyContinue -WarningAction SilentlyContinue # If the module is found, the count > 0 - if ($modules.count -gt 0) - { + if ($modules.count -gt 0) { $ensure = 'Present' Write-Verbose -Message ($localizedData.ModuleFound -f $($Name)) } - else - { + else { Write-Verbose -Message ($localizedData.ModuleNotFound -f $($Name)) } Write-Debug -Message "Ensure of $($Name) module is $($ensure)" - if ($ensure -eq 'Absent') - { + if ($ensure -eq 'Absent') { $returnValue = @{ Ensure = $ensure Name = $Name } } - else - { + else { # Find a module with the latest version and return its properties. $latestModule = $modules[0] - foreach ($module in $modules) - { - if ($module.Version -gt $latestModule.Version) - { + foreach ($module in $modules) { + if ($module.Version -gt $latestModule.Version) { $latestModule = $module } } @@ -144,19 +144,18 @@ function Get-TargetResource ModuleBase = $latestModule.ModuleBase ModuleType = $latestModule.ModuleType Author = $latestModule.Author - InstalledVersion = $latestModule.Version + InstalledVersion = $latestModule.Version InstallationPolicy = if ($installationPolicy) {"Trusted"}else {"Untrusted"} } } return $returnValue } -function Test-TargetResource -{ +function Test-TargetResource { <# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell - Gallery and install it on your computer. + Gallery and install it on your computer. Test-TargetResource validates whether the resource is currently in the desired state. @@ -182,7 +181,7 @@ function Test-TargetResource Provides the minimum version of the module you want to install or uninstall. .PARAMETER Force - Forces the installation of modules. If a module of the same name and version already exists on the computer, + Forces the installation of modules. If a module of the same name and version already exists on the computer, this parameter overwrites the existing module with one of the same name that was found by the command. .PARAMETER AllowClobber @@ -221,13 +220,13 @@ function Test-TargetResource [System.String] $MinimumVersion, - [Switch] + [System.Boolean] $Force, - [Switch] + [System.Boolean] $AllowClobber, - [Switch] + [System.Boolean] $SkipPublisherCheck ) @@ -239,24 +238,21 @@ function Test-TargetResource $status = Get-TargetResource @extractedArguments # The ensure returned from Get-TargetResource is not equal to the desired $Ensure. - if ($status.Ensure -ieq $Ensure) - { + if ($status.Ensure -ieq $Ensure) { Write-Verbose -Message ($localizedData.InDesiredState -f $Name) return $true } - else - { + else { Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name) return $false } } -function Set-TargetResource -{ +function Set-TargetResource { <# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell - Gallery and install it on your computer. + Gallery and install it on your computer. Set-TargetResource sets the resource to the desired state. "Make it so". @@ -320,29 +316,27 @@ function Set-TargetResource [System.String] $MinimumVersion, - [Switch] + [System.Boolean] $Force, - - [Switch] + + [System.Boolean] $AllowClobber, - - [Switch] + + [System.Boolean] $SkipPublisherCheck ) # Validate the repository argument - if ($PSBoundParameters.ContainsKey("Repository")) - { + if ($PSBoundParameters.ContainsKey("Repository")) { ValidateArgument -Argument $Repository -Type "PackageSource" -ProviderName $CurrentProviderName -Verbose } - if ($Ensure -ieq "Present") - { + if ($Ensure -ieq "Present") { # Version check $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ("MinimumVersion", "MaximumVersion", "RequiredVersion") - ValidateVersionArgument @extractedArguments + ValidateVersionArgument @extractedArguments $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") @@ -351,8 +345,7 @@ function Set-TargetResource $modules = Find-Module @extractedArguments -ErrorVariable ev - if (-not $modules) - { + if (-not $modules) { ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage ($localizedData.ModuleNotFoundInRepository -f $Name, $ev.Exception) ` -ErrorId "ModuleNotFoundInRepository" ` @@ -362,32 +355,28 @@ function Set-TargetResource $trusted = $null $moduleFound = $null - foreach ($m in $modules) - { + foreach ($m in $modules) { # Check for the installation policy. $trusted = Get-InstallationPolicy -RepositoryName $m.Repository -ErrorAction SilentlyContinue -WarningAction SilentlyContinue # Stop the loop if found a trusted repository. - if ($trusted) - { - $moduleFound = $m + if ($trusted) { + $moduleFound = $m break; } } # The respository is trusted, so we install it. - if ($trusted) - { + if ($trusted) { Write-Verbose -Message ($localizedData.StartInstallModule -f $Name, $moduleFound.Version.toString(), $moduleFound.Repository) # Extract the installation options. $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("Force", "AllowClobber", "SkipPublisherCheck") - $moduleFound | Install-Module -ErrorVariable ev + $moduleFound | Install-Module @extractedSwitches -ErrorVariable ev } # The repository is untrusted but user's installation policy is trusted, so we install it with a warning. - elseif ($InstallationPolicy -ieq 'Trusted') - { + elseif ($InstallationPolicy -ieq 'Trusted') { Write-Warning -Message ($localizedData.InstallationPolicyWarning -f $Name, $modules[0].Repository, $InstallationPolicy) # Extract installation options (Force implied by InstallationPolicy). @@ -397,29 +386,25 @@ function Set-TargetResource $modules[0] | Install-Module @extractedSwitches -Force -ErrorVariable ev } # Both user and repository is untrusted - else - { + else { ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage ($localizedData.InstallationPolicyFailed -f $InstallationPolicy, "Untrusted") ` -ErrorId "InstallationPolicyFailed" ` -ErrorCategory InvalidOperation } - if ($ev) - { + if ($ev) { ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage ($localizedData.FailtoInstall -f $Name, $ev.Exception) ` -ErrorId "FailtoInstall" ` -ErrorCategory InvalidOperation } - else - { + else { Write-Verbose -Message ($localizedData.InstalledSuccess -f $($Name)) } } # Ensure=Absent - else - { + else { $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") @@ -427,41 +412,36 @@ function Set-TargetResource # Get the module with the right version and repository properties. $modules = Get-RightModule @extractedArguments -ErrorVariable ev - if ((-not $modules) -or $ev) - { + if ((-not $modules) -or $ev) { ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage ($localizedData.ModuleWithRightPropertyNotFound -f $Name, $ev.Exception) ` -ErrorId "ModuleWithRightPropertyNotFound" ` -ErrorCategory InvalidOperation } - foreach ($module in $modules) - { + foreach ($module in $modules) { # Get the path where the module is installed. $path = $module.ModuleBase - Write-Verbose -Message ($localizedData.StartUnInstallModule -f $($Name)) + Write-Verbose -Message ($localizedData.StartUnInstallModule -f $($Name)) # There is no Uninstall-Module cmdlet exists, so we will remove the ModuleBase folder as an uninstall operation. Microsoft.PowerShell.Management\Remove-Item -Path $path -Force -Recurse -ErrorVariable ev - if ($ev) - { + if ($ev) { ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage ($localizedData.FailtoUninstall -f $module.Name, $ev.Exception) ` -ErrorId "FailtoUninstall" ` -ErrorCategory InvalidOperation } - else - { + else { Write-Verbose -Message ($localizedData.UnInstalledSuccess -f $($module.Name)) } } # foreach } # Ensure=Absent } -Function Get-RightModule -{ +Function Get-RightModule { <# .SYNOPSIS This is a helper function. It returns the modules that meet the specified versions and the repository requirements. @@ -507,8 +487,7 @@ Function Get-RightModule $modules = Microsoft.PowerShell.Core\Get-Module -Name $Name -ListAvailable -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - if (-not $modules) - { + if (-not $modules) { return $null } @@ -519,77 +498,63 @@ Function Get-RightModule -ArgumentNames ("MaximumVersion", "MinimumVersion", "RequiredVersion") $returnVal = @() - foreach ($m in $modules) - { + foreach ($m in $modules) { $versionMatch = $false $installedVersion = $m.Version # Case 1 - a user provides none of RequiredVersion, MinimumVersion, MaximumVersion - if ($extractedArguments.Count -eq 0) - { + if ($extractedArguments.Count -eq 0) { $versionMatch = $true } - # Case 2 - a user provides RequiredVersion - elseif ($extractedArguments.ContainsKey("RequiredVersion")) - { + # Case 2 - a user provides RequiredVersion + elseif ($extractedArguments.ContainsKey("RequiredVersion")) { # Check if it matches with the installedversion $versionMatch = ($installedVersion -eq [System.Version]$RequiredVersion) } - else - { - - # Case 3 - a user provides MinimumVersion - if ($extractedArguments.ContainsKey("MinimumVersion")) - { + else { + + # Case 3 - a user provides MinimumVersion + if ($extractedArguments.ContainsKey("MinimumVersion")) { $versionMatch = ($installedVersion -ge [System.Version]$extractedArguments['MinimumVersion']) } - + # Case 4 - a user provides MaximumVersion - if ($extractedArguments.ContainsKey("MaximumVersion")) - { + if ($extractedArguments.ContainsKey("MaximumVersion")) { $isLessThanMax = ($installedVersion -le [System.Version]$extractedArguments['MaximumVersion']) - if ($extractedArguments.ContainsKey("MinimumVersion")) - { + if ($extractedArguments.ContainsKey("MinimumVersion")) { $versionMatch = $versionMatch -and $isLessThanMax } - else - { + else { $versionMatch = $isLessThanMax } } - + # Case 5 - Both MinimumVersion and MaximumVersion are provided. It's covered by the above. # Do not return $false yet to allow the foreach to continue - if (-not $versionMatch) - { + if (-not $versionMatch) { Write-Verbose -Message ($localizedData.VersionMismatch -f $($Name), $($installedVersion)) $versionMatch = $false } } # Case 6 - Version matches but need to check if the module is from the right repository. - if ($versionMatch) - { + if ($versionMatch) { # a user does not provide Repository, we are good - if (-not $PSBoundParameters.ContainsKey("Repository")) - { + if (-not $PSBoundParameters.ContainsKey("Repository")) { Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") $returnVal += $m } - else - { + else { # Check if the Repository matches $sourceName = Get-ModuleRepositoryName -Module $m - if ($Repository -ieq $sourceName) - { + if ($Repository -ieq $sourceName) { Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") $returnVal += $m } - else - { + else { Write-Verbose -Message ($localizedData.RepositoryMismatch -f $($Name), $($sourceName)) } } @@ -598,8 +563,7 @@ Function Get-RightModule return $returnVal } -Function Get-ModuleRepositoryName -{ +Function Get-ModuleRepositoryName { <# .SYNOPSIS This is a helper function that returns the module's repository name. @@ -624,8 +588,7 @@ Function Get-ModuleRepositoryName Write-Verbose -Message ($localizedData.FoundModulePath -f $($psGetModuleInfoPath)) - if (Microsoft.PowerShell.Management\Test-path -Path $psGetModuleInfoPath) - { + if (Microsoft.PowerShell.Management\Test-path -Path $psGetModuleInfoPath) { $psGetModuleInfo = Microsoft.PowerShell.Utility\Import-Clixml -Path $psGetModuleInfoPath return $psGetModuleInfo.Repository diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl b/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl similarity index 100% rename from PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl rename to DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mfl diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof b/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof similarity index 100% rename from PowerShellGet/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof rename to DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.schema.mof diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 b/DSC/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 similarity index 100% rename from PowerShellGet/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 rename to DSC/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 diff --git a/DSC/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1 b/DSC/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1 new file mode 100644 index 00000000..7b0c8a26 --- /dev/null +++ b/DSC/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1 @@ -0,0 +1,250 @@ +<# + .SYNOPSIS + Creates and throws an invalid argument exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown. +#> +function New-InvalidArgumentException { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' ` + -ArgumentList @($Message, $ArgumentName) + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) + } + + $errorRecord = New-Object @newObjectParameters + + throw $errorRecord +} + +<# + .SYNOPSIS + Creates and throws an invalid operation exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidOperationException { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message) + } + else { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'InvalidOperation', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an object not found exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-ObjectNotFoundException { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'ObjectNotFound', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an invalid result exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidResultException { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'InvalidResult', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Retrieves the localized string data based on the machine's culture. + Falls back to en-US strings if the machine's culture is not supported. + + .PARAMETER ResourceName + The name of the resource as it appears before '.strings.psd1' of the localized string file. + For example: + For WindowsOptionalFeature: MSFT_WindowsOptionalFeature + For Service: MSFT_ServiceResource + For Registry: MSFT_RegistryResource + For Helper: SqlServerDscHelper + + .PARAMETER ScriptRoot + Optional. The root path where to expect to find the culture folder. This is only needed + for localization in helper modules. This should not normally be used for resources. +#> +function Get-LocalizedData { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ResourceName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ScriptRoot + ) + + if ( -not $ScriptRoot ) { + $resourceDirectory = Join-Path -Path $PSScriptRoot -ChildPath $ResourceName + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture + } + else { + $localizedStringFileLocation = Join-Path -Path $ScriptRoot -ChildPath $PSUICulture + } + + if (-not (Test-Path -Path $localizedStringFileLocation)) { + # Fallback to en-US + if ( -not $ScriptRoot ) { + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' + } + else { + $localizedStringFileLocation = Join-Path -Path $ScriptRoot -ChildPath 'en-US' + } + } + + Import-LocalizedData ` + -BindingVariable 'localizedData' ` + -FileName "$ResourceName.strings.psd1" ` + -BaseDirectory $localizedStringFileLocation + + return $localizedData +} + +Export-ModuleMember -Function @( + 'New-InvalidArgumentException', + 'New-InvalidOperationException', + 'New-ObjectNotFoundException', + 'New-InvalidResultException', + 'Get-LocalizedData' +) diff --git a/PowerShellGet/DSCResources/PowerShellGetHelper.psm1 b/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 similarity index 83% rename from PowerShellGet/DSCResources/PowerShellGetHelper.psm1 rename to DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 index 0c22eba6..36b8a1eb 100644 --- a/PowerShellGet/DSCResources/PowerShellGetHelper.psm1 +++ b/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 @@ -9,15 +9,21 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # -# Helper functions for PackageManagement DSC Resouces + +<# + Helper functions for PowerShellGet DSC Resources. +#> + +# Import localization helper functions. +$helperName = 'PowerShellGet.LocalizationHelper' +$resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent +$dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" +Import-Module -Name $dscResourcesFolderFilePath # Import Localization Strings -$script:localizedData = Get-LocalizedData ` - -ResourceName 'PowerShellGet' ` - -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) +$script:localizedData = Get-LocalizedData -ResourceName 'PowerShellGet.ResourceHelper' -ScriptRoot $PSScriptRoot -Function ExtractArguments -{ +Function ExtractArguments { <# .SYNOPSIS This is a helper function that extract the parameters from a given table. @@ -28,7 +34,7 @@ Function ExtractArguments .PARAMETER ArgumentNames Specifies a list of arguments you want to extract. #> - + [CmdletBinding()] param ( @@ -45,10 +51,8 @@ Function ExtractArguments $returnValue = @{} - foreach ($arg in $ArgumentNames) - { - if ($FunctionBoundParameters.ContainsKey($arg)) - { + foreach ($arg in $ArgumentNames) { + if ($FunctionBoundParameters.ContainsKey($arg)) { # Found an argument we are looking for, so we add it to return collection. $returnValue.Add($arg, $FunctionBoundParameters[$arg]) } @@ -56,11 +60,10 @@ Function ExtractArguments return $returnValue } -Function ThrowError -{ +Function ThrowError { <# .SYNOPSIS - This is a helper function that throws an error. + This is a helper function that throws an error. .PARAMETER ExceptionName Specifies the type of errors, e.g. System.ArgumentException. @@ -87,7 +90,7 @@ Function ThrowError [ValidateNotNullOrEmpty()] [System.String] $ExceptionMessage, - + [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] @@ -107,8 +110,7 @@ Function ThrowError throw $errorRecord } -Function ValidateArgument -{ +Function ValidateArgument { <# .SYNOPSIS This is a helper function that validates the arguments. @@ -141,63 +143,53 @@ Function ValidateArgument Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) - switch ($Type) - { - "SourceUri" - { + switch ($Type) { + "SourceUri" { # Checks whether given URI represents specific scheme # Most common schemes: file, http, https, ftp $scheme = @('http', 'https', 'file', 'ftp') - - $newUri = $Argument -as [System.URI] + + $newUri = $Argument -as [System.URI] $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newuri.Scheme)) - if ($returnValue -eq $false) - { + if ($returnValue -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` -ErrorId "InValidUri" ` -ErrorCategory InvalidArgument } } - "DestinationPath" - { + "DestinationPath" { $returnValue = Test-Path -Path $Argument - if ($returnValue -eq $false) - { + if ($returnValue -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.PathDoesNotExist -f $Argument)` -ErrorId "PathDoesNotExist" ` -ErrorCategory InvalidArgument } } - "PackageSource" - { + "PackageSource" { # Argument can be either the package source Name or source Uri. - + # Check if the source is a Uri. $uri = $Argument -as [System.URI] - if ($uri -and $uri.AbsoluteURI) - { + if ($uri -and $uri.AbsoluteURI) { # Check if it's a valid Uri. ValidateArgument -Argument $Argument -Type "SourceUri" -ProviderName $ProviderName } - else - { + else { # Check if it's a registered package source name. $source = PackageManagement\Get-PackageSource -Name $Argument -ProviderName $ProviderName -verbose -ErrorVariable ev - if ((-not $source) -or $ev) - { + if ((-not $source) -or $ev) { # We do not need to throw error here as Get-PackageSource does already. Write-Verbose -Message ($LocalizedData.SourceNotFound -f $source) } } } - default - { + default { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.UnexpectedArgument -f $Type)` -ErrorId "UnexpectedArgument" ` @@ -206,11 +198,10 @@ Function ValidateArgument } } -Function ValidateVersionArgument -{ +Function ValidateVersionArgument { <# .SYNOPSIS - This is a helper function that does the version validation. + This is a helper function that does the version validation. .PARAMETER RequiredVersion Provides the required version. @@ -225,13 +216,13 @@ Function ValidateVersionArgument [CmdletBinding()] param ( - [System.String] + [System.String] $RequiredVersion, - [System.String] + [System.String] $MinimumVersion, - [System.String] + [System.String] $MaximumVersion ) @@ -240,14 +231,12 @@ Function ValidateVersionArgument $isValid = $false # Case 1: No further check required if a user provides either none or one of these: minimumVersion, maximumVersion, and requiredVersion. - if ($PSBoundParameters.Count -le 1) - { + if ($PSBoundParameters.Count -le 1) { return $true } # Case 2: #If no RequiredVersion is provided. - if (-not $PSBoundParameters.ContainsKey('RequiredVersion')) - { + if (-not $PSBoundParameters.ContainsKey('RequiredVersion')) { # If no RequiredVersion, both MinimumVersion and MaximumVersion are provided. Otherwise fall into the Case #1. $isValid = $PSBoundParameters['MinimumVersion'] -le $PSBoundParameters['MaximumVersion'] } @@ -256,8 +245,7 @@ Function ValidateVersionArgument # In this case MinimumVersion and/or MaximumVersion also are provided. Otherwise fall in to Case #1. # This is an invalid case. When RequiredVersion is provided, others are not allowed. so $isValid is false, which is already set in the init. - if ($isValid -eq $false) - { + if ($isValid -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.VersionError)` -ErrorId "VersionError" ` @@ -265,11 +253,10 @@ Function ValidateVersionArgument } } -Function Get-InstallationPolicy -{ +Function Get-InstallationPolicy { <# .SYNOPSIS - This is a helper function that retrives the InstallationPolicy from the given repository. + This is a helper function that retrives the InstallationPolicy from the given repository. .PARAMETER RepositoryName Provides the repository Name. @@ -287,8 +274,7 @@ Function Get-InstallationPolicy $repositoryobj = PackageManagement\Get-PackageSource -Name $RepositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - if ($repositoryobj) - { + if ($repositoryobj) { return $repositoryobj.IsTrusted } } diff --git a/PowerShellGet/DSCResources/MSFT_PSModule/en-US/PowerShellGet.strings.psd1 b/DSC/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 similarity index 100% rename from PowerShellGet/DSCResources/MSFT_PSModule/en-US/PowerShellGet.strings.psd1 rename to DSC/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 diff --git a/DSC/Tests/Integration/MSFT_PSModule.Integration.Tests.ps1 b/DSC/Tests/Integration/MSFT_PSModule.Integration.Tests.ps1 new file mode 100644 index 00000000..b9eea3ef --- /dev/null +++ b/DSC/Tests/Integration/MSFT_PSModule.Integration.Tests.ps1 @@ -0,0 +1,499 @@ +<# + .SYNOPSIS + Integration tests for DSC resource PSModule. + + .NOTES + The header and footer was removed that is usually part of the + DscResource.Tests integration test template. + The header was adding the project folder as a PSModulePath which + collide with the PowerShellGet test framework that copies the + module to the regular PowerShell Module folder. +#> + +$script:dscResourceFriendlyName = 'PSModule' +$script:dcsResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region Integration Tests +$configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" +. $configurationFile + +Describe "$($script:dcsResourceName)_Integration" { + $configurationName = "$($script:dcsResourceName)_SetPackageSourceAsNotTrusted_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } + + $configurationName = "$($script:dcsResourceName)_InstallWithTrusted_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module1_Name + $resourceCurrentState.InstallationPolicy | Should -Be 'Untrusted' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_UninstallModule1_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module1_Name + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_SetPackageSourceAsTrusted_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } + + $configurationName = "$($script:dcsResourceName)_DefaultParameters_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module1_Name + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_UsingAllowClobber_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module2_Name + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_UninstallModule2_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module2_Name + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_RequiredVersion_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module2_Name + $resourceCurrentState.InstalledVersion | Should -Be $ConfigurationData.AllNodes.Module2_RequiredVersion + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + <# + This should not install a newer version of the module since the + previous test installed a module within the version range. + #> + $configurationName = "$($script:dcsResourceName)_VersionRange_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module2_Name + $resourceCurrentState.InstalledVersion | Should -Be $ConfigurationData.AllNodes.Module2_RequiredVersion + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_UninstallModule2_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module2_Name + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + <# + This should install a newer version of the module since no + module is installed within the version range. + #> + $configurationName = "$($script:dcsResourceName)_VersionRange_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Module2_Name + $resourceCurrentState.InstalledVersion | Should -Be $ConfigurationData.AllNodes.Module2_MaximumVersion + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } +} +#endregion diff --git a/DSC/Tests/Integration/MSFT_PSModule.config.ps1 b/DSC/Tests/Integration/MSFT_PSModule.config.ps1 new file mode 100644 index 00000000..e667268d --- /dev/null +++ b/DSC/Tests/Integration/MSFT_PSModule.config.ps1 @@ -0,0 +1,280 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + <# + Allows reading the configuration data from a JSON file + for real testing scenarios outside of the CI. + #> + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + + Module1_Name = 'PSLogging' + Module2_Name = 'SqlServer' + + Module2_RequiredVersion = '21.0.17279' + Module2_MinimumVersion = '21.0.17199' + Module2_MaximumVersion = '21.1.18068' + } + ) + } +} + +<# + .SYNOPSIS + Changes the package source 'PSGallery' to not trusted. + + .NOTES + Since the module is installed by SYSTEM as default, the package + source 'PSGallery' must be trusted for SYSTEM for some of the + tests. +#> +Configuration MSFT_PSModule_SetPackageSourceAsNotTrusted_Config +{ + Import-DscResource -ModuleName PSDscResources + + node $AllNodes.NodeName + { + Script 'TrustPackageSourcePSGallery' + { + SetScript = { + Write-Verbose -Message 'Setting package source ''PSGallery'' as trusted' + Set-PackageSource -Name 'PSGallery' -Trusted:$false + } + + TestScript = { + Write-Verbose -Message 'Test if the package source ''PSGallery'' is trusted.' + + <# + This takes the string of the $GetScript parameter and creates + a new script block (during runtime in the resource) and then + runs that script block. + #> + $getScriptResult = & ([ScriptBlock]::Create($GetScript)) + + return $getScriptResult.Result -eq $false + } + + GetScript = { + Write-Verbose -Message 'Return the current state of package source ''PSGallery''.' + $trusted = (Get-PackageSource -Name 'PSGallery').IsTrusted + + return @{ + Result = $trusted + } + } + } + } +} + +<# + .SYNOPSIS + Installs a module as trusted. + + .NOTES + This assumes that the package source 'PSGallery' is not trusted for SYSTEM. +#> +Configuration MSFT_PSModule_InstallWithTrusted_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Name = $Node.Module1_Name + InstallationPolicy = 'Trusted' + } + } +} + +<# + .SYNOPSIS + Uninstalls a module ($Node.Module1_Name). +#> +Configuration MSFT_PSModule_UninstallModule1_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Ensure = 'Absent' + Name = $Node.Module1_Name + } + } +} + +<# + .SYNOPSIS + Changes the package source 'PSGallery' to trusted. + + .NOTES + Since the module is installed by SYSTEM as default, the package + source 'PSGallery' must be trusted for SYSTEM for some of the + tests. +#> +Configuration MSFT_PSModule_SetPackageSourceAsTrusted_Config +{ + Import-DscResource -ModuleName PSDscResources + + node $AllNodes.NodeName + { + Script 'TrustPackageSourcePSGallery' + { + SetScript = { + Write-Verbose -Message 'Setting package source ''PSGallery'' as trusted' + Set-PackageSource -Name 'PSGallery' -Trusted + } + + TestScript = { + Write-Verbose -Message 'Test if the package source ''PSGallery'' is trusted.' + + <# + This takes the string of the $GetScript parameter and creates + a new script block (during runtime in the resource) and then + runs that script block. + #> + $getScriptResult = & ([ScriptBlock]::Create($GetScript)) + + return $getScriptResult.Result -eq $true + } + + GetScript = { + Write-Verbose -Message 'Return the current state of package source ''PSGallery''.' + $trusted = (Get-PackageSource -Name 'PSGallery').IsTrusted + + return @{ + Result = $trusted + } + } + } + } +} + +<# + .SYNOPSIS + Installs a module with the default parameters. +#> +Configuration MSFT_PSModule_DefaultParameters_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Name = $Node.Module1_Name + } + } +} + +<# + .SYNOPSIS + Installed a module using AllowClobber. + + .NOTES + This test uses SqlServer module that actually needs AllowClobber. + On the build worker there are other modules (SQLPS) already installed, + those modules have the same cmdlets in them. +#> +Configuration MSFT_PSModule_UsingAllowClobber_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Name = $Node.Module2_Name + AllowClobber = $true + } + } +} + +<# + .SYNOPSIS + Uninstalls a module ($Node.Module2_Name). +#> +Configuration MSFT_PSModule_UninstallModule2_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Ensure = 'Absent' + Name = $Node.Module2_Name + } + } +} + +<# + .SYNOPSIS + Installs a module with the specific version. +#> +Configuration MSFT_PSModule_RequiredVersion_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Name = $Node.Module2_Name + RequiredVersion = $Node.Module2_RequiredVersion + AllowClobber = $true + } + } +} + +<# + .SYNOPSIS + Installs a module with the specific version. +#> +Configuration MSFT_PSModule_RequiredVersion_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Name = $Node.Module2_Name + RequiredVersion = $Node.Module2_RequiredVersion + AllowClobber = $true + } + } +} + +<# + .SYNOPSIS + Installs a module within the specific version range. +#> +Configuration MSFT_PSModule_VersionRange_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Name = $Node.Module2_Name + MinimumVersion = $Node.Module2_MinimumVersion + MaximumVersion = $Node.Module2_MaximumVersion + AllowClobber = $true + } + } +} diff --git a/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 b/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 new file mode 100644 index 00000000..0a766936 --- /dev/null +++ b/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 @@ -0,0 +1,715 @@ +#region HEADER +# This must be same name as the root folder, and module manifest. +$script:DSCModuleName = 'DSC' +$script:DSCResourceName = 'MSFT_PSModule' + +# Unit Test Template Version: 1.2.4 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -ResourceType 'Mof' ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup { +} + +function Invoke-TestCleanup { + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try { + Invoke-TestSetup + + InModuleScope $script:DSCResourceName { + $mockModuleName = 'MockedModule' + $mockRepositoryName = 'PSGallery' + $mockModuleBase = 'TestDrive:\MockPath' + + $mockModule_v1 = New-Object -TypeName Object | + Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockModuleName -PassThru | + Add-Member -Name 'Description' -MemberType NoteProperty -Value 'Mocked description' -PassThru | + Add-Member -Name 'Guid' -MemberType NoteProperty -Value '4c189dbd-d858-4893-bac0-d682423c5fc7' -PassThru | + Add-Member -Name 'ModuleBase' -MemberType NoteProperty -Value $mockModuleBase -PassThru | + Add-Member -Name 'ModuleType' -MemberType NoteProperty -Value 'Script' -PassThru | + Add-Member -Name 'Author' -MemberType NoteProperty -Value 'Mocked Author' -PassThru | + Add-Member -Name 'Version' -MemberType NoteProperty -Value ([System.Version]'1.0.0.0') -PassThru -Force + + $mockModule_v2 = New-Object -TypeName Object | + Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockModuleName -PassThru | + Add-Member -Name 'Description' -MemberType NoteProperty -Value 'Mocked description' -PassThru | + Add-Member -Name 'Guid' -MemberType NoteProperty -Value '4c189dbd-d858-4893-bac0-d682423c5fc7' -PassThru | + Add-Member -Name 'ModuleBase' -MemberType NoteProperty -Value $mockModuleBase -PassThru | + Add-Member -Name 'ModuleType' -MemberType NoteProperty -Value 'Script' -PassThru | + Add-Member -Name 'Author' -MemberType NoteProperty -Value 'Mocked Author' -PassThru | + Add-Member -Name 'Version' -MemberType NoteProperty -Value ([System.Version]'2.0.0.0') -PassThru -Force + + $mockGalleryModule = New-Object -TypeName PSCustomObject | + Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockModuleName -PassThru | + Add-Member -Name 'Repository' -MemberType NoteProperty -Value 'PSGallery' -PassThru | + Add-Member -Name 'Version' -MemberType NoteProperty -Value ([System.Version]'3.0.0.0') -PassThru -Force + + $mockGetRightModule_SingleModule = { + return @($mockModule_v1) + } + + $mockGetRightModule_MultipleModules = { + return @( + $mockModule_v1 + $mockModule_v2 + ) + } + + $mockGetModule_SingleModule = { + return @($mockModule_v1) + } + + $mockGetModule_MultipleModules = { + return @( + $mockModule_v1 + $mockModule_v2 + ) + } + + $mockGetModuleRepositoryName = { + return $mockRepositoryName + } + + $mockGetInstallationPolicy_Trusted = { + return $true + } + + $mockGetInstallationPolicy_NotTrusted = { + return $false + } + + $mockFindModule = { + return $mockGalleryModule + } + + Describe 'MSFT_PSModule\Get-TargetResource' -Tag 'Get' { + Context 'When the system is in the desired state' { + Context 'When the configuration is present' { + Context 'When the module is trusted' { + BeforeEach { + Mock -CommandName Get-RightModule -MockWith $mockGetRightModule_SingleModule + Mock -CommandName Get-ModuleRepositoryName -MockWith $mockGetModuleRepositoryName + Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_Trusted + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + $getTargetResourceResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + } + + It 'Should return the correct values for the other properties' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + + $getTargetResourceResult.Ensure | Should -Be 'Present' + $getTargetResourceResult.Repository | Should -Be $mockRepositoryName + $getTargetResourceResult.Description | Should -Be $mockModule_v1.Description + $getTargetResourceResult.Guid | Should -Be $mockModule_v1.Guid + $getTargetResourceResult.ModuleBase | Should -Be $mockModule_v1.ModuleBase + $getTargetResourceResult.ModuleType | Should -Be $mockModule_v1.ModuleType + $getTargetResourceResult.Author | Should -Be $mockModule_v1.Author + $getTargetResourceResult.InstalledVersion | Should -Be $mockModule_v1.Version + $getTargetResourceResult.InstallationPolicy | Should -Be 'Trusted' + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is not trusted' { + BeforeEach { + Mock -CommandName Get-RightModule -MockWith $mockGetRightModule_SingleModule + Mock -CommandName Get-ModuleRepositoryName -MockWith $mockGetModuleRepositoryName + Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_NotTrusted + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + $getTargetResourceResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + } + + It 'Should return the correct values for the other properties' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + + $getTargetResourceResult.Ensure | Should -Be 'Present' + $getTargetResourceResult.Repository | Should -Be $mockRepositoryName + $getTargetResourceResult.Description | Should -Be $mockModule_v1.Description + $getTargetResourceResult.Guid | Should -Be $mockModule_v1.Guid + $getTargetResourceResult.ModuleBase | Should -Be $mockModule_v1.ModuleBase + $getTargetResourceResult.ModuleType | Should -Be $mockModule_v1.ModuleType + $getTargetResourceResult.Author | Should -Be $mockModule_v1.Author + $getTargetResourceResult.InstalledVersion | Should -Be $mockModule_v1.Version + $getTargetResourceResult.InstallationPolicy | Should -Be 'Untrusted' + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + } + } + + Context 'When there are multiple version of the same module' { + BeforeEach { + Mock -CommandName Get-RightModule -MockWith $mockGetRightModule_MultipleModules + Mock -CommandName Get-ModuleRepositoryName -MockWith $mockGetModuleRepositoryName + Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_Trusted + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + $getTargetResourceResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + } + + It 'Should return the correct module version' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + + $getTargetResourceResult.InstalledVersion | Should -Be $mockModule_v2.Version + } + + It 'Should return the correct values for the other properties' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + + $getTargetResourceResult.Ensure | Should -Be 'Present' + $getTargetResourceResult.Repository | Should -Be $mockRepositoryName + $getTargetResourceResult.Description | Should -Be $mockModule_v2.Description + $getTargetResourceResult.Guid | Should -Be $mockModule_v2.Guid + $getTargetResourceResult.ModuleBase | Should -Be $mockModule_v2.ModuleBase + $getTargetResourceResult.ModuleType | Should -Be $mockModule_v2.ModuleType + $getTargetResourceResult.Author | Should -Be $mockModule_v2.Author + $getTargetResourceResult.InstallationPolicy | Should -Be 'Trusted' + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the configuration is absent' { + BeforeEach { + Mock -CommandName Get-RightModule + Mock -CommandName Get-ModuleRepositoryName + Mock -CommandName Get-InstallationPolicy + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + $getTargetResourceResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 0 -Scope It + } + + It 'Should return the correct values for the other properties' { + $getTargetResourceResult = Get-TargetResource -Name $mockModuleName + + $getTargetResourceResult.Ensure | Should -Be 'Absent' + $getTargetResourceResult.Repository | Should -BeNullOrEmpty + $getTargetResourceResult.Description | Should -BeNullOrEmpty + $getTargetResourceResult.Guid | Should -BeNullOrEmpty + $getTargetResourceResult.ModuleBase | Should -BeNullOrEmpty + $getTargetResourceResult.ModuleType | Should -BeNullOrEmpty + $getTargetResourceResult.Author | Should -BeNullOrEmpty + $getTargetResourceResult.InstalledVersion | Should -BeNullOrEmpty + $getTargetResourceResult.InstallationPolicy | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 0 -Scope It + } + } + } + } + + Describe 'MSFT_PSModule\Set-TargetResource' -Tag 'Set' { + Context 'When the system is not in the desired state' { + Context 'When the configuration should be present' { + BeforeAll { + Mock -CommandName Find-Module -MockWith $mockFindModule + Mock -CommandName Install-Module + } + + Context 'When the repository is ''Trusted''' { + BeforeAll { + Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_Trusted + } + + It 'Should call the Install-Module with the correct parameters' { + { Set-TargetResource -Name $mockModuleName } | Should -Not -Throw + + Assert-MockCalled -CommandName Find-Module -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Install-Module -ParameterFilter { + $InputObject.Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the InstallationPolicy is ''Trusted''' { + BeforeAll { + Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_NotTrusted + } + + It 'Should call the Install-Module with the correct parameters' { + { Set-TargetResource -Name $mockModuleName -InstallationPolicy 'Trusted' } | Should -Not -Throw + + Assert-MockCalled -CommandName Find-Module -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Install-Module -ParameterFilter { + $InputObject.Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the Repository is ''PSGallery''' { + BeforeAll { + Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_Trusted + Mock -CommandName ValidateArgument + } + + It 'Should call the Install-Module with the correct parameters' { + { Set-TargetResource -Name $mockModuleName -Repository 'PSGallery' -RequiredVersion '1.0.0.0' } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-InstallationPolicy -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Find-Module -ParameterFilter { + $Name -eq $mockModuleName -and $Repository -eq 'PSGallery' + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Install-Module -ParameterFilter { + $InputObject.Name -eq $mockModuleName -and $InputObject.Repository -eq 'PSGallery' + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the InstallationPolicy is ''Untrusted'' and the repository is ''Untrusted''' { + BeforeAll { + Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_NotTrusted + } + + It 'Should throw the correct error' { + { Set-TargetResource -Name $mockModuleName } | + Should -Throw ($localizedData.InstallationPolicyFailed -f 'Untrusted', 'Untrusted') + } + } + + Context 'When the module name cannot be found' { + BeforeAll { + Mock -CommandName Find-Module + } + + It 'Should throw the correct error' { + { Set-TargetResource -Name $mockModuleName } | + Should -Throw ($localizedData.ModuleNotFoundInRepository -f $mockModuleName, '') + } + } + } + + Context 'When the configuration should be absent' { + Context 'When uninstalling a module that has a single version' { + BeforeAll { + Mock -CommandName Get-RightModule -MockWith $mockGetRightModule_SingleModule + Mock -CommandName Remove-Item + } + + It 'Should call the Remove-Item with the correct parameters' { + { Set-TargetResource -Name $mockModuleName -Ensure 'Absent' } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Remove-Item -ParameterFilter { + $path -eq $mockModuleBase + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module name cannot be found' { + BeforeAll { + Mock -CommandName Get-RightModule + } + + It 'Should throw the correct error' { + { Set-TargetResource -Name $mockModuleName -Ensure 'Absent' } | + Should -Throw ($localizedData.ModuleWithRightPropertyNotFound -f $mockModuleName, '') + } + } + } + } + } + + Describe 'MSFT_PSModule\Test-TargetResource' -Tag 'Test' { + Context 'When the system is in the desired state' { + Context 'When the configuration is present' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockModuleName + Repository = $mockRepositoryName + Description = $mockModule_v1.Description + Guid = $mockModule_v1.Guid + ModuleBase = $mockModule_v1.ModuleBase + ModuleType = $mockModule_v1.ModuleType + Author = $mockModule_v1.Author + InstalledVersion = $mockModule_v1.Version + InstallationPolicy = 'Untrusted' + } + } + } + + It 'Should return the same values as passed as parameters' { + $testTargetResourceResult = Test-TargetResource -Name $mockModuleName + $testTargetResourceResult | Should -Be $true + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration is absent' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockModuleName + Repository = $null + Description = $null + Guid = $null + ModuleBase = $null + ModuleType = $null + Author = $null + InstalledVersion = $null + InstallationPolicy = $null + } + } + } + + It 'Should return the same values as passed as parameters' { + $testTargetResourceResult = Test-TargetResource -Ensure 'Absent' -Name $mockModuleName + $testTargetResourceResult | Should -Be $true + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the system is not in the desired state' { + Context 'When the configuration should be present' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockModuleName + Repository = $mockRepositoryName + Description = $mockModule_v1.Description + Guid = $mockModule_v1.Guid + ModuleBase = $mockModule_v1.ModuleBase + ModuleType = $mockModule_v1.ModuleType + Author = $mockModule_v1.Author + InstalledVersion = $mockModule_v1.Version + InstallationPolicy = 'Untrusted' + } + } + } + + It 'Should return the same values as passed as parameters' { + $testTargetResourceResult = Test-TargetResource -Name $mockModuleName + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration should be absent' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockModuleName + Repository = $null + Description = $null + Guid = $null + ModuleBase = $null + ModuleType = $null + Author = $null + InstalledVersion = $null + InstallationPolicy = $null + } + } + } + + It 'Should return the same values as passed as parameters' { + $testTargetResourceResult = Test-TargetResource -Ensure 'Absent' -Name $mockModuleName + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + } + + Describe 'MSFT_PSModule\Get-ModuleRepositoryName' -Tag 'Helper' { + BeforeAll { + # Mock the file PSGetModuleInfo.xml in the module base folder. + New-Item -Path $mockModuleBase -ItemType File -Name 'PSGetModuleInfo.xml' -Force + + $mockModule = New-Object -TypeName Object | + Add-Member -Name 'ModuleBase' -MemberType NoteProperty -Value $mockModuleBase -PassThru -Force + + Mock -CommandName Import-Clixml -MockWith { + return New-Object -TypeName Object | + Add-Member -Name 'Repository' -MemberType NoteProperty -Value $mockRepositoryName -PassThru -Force + } + } + + It 'Should return the correct repository name of a module' { + Get-ModuleRepositoryName -Module $mockModule | Should -Be $mockRepositoryName + + Assert-MockCalled -CommandName 'Import-Clixml' -Exactly -Times 1 -Scope It + } + } + + Describe 'MSFT_PSModule\Get-RightModule' -Tag 'Helper' { + BeforeEach { + Mock -CommandName Get-ModuleRepositoryName -MockWith $mockGetModuleRepositoryName + } + + Context 'When the module does not exist' { + BeforeEach { + Mock -CommandName Get-Module + } + + It 'Should return $null' { + Get-RightModule -Name 'UnknownModule' | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName 'Get-Module' -Exactly -Times 1 -Scope It + } + } + + Context 'When one version of the module exist' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName + $getRightModuleResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When parameter Repository is specified, and one version of the module exist' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -Repository $mockRepositoryName -Verbose + $getRightModuleResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When parameter Repository is specified, and one version of the module exist, but from a different repository' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return $null' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -Repository 'OtherRepository' -Verbose + $getRightModuleResult.Name | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is required to have a specific version, and the specific version is installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -RequiredVersion '1.0.0.0' -Verbose + $getRightModuleResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is required to have a specific version, and the specific version is not installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return $null' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -RequiredVersion '2.0.0.0' -Verbose + $getRightModuleResult.Name | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is required to have a maximum version, and a suitable module is installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -MaximumVersion '1.0.0.0' -Verbose + $getRightModuleResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is required to have a minimum version, and a suitable module is installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -MinimumVersion '1.0.0.0' -Verbose + $getRightModuleResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is required to have a maximum version, and the suitable module is not installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return $null' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -MaximumVersion '0.9.0.0' -Verbose + $getRightModuleResult.Name | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is required to have a minimum version, and the suitable module is not installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return $null' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -MinimumVersion '2.0.0.0' -Verbose + $getRightModuleResult.Name | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the module is required to have a minimum and maximum version' { + Context 'When a suitable module is installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_SingleModule + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -MinimumVersion '0.9.0.0' -MaximumVersion '2.0.0.0' -Verbose + $getRightModuleResult.Name | Should -Be $mockModuleName + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + Context 'When there two suitable modules installed' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_MultipleModules + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -MinimumVersion '0.9.0.0' -MaximumVersion '2.0.0.0' -Verbose + $getRightModuleResult | Should -HaveCount 2 + $getRightModuleResult[0].Version.Major | Should -Be 1 + $getRightModuleResult[1].Version.Major | Should -Be 2 + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + + + Context 'When there two installed modules, but only one module is suitable' { + BeforeEach { + Mock -CommandName Get-Module -MockWith $mockGetModule_MultipleModules + } + + It 'Should return the correct module information' { + $getRightModuleResult = Get-RightModule -Name $mockModuleName -MinimumVersion '1.1.0.0' -MaximumVersion '2.0.0.0' -Verbose + $getRightModuleResult | Should -HaveCount 1 + $getRightModuleResult[0].Version.Major | Should -Be 2 + + Assert-MockCalled -CommandName 'Get-ModuleRepositoryName' -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName 'Get-Module' -ParameterFilter { + $Name -eq $mockModuleName + } -Exactly -Times 1 -Scope It + } + } + } + } + } +} +finally { + Invoke-TestCleanup +} diff --git a/DSC/Tests/Unit/PowerShellGet.LocalizationHelper.Tests.ps1 b/DSC/Tests/Unit/PowerShellGet.LocalizationHelper.Tests.ps1 new file mode 100644 index 00000000..4fed58ff --- /dev/null +++ b/DSC/Tests/Unit/PowerShellGet.LocalizationHelper.Tests.ps1 @@ -0,0 +1,199 @@ +<# + .SYNOPSIS + Automated unit test for helper functions in module PowerShellGet.ResourceHelper. +#> + + +$script:helperModuleName = 'PowerShellGet.LocalizationHelper' + +Describe "$script:helperModuleName Unit Tests" { + BeforeAll { + $resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $dscResourcesFolderFilePath = Join-Path -Path (Join-Path -Path $resourceModuleRoot -ChildPath 'Modules') ` + -ChildPath $script:helperModuleName + + Import-Module -Name (Join-Path -Path $dscResourcesFolderFilePath ` + -ChildPath "$script:helperModuleName.psm1") -Force + } + + InModuleScope $script:helperModuleName { + Describe 'Get-LocalizedData' { + $mockTestPath = { + return $mockTestPathReturnValue + } + + $mockImportLocalizedData = { + $BaseDirectory | Should -Be $mockExpectedLanguagePath + } + + BeforeEach { + Mock -CommandName Test-Path -MockWith $mockTestPath -Verifiable + Mock -CommandName Import-LocalizedData -MockWith $mockImportLocalizedData -Verifiable + } + + Context 'When loading localized data for Swedish' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 3 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + Context 'When $ScriptRoot is set to a path' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + } + } + + Context 'When loading localized data for English' { + Mock -CommandName Join-Path -MockWith { + return 'en-US' + } -Verifiable + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with en-US language' { + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + } + } + + Assert-VerifiableMock + } + + Describe 'New-InvalidResultException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidResultException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-InvalidResultException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'New-ObjectNotFoundException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-ObjectNotFoundException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-ObjectNotFoundException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'New-InvalidOperationException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidOperationException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-InvalidOperationException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.InvalidOperationException: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'New-InvalidArgumentException' { + Context 'When calling with both the Message and ArgumentName parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockArgumentName = 'MockArgument' + + { New-InvalidArgumentException -Message $mockErrorMessage -ArgumentName $mockArgumentName } | Should -Throw ('Parameter name: {0}' -f $mockArgumentName) + } + } + + Assert-VerifiableMock + } + } +} diff --git a/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 b/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 new file mode 100644 index 00000000..aadb9c29 --- /dev/null +++ b/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 @@ -0,0 +1,302 @@ +<# + .SYNOPSIS + Automated unit test for helper functions in module PowerShellGet.ResourceHelper. +#> + + +$script:helperModuleName = 'PowerShellGet.ResourceHelper' + +Describe "$script:helperModuleName Unit Tests" { + BeforeAll { + $resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $dscResourcesFolderFilePath = Join-Path -Path (Join-Path -Path $resourceModuleRoot -ChildPath 'Modules') ` + -ChildPath $script:helperModuleName + + Import-Module -Name (Join-Path -Path $dscResourcesFolderFilePath ` + -ChildPath "$script:helperModuleName.psm1") -Force + } + + InModuleScope $script:helperModuleName { + Describe 'ExtractArguments' { + Context 'When specific parameters should be returned' { + It 'Should return a hashtable with the correct values' { + $mockPSBoundParameters = @{ + Property1 = '1' + Property2 = '2' + Property3 = '3' + Property4 = '4' + } + + $extractArgumentsResult = ExtractArguments ` + -FunctionBoundParameters $mockPSBoundParameters ` + -ArgumentNames @('Property2', 'Property3') + + $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] + $extractArgumentsResult.Count | Should -Be 2 + $extractArgumentsResult.ContainsKey('Property2') | Should -BeTrue + $extractArgumentsResult.ContainsKey('Property3') | Should -BeTrue + $extractArgumentsResult.Property2 | Should -Be '2' + $extractArgumentsResult.Property3 | Should -Be '3' + } + } + + Context 'When the specific parameters to be returned does not exist' { + It 'Should return an empty hashtable' { + $mockPSBoundParameters = @{ + Property1 = '1' + } + + $extractArgumentsResult = ExtractArguments ` + -FunctionBoundParameters $mockPSBoundParameters ` + -ArgumentNames @('Property2', 'Property3') + + $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] + $extractArgumentsResult.Count | Should -Be 0 + } + } + + Context 'When and empty hashtable is passed in the parameter FunctionBoundParameters' { + It 'Should return an empty hashtable' { + $mockPSBoundParameters = @{ + } + + $extractArgumentsResult = ExtractArguments ` + -FunctionBoundParameters $mockPSBoundParameters ` + -ArgumentNames @('Property2', 'Property3') + + $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] + $extractArgumentsResult.Count | Should -Be 0 + } + } + } + + Describe 'ThrowError' { + It 'Should throw the correct error' { + { + $mockedErrorMessage = 'mocked error' + $mockErrorId = 'MockedError' + $mockExceptionName = 'InvalidOperationException' + + ThrowError ` + -ExceptionName $mockExceptionName ` + -ExceptionMessage $mockedErrorMessage ` + -ErrorId $mockErrorId ` + -ErrorCategory 'InvalidOperation' + } | Should -Throw $mockedErrorMessage + } + } + + Describe 'ValidateArgument' { + BeforeAll { + $mockProviderName = 'PowerShellGet' + } + + Context 'When passing a correct uri as ''Argument'' and type is ''SourceUri''' { + It 'Should not throw an error' { + { + ValidateArgument ` + -Argument 'https://mocked.uri' ` + -Type 'SourceUri' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + } + } + + Context 'When passing an invalid uri as ''Argument'' and type is ''SourceUri''' { + It 'Should throw the correct error' { + $mockArgument = 'mocked.uri' + + { + ValidateArgument ` + -Argument $mockArgument ` + -Type 'SourceUri' ` + -ProviderName $mockProviderName + } | Should -Throw ($LocalizedData.InValidUri -f $mockArgument) + } + } + + Context 'When passing a correct path as ''Argument'' and type is ''DestinationPath''' { + It 'Should not throw an error' { + { + ValidateArgument ` + -Argument 'TestDrive:\' ` + -Type 'DestinationPath' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + } + } + + Context 'When passing an invalid path as ''Argument'' and type is ''DestinationPath''' { + It 'Should throw the correct error' { + $mockArgument = 'TestDrive:\NonExistentPath' + + { + ValidateArgument ` + -Argument $mockArgument ` + -Type 'DestinationPath' ` + -ProviderName $mockProviderName + } | Should -Throw ($LocalizedData.PathDoesNotExist -f $mockArgument) + } + } + + Context 'When passing a correct uri as ''Argument'' and type is ''PackageSource''' { + It 'Should not throw an error' { + { + ValidateArgument ` + -Argument 'https://mocked.uri' ` + -Type 'PackageSource' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + } + } + + Context 'When passing an correct package source as ''Argument'' and type is ''PackageSource''' { + BeforeAll { + $mockArgument = 'PSGallery' + + Mock -CommandName Get-PackageSource -MockWith { + return New-Object -TypeName Object | + Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockArgument -PassThru | + Add-Member -Name 'ProviderName' -MemberType NoteProperty -Value $mockProviderName -PassThru -Force + } + } + + It 'Should not throw an error' { + { + ValidateArgument ` + -Argument $mockArgument ` + -Type 'PackageSource' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } + + Context 'When passing type is ''PackageSource'' and passing a package source that does not exist' { + BeforeAll { + $mockArgument = 'PSGallery' + + Mock -CommandName Get-PackageSource + } + + It 'Should not throw an error' { + { + ValidateArgument ` + -Argument $mockArgument ` + -Type 'PackageSource' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } + + Context 'When passing invalid type in parameter ''Type''' { + BeforeAll { + $mockType = 'UnknownType' + } + + It 'Should throw the correct error' { + { + ValidateArgument ` + -Argument 'AnyArgument' ` + -Type $mockType ` + -ProviderName $mockProviderName + } | Should -Throw ($LocalizedData.UnexpectedArgument -f $mockType) + } + } + } + + Describe 'ValidateVersionArgument' { + Context 'When not passing in any parameters (using default values)' { + It 'Should return true' { + ValidateVersionArgument | Should -BeTrue + } + } + + Context 'When only ''RequiredVersion'' are passed' { + It 'Should return true' { + ValidateVersionArgument -RequiredVersion '3.0.0.0' | Should -BeTrue + } + } + + Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { + It 'Should throw the correct error' { + { + ValidateVersionArgument ` + -MinimumVersion '2.0.0.0' ` + -MaximumVersion '1.0.0.0' + } | Should -Throw $LocalizedData.VersionError + } + } + + Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { + It 'Should throw the correct error' { + { + ValidateVersionArgument ` + -MinimumVersion '2.0.0.0' ` + -MaximumVersion '1.0.0.0' + } | Should -Throw $LocalizedData.VersionError + } + } + + Context 'When ''RequiredVersion'', ''MinimumVersion'', and ''MaximumVersion'' are passed' { + It 'Should throw the correct error' { + { + ValidateVersionArgument ` + -RequiredVersion '3.0.0.0' ` + -MinimumVersion '2.0.0.0' ` + -MaximumVersion '1.0.0.0' + } | Should -Throw $LocalizedData.VersionError + } + } + } + + Describe 'Get-InstallationPolicy' { + Context 'When the package source exist, and is trusted' { + BeforeAll { + Mock -CommandName Get-PackageSource -MockWith { + return New-Object -TypeName Object | + Add-Member -Name 'IsTrusted' -MemberType NoteProperty -Value $true -PassThru -Force + } + } + + It 'Should return true' { + Get-InstallationPolicy -RepositoryName 'PSGallery' | Should -BeTrue + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } + + Context 'When the package source exist, and is not trusted' { + BeforeAll { + Mock -CommandName Get-PackageSource -MockWith { + return New-Object -TypeName Object | + Add-Member -Name 'IsTrusted' -MemberType NoteProperty -Value $false -PassThru -Force + } + } + + It 'Should return false' { + + + Get-InstallationPolicy -RepositoryName 'PSGallery' | Should -BeFalse + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } + + Context 'When the package source does not exist' { + BeforeAll { + Mock -CommandName Get-PackageSource + } + + It 'Should return $null' { + Get-InstallationPolicy -RepositoryName 'Unknown' | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } + } + } +} diff --git a/appveyor.yml b/appveyor.yml index 05119f7d..95b052a1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,9 @@ version: 1.1.3.{build} environment: matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PowerShellEdition: Desktop + TestCategory: DSC - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 PowerShellEdition: Desktop - APPVEYOR_BUILD_WORKER_IMAGE: WMF 5 @@ -52,6 +55,22 @@ test_script: - ps: | Invoke-PowerShellGetTest +for: +# Only run for jobs where TestCategory is 'DSC'. +- + matrix: + only: + - TestCategory: DSC + + install: + - git clone https://github.com/PowerShell/DscResource.Tests %APPVEYOR_BUILD_FOLDER%\DSC\DscResource.Tests + - ps: | + Import-Module .\tools\build.psm1 + Invoke-DscPowerShellGetAppVeyorInstallTask + + + test_script: + - ps: Invoke-DscPowerShellGetAppVeyorTestTask # Upload the project along with TestResults as a zip archive on_finish: diff --git a/tools/build.psm1 b/tools/build.psm1 index 60cd2610..d74ff2fb 100644 --- a/tools/build.psm1 +++ b/tools/build.psm1 @@ -7,6 +7,7 @@ $script:IsCoreCLR = $PSVersionTable.ContainsKey('PSEdition') -and $PSVersionTabl $script:ProjectRoot = Split-Path -Path $PSScriptRoot -Parent $script:ModuleRoot = Join-Path -Path $ProjectRoot -ChildPath "src\PowerShellGet" $script:ModuleFile = Join-Path -Path $ModuleRoot -ChildPath "PSModule.psm1" +$script:DscModuleRoot = Join-Path -Path $ProjectRoot -ChildPath "DSC" $script:ArtifactRoot = Join-Path -Path $ProjectRoot -ChildPath "dist" @@ -100,7 +101,7 @@ function Install-PackageManagement { if (-not $NuGetProvider) { Install-PackageProvider -Name NuGet -Force } - + $FindModule_params = @{ Name = $OneGetModuleName } @@ -337,6 +338,9 @@ function New-ModulePSMFile { # Add the remaining part of the psm1 file from template. Get-Content -Path "$ModuleRoot\private\modulefile\PartTwo.ps1" | Out-File -FilePath $moduleFile -Append + # Copy the DSC resources into the dist folder. + Copy-Item -Path "$script:DscModuleRoot\DSCResources" -Destination "$script:ArtifactRoot\PowerShellGet" -Recurse + Copy-Item -Path "$script:DscModuleRoot\Modules" -Destination "$script:ArtifactRoot\PowerShellGet" -Recurse } function Update-ModuleManifestFunctions { # Update the psd1 file with the /public/psgetfunctions @@ -434,3 +438,107 @@ function Install-PublishedModule { Write-Verbose -Message "Copied module artifacts from $moduleFolder merged module artifact to`n$InstallLocation" } + +<# + .SYNOPSIS + This function changes the folder location in the current session to + the folder that is the root for the DSC resources + ('$env:APPVEYOR_BUILD_FOLDER/PowerShellGet'). + + .NOTES + Used by other helper functions when testing DSC resources in AppVeyor. +#> +function Set-AppVeyorLocationDscResourceModuleBuildFolder { + [CmdletBinding()] + param() + + $script:originalBuildFolder = $env:APPVEYOR_BUILD_FOLDER + $env:APPVEYOR_BUILD_FOLDER = Join-Path -Path $env:APPVEYOR_BUILD_FOLDER -ChildPath 'DSC' + Set-Location -Path $env:APPVEYOR_BUILD_FOLDER +} + +<# + .SYNOPSIS + This function changes the folder location in the current session back + to the original build folder that is the root for the module + ('$env:APPVEYOR_BUILD_FOLDER'). + + .NOTES + Used by other helper functions when testing DSC resources in AppVeyor. +#> +function Set-AppVeyorLocationOriginalBuildFolder { + [CmdletBinding()] + param() + + $env:APPVEYOR_BUILD_FOLDER = $script:originalBuildFolder + Set-Location -Path $env:APPVEYOR_BUILD_FOLDER +} + +<# + .SYNOPSIS + This function runs all the necessary install steps to prepare the + environment for testing of DSC resources. + + .NOTES + Used when testing DSC resources in AppVeyor. +#> +function Invoke-DscPowerShellGetAppVeyorInstallTask { + [CmdletBinding()] + param() + + # Temporary change the build folder during install phase of DscResource.Test framework. + Set-AppVeyorLocationDscResourceModuleBuildFolder + + try { + Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" + Invoke-AppveyorInstallTask + + # Remove any present PowerShellGet modules so DSC integration tests don't see multiple modules. + Remove-Module -Name PowerShellGet -Force + Get-Module -Name PowerShellGet -ListAvailable | ForEach-Object { + Remove-Item -Path (Split-Path -Path $_.Path -Parent) -Recurse -Force + } + } + catch { + throw $_ + } + finally { + Set-AppVeyorLocationOriginalBuildFolder + } + + Update-ModuleManifestFunctions + Publish-ModuleArtifacts + # Deploy PowerShellGet module so DSC integration tests uses the correct one. + Install-PublishedModule + + Get-Module -Name PowerShellGet -ListAvailable | ForEach-Object { + $pathNumber += 1 + Write-Verbose -Message ('Found module path {0}: {1}' -f $pathNumber, $_.Path) -Verbose + } +} + +<# + .SYNOPSIS + This function starts the test step and tests all of DSC resources. + + .NOTES + Used when testing DSC resources in AppVeyor. +#> +function Invoke-DscPowerShellGetAppVeyorTestTask { + [CmdletBinding()] + param() + + # Temporary change the build folder during testing of the DSC resources. + Set-AppVeyorLocationDscResourceModuleBuildFolder + + try { + Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" + Invoke-AppveyorTestScriptTask -CodeCoverage -ExcludeTag @() + } + catch { + throw $_ + } + finally { + Set-AppVeyorLocationOriginalBuildFolder + } +} From 8c6ed52eeb874f6a50fcd7c7485ae5ba7af60a12 Mon Sep 17 00:00:00 2001 From: pougetat Date: Sun, 10 Feb 2019 10:50:20 +0100 Subject: [PATCH 13/21] Fix Update-ModuleManifest clears FunctionsToExport, AliasesToExport, NestedModules #373 (#415) --- Tests/PSGetUpdateModuleManifest.Tests.ps1 | 27 ++++++++++++++++--- .../psgetfunctions/Update-ModuleManifest.ps1 | 18 ++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Tests/PSGetUpdateModuleManifest.Tests.ps1 b/Tests/PSGetUpdateModuleManifest.Tests.ps1 index 341714ae..f27ecad0 100644 --- a/Tests/PSGetUpdateModuleManifest.Tests.ps1 +++ b/Tests/PSGetUpdateModuleManifest.Tests.ps1 @@ -118,7 +118,7 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { # Action: # Update-ModuleManifest -Path [Path] # - # Expected Result: The updated manifest should have the same proerty values. + # Expected Result: The updated manifest should have the same property values. # It UpdateModuleManifestWithNoAdditionalParameters2 { @@ -144,6 +144,29 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { AssertEquals $updatedModuleInfo.CompanyName $editedModuleInfo.CompanyName "Company name should be $expectedCompanyName" AssertEquals $($text.length) $expectedLength "Number of wildcards should be $expectedLength" + } + + # Purpose: Validate Update-ModuleManifest will not reset original parameter values to default values + # + # Action: + # Update-ModuleManifest -Path [Path] + # + # Expected Result: The updated manifest should have the same property values. + # + It UpdateModuleManifestWithNoAdditionalParameters3 { + + New-ModuleManifest -Path $script:testManifestPath -ModuleVersion '1.0' -FunctionsToExport 'function1' -NestedModules 'Microsoft.PowerShell.Management' -AliasesToExport 'alias1' + Update-ModuleManifest -Path $script:testManifestPath + + Import-LocalizedData -BindingVariable ModuleManifestHashTable ` + -FileName (Microsoft.PowerShell.Management\Split-Path $script:testManifestPath -Leaf) ` + -BaseDirectory (Microsoft.PowerShell.Management\Split-Path $script:testManifestPath -Parent) ` + -ErrorAction SilentlyContinue ` + -WarningAction SilentlyContinue + + AssertEquals $ModuleManifestHashTable.FunctionsToExport 'function1' "FunctionsToExport should be 'function1'" + AssertEquals $ModuleManifestHashTable.NestedModules 'Microsoft.PowerShell.Management' "NestedModules should be 'module1'" + AssertEquals $ModuleManifestHashTable.AliasesToExport 'alias1' "AliasesToExport should be 'alias1'" } # Purpose: Validate Update-ModuleManifest will keep the original property values for DefaultCommandPrefix, @@ -214,7 +237,6 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { AssertEquals $updatedModuleInfo.AliasesToExport.Count 0 "AliasesToExport count should be 0" } - # Purpose: Update a module manifest with same parameters # # Action: Update-ModuleManifest -Path [path] -NestedModules -Guid -Author -CompanyName -Copyright -RootModule -ModuleVersion... @@ -652,7 +674,6 @@ Describe PowerShell.PSGet.UpdateModuleManifest -Tags 'BVT','InnerLoop' { } - # Purpose: Validate Update-ModuleManifest will throw errors when there are paths defined in FilePath that are not in the module base # # Action: diff --git a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 index 3d395011..3f0d6bfb 100644 --- a/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Update-ModuleManifest.ps1 @@ -270,13 +270,9 @@ function Update-ModuleManifest { $params.Add("NestedModules",$NestedModules) } - elseif($moduleInfo.NestedModules) + elseif($ModuleManifestHashTable -and $ModuleManifestHashTable.ContainsKey("NestedModules")) { - #Get the original module info from ManifestHashTab - if($ModuleManifestHashTable -and $ModuleManifestHashTable.ContainsKey("NestedModules")) - { - $params.Add("NestedModules",$ModuleManifestHashtable.NestedModules) - } + $params.Add("NestedModules",$ModuleManifestHashtable.NestedModules) } #Guid is read-only property @@ -519,6 +515,10 @@ function Update-ModuleManifest $params.Add("FunctionsToExport",($moduleInfo.ExportedFunctions.Keys -split ' ')) } } + elseif ($ModuleManifestHashTable -and $ModuleManifestHashTable.ContainsKey("FunctionsToExport")) + { + $params.Add("FunctionsToExport", $ModuleManifestHashTable['FunctionsToExport']) + } if($AliasesToExport -or $AliasesToExport -is [array]) { @@ -545,6 +545,10 @@ function Update-ModuleManifest $params.Add("AliasesToExport",($moduleInfo.ExportedAliases.Keys -split ' ')) } } + elseif ($ModuleManifestHashTable -and $ModuleManifestHashTable.ContainsKey("AliasesToExport")) + { + $params.Add("AliasesToExport", $ModuleManifestHashTable['AliasesToExport']) + } if($VariablesToExport) { @@ -585,7 +589,7 @@ function Update-ModuleManifest ForEach-Object { $parts = $_ -split '-', 2; $parts[-1] = $parts[-1] -replace "^$($moduleInfo.Prefix)"; $parts -join '-' } $params.Add("CmdletsToExport", $originalCmdlets) } - else + else { $params.Add("CmdletsToExport",($moduleInfo.ExportedCmdlets.Keys -split ' ')) } From cfdc6c72892871fe4848c0d4ff48802ffc6ba714 Mon Sep 17 00:00:00 2001 From: edwin young Date: Sun, 10 Feb 2019 22:05:03 -0800 Subject: [PATCH 14/21] Fix bug #9, use utf8 for .nuspec --- Tests/PSGetPublishScript.Tests.ps1 | 44 ++++++++++++++++++- .../functions/Publish-PSArtifactUtility.ps1 | 4 +- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Tests/PSGetPublishScript.Tests.ps1 b/Tests/PSGetPublishScript.Tests.ps1 index a2e845f3..d8883f54 100644 --- a/Tests/PSGetPublishScript.Tests.ps1 +++ b/Tests/PSGetPublishScript.Tests.ps1 @@ -86,6 +86,45 @@ function SuiteCleanup { RemoveItem $script:TempScriptsPath } +Describe PowerShell.PSGet.PublishNonEnglishCharacterScriptTests -Tags 'BVT' { + BeforeAll { + SuiteSetup + } + + AfterAll { + SuiteCleanup + } + + BeforeEach { + + } + + AfterEach { + RemoveItem "$script:PSGalleryRepoPath\*" + RemoveItem $script:PublishScriptFilePath + RemoveItem "$script:TempScriptsPath\*.ps1" + RemoveItem "$script:TempScriptsLiteralPath\*" + + } + + It "PublishScriptRoundTripsNonAnsiCharacters" { + $description = "Remplace toutes les occurrences d'un modèle de caractère" + New-ScriptFileInfo -Path $script:PublishScriptFilePath ` + -Version $script:PublishScriptVersion ` + -Author Author@contoso.com ` + -Description $description ` + -Force + + $sfi = Test-ScriptFileInfo -Path $script:PublishScriptFilePath + AssertEquals $description $sfi.Description + + Publish-Script -Path $script:PublishScriptFilePath -NuGetApiKey $script:ApiKey + $psgetItemInfo = Find-Script $script:PublishScriptName + + AssertEquals $description $psgetItemInfo.description + } + +} Describe PowerShell.PSGet.PublishScriptTests -Tags 'BVT','InnerLoop' { BeforeAll { SuiteSetup @@ -123,6 +162,7 @@ Describe PowerShell.PSGet.PublishScriptTests -Tags 'BVT','InnerLoop' { + # Purpose: Validate Publish-Script cmdlet with versioned script dependencies # # Action: @@ -1142,7 +1182,7 @@ Describe PowerShell.PSGet.PublishScriptTests -Tags 'BVT','InnerLoop' { # Purpose: Validate that Publish-Module prompts to upgrade NuGet.exe if local NuGet.exe file is less than minimum required version # - # Action: Publish-Script + # Action: Publish-Script # # Expected Result: Publish operation should fail, NuGet.exe should not upgrade to latest version # @@ -2128,4 +2168,4 @@ Describe PowerShell.PSGet.PublishScriptTests.P2 -Tags 'P2','OuterLoop' { AssertEquals $res2.Name $ScriptName "Find-Script didn't find the exact script which has dependencies, $res2" Assert ($res2.Dependencies.Name.Count -ge ($DepencyModuleNames.Count+$RequiredScripts.Count+1)) "Find-Script with -IncludeDependencies returned wrong results, $res4" } -} \ No newline at end of file +} diff --git a/src/PowerShellGet/private/functions/Publish-PSArtifactUtility.ps1 b/src/PowerShellGet/private/functions/Publish-PSArtifactUtility.ps1 index 390b1051..b1f662d9 100644 --- a/src/PowerShellGet/private/functions/Publish-PSArtifactUtility.ps1 +++ b/src/PowerShellGet/private/functions/Publish-PSArtifactUtility.ps1 @@ -471,7 +471,7 @@ $CsprojContent = @" Microsoft.PowerShell.Management\Remove-Item $NuspecPath -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false } - Microsoft.PowerShell.Management\Set-Content -Value $nuspec -Path $NuspecPath -Force -Confirm:$false -WhatIf:$false + Microsoft.PowerShell.Management\Set-Content -Value $nuspec -Path $NuspecPath -Force -Confirm:$false -WhatIf:$false -Encoding UTF8 # Create .nupkg file if($script:DotnetCommandPath) { @@ -553,7 +553,7 @@ $CsprojContent = @" if(Test-Path -Path $tempErrorFile -PathType Leaf) { $errorMsg = Microsoft.PowerShell.Management\Get-Content -Path $tempErrorFile -Raw - + if($errorMsg) { Write-Verbose -Message $errorMsg } From 041cb48bc42048b695433f0e8db7ee51b669ac6a Mon Sep 17 00:00:00 2001 From: Edwin Young Date: Mon, 11 Feb 2019 01:07:29 -0800 Subject: [PATCH 15/21] Fix #36 Allow piping psrepositories (#420) --- Tests/Pester.PSRepository.Tests.ps1 | 78 ++++++++++--------- .../psgetfunctions/Set-PSRepository.ps1 | 4 +- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/Tests/Pester.PSRepository.Tests.ps1 b/Tests/Pester.PSRepository.Tests.ps1 index 40b4ff00..f4c1474f 100644 --- a/Tests/Pester.PSRepository.Tests.ps1 +++ b/Tests/Pester.PSRepository.Tests.ps1 @@ -6,23 +6,21 @@ Import-Module "$PSScriptRoot\PSGetTestUtils.psm1" -WarningAction SilentlyContinu $RepositoryName = 'PSGallery' $SourceLocation = 'https://www.poshtestgallery.com/api/v2/' -$PublishLocation= 'https://www.poshtestgallery.com/api/v2/package/' -$ScriptSourceLocation= 'https://www.poshtestgallery.com/api/v2/items/psscript/' -$ScriptPublishLocation= 'https://www.poshtestgallery.com/api/v2/package/' +$PublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' +$ScriptSourceLocation = 'https://www.poshtestgallery.com/api/v2/items/psscript/' +$ScriptPublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' -Describe 'Test Register-PSRepository and Register-PackageSource for PSGallery repository' -tags 'BVT','InnerLoop' { +Describe 'Test Register-PSRepository and Register-PackageSource for PSGallery repository' -tags 'BVT', 'InnerLoop' { - BeforeAll { - Install-NuGetBinaries + BeforeAll { + Install-NuGetBinaries } AfterAll { - if(Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue) - { + if (Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue) { Set-PSRepository -Name $RepositoryName -InstallationPolicy Trusted } - else - { + else { Register-PSRepository -Default -InstallationPolicy Trusted } } @@ -31,6 +29,16 @@ Describe 'Test Register-PSRepository and Register-PackageSource for PSGallery re Unregister-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue } + It 'Should pipe from Get-PSRepository to Set' { + Register-PSRepository -Default + + Get-PSRepository $RepositoryName | Set-PSRepository -InstallationPolicy Trusted + + $repo = Get-PSRepository $RepositoryName + $repo.Name | should be $RepositoryName + $repo.Trusted | should be $true + } + It 'Register-PSRepository -Default: Should work' { Register-PSRepository -Default $repo = Get-PSRepository $RepositoryName @@ -57,14 +65,14 @@ Describe 'Test Register-PSRepository and Register-PackageSource for PSGallery re Register-PSRepository -Default -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'PackageSourceExists,Microsoft.PowerShell.PackageManagement.Cmdlets.RegisterPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Register-PSRepository -Default:$false : Should not register' { Register-PSRepository -Default:$false Get-PSRepository PSGallery -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'SourceNotFound,Microsoft.PowerShell.PackageManagement.Cmdlets.GetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Register-PSRepository -Name PSGallery -SourceLocation $SourceLocation : Should fail' { { Register-PSRepository $RepositoryName $SourceLocation -ErrorVariable ev -ErrorAction SilentlyContinue } | Should Throw @@ -100,51 +108,47 @@ Describe 'Test Register-PSRepository and Register-PackageSource for PSGallery re Register-PackageSource -ProviderName PowerShellGet -Name $RepositoryName -Location $SourceLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.RegisterPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') - + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + It 'Register-PackageSource -ProviderName PowerShellGet -Name PSGallery -PublishLocation $PublishLocation : Should fail' { Register-PackageSource -ProviderName PowerShellGet -Name $RepositoryName -PublishLocation $PublishLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.RegisterPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Register-PackageSource -ProviderName PowerShellGet -Name PSGallery -ScriptPublishLocation $ScriptPublishLocation : should fail' { Register-PackageSource -ProviderName PowerShellGet -Name $RepositoryName -ScriptPublishLocation $ScriptPublishLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.RegisterPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Register-PackageSource -ProviderName PowerShellGet -Name PSGallery -ScriptSourceLocation $ScriptSourceLocation : should fail' { Register-PackageSource -ProviderName PowerShellGet -Name PSGallery -ScriptSourceLocation $ScriptSourceLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.RegisterPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') } -Describe 'Test Set-PSRepository and Set-PackageSource for PSGallery repository' -tags 'BVT','InnerLoop' { +Describe 'Test Set-PSRepository and Set-PackageSource for PSGallery repository' -tags 'BVT', 'InnerLoop' { - BeforeAll { - Install-NuGetBinaries + BeforeAll { + Install-NuGetBinaries } AfterAll { - if(Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue) - { + if (Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue) { Set-PSRepository -Name $RepositoryName -InstallationPolicy Trusted } - else - { + else { Register-PSRepository -Default -InstallationPolicy Trusted } } BeforeEach { - if(Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue) - { + if (Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue) { Set-PSRepository -Name $RepositoryName -InstallationPolicy Untrusted } - else - { + else { Register-PSRepository -Default -InstallationPolicy Untrusted } } @@ -168,25 +172,25 @@ Describe 'Test Set-PSRepository and Set-PackageSource for PSGallery repository' Set-PSRepository $RepositoryName $SourceLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Set-PSRepository -Name PSGallery -PublishLocation $PublishLocation : should fail' { Set-PSRepository $RepositoryName -PublishLocation $PublishLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Set-PSRepository -Name PSGallery -ScriptPublishLocation $ScriptPublishLocation : should fail' { Set-PSRepository $RepositoryName -ScriptPublishLocation $ScriptPublishLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Set-PSRepository -Name PSGallery -ScriptSourceLocation $ScriptSourceLocation : should fail' { Set-PSRepository -Name $RepositoryName -ScriptSourceLocation $ScriptSourceLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Set-PackageSource -ProviderName PowerShellGet -Name PSGallery : should work actually this is a no-op. Installation policy should not be changed' { Set-PackageSource -ProviderName PowerShellGet -Name $RepositoryName @@ -206,23 +210,23 @@ Describe 'Test Set-PSRepository and Set-PackageSource for PSGallery repository' Set-PackageSource -ProviderName PowerShellGet -Name $RepositoryName -NewLocation $SourceLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Set-PackageSource -ProviderName PowerShellGet -Name PSGallery -PublishLocation $PublishLocation : should fail' { Set-PackageSource -ProviderName PowerShellGet -Name $RepositoryName -PublishLocation $PublishLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Set-PackageSource -ProviderName PowerShellGet -Name PSGallery -ScriptPublishLocation $ScriptPublishLocation : should fail' { Set-PackageSource -ProviderName PowerShellGet -Name $RepositoryName -ScriptPublishLocation $ScriptPublishLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') It 'Set-PackageSource -ProviderName PowerShellGet -Name PSGallery -ScriptSourceLocation $ScriptSourceLocation : should fail' { Set-PackageSource -ProviderName PowerShellGet -Name $RepositoryName -ScriptSourceLocation $ScriptSourceLocation -ErrorVariable ev -ErrorAction SilentlyContinue $ev[0].FullyQualifiedErrorId | Should be 'ParameterIsNotAllowedWithPSGallery,Add-PackageSource,Microsoft.PowerShell.PackageManagement.Cmdlets.SetPackageSource' } ` - -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') -} \ No newline at end of file + -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') +} diff --git a/src/PowerShellGet/public/psgetfunctions/Set-PSRepository.ps1 b/src/PowerShellGet/public/psgetfunctions/Set-PSRepository.ps1 index 5e647316..9ecbd691 100644 --- a/src/PowerShellGet/public/psgetfunctions/Set-PSRepository.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Set-PSRepository.ps1 @@ -6,7 +6,7 @@ function Set-PSRepository { HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=517128')] Param ( - [Parameter(Mandatory = $true, Position = 0)] + [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [string] $Name, @@ -166,7 +166,7 @@ function Set-PSRepository { } $PSBoundParameters[$script:PackageManagementProviderParam] = $PackageManagementProvider - $PSBoundParameters.Add("Trusted", $Trusted) + $PSBoundParameters["Trusted"] = $Trusted $PSBoundParameters["Provider"] = $script:PSModuleProviderName $PSBoundParameters["MessageResolver"] = $script:PackageManagementMessageResolverScriptBlock From cb52f7947f245ae25da8c1f9ddc35d1e2d538652 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 18 Feb 2019 03:28:15 +0100 Subject: [PATCH 16/21] PSModule: Clean up code (#424) --- .MetaTestOptIn.json | 11 + CHANGELOG.md | 177 +++++--- .../MSFT_PSModule/MSFT_PSModule.psm1 | 383 +++++++++++------- .../en-US/MSFT_PSModule.strings.psd1 | 28 +- DSC/Examples/.gitattributes | 1 + DSC/Examples/README.md | 7 + .../1-PSModule_InstallModuleConfig.ps1 | 73 ++++ .../2-PSModule_InstallModuleTrustedConfig.ps1 | 76 ++++ ...Module_InstallModuleAllowClobberConfig.ps1 | 74 ++++ ...ule_InstallModuleSpecificVersionConfig.ps1 | 83 ++++ ..._InstallModuleWithinVersionRangeConfig.ps1 | 93 +++++ .../6-PSModule_UninstallModuleConfig.ps1 | 74 ++++ .../PowerShellGet.ResourceHelper.psm1 | 176 +++----- DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 | 40 +- .../PowerShellGet.ResourceHelper.Tests.ps1 | 96 ++--- appveyor.yml | 8 +- tools/build.psm1 | 66 ++- 17 files changed, 1052 insertions(+), 414 deletions(-) create mode 100644 .MetaTestOptIn.json create mode 100644 DSC/Examples/.gitattributes create mode 100644 DSC/Examples/README.md create mode 100644 DSC/Examples/Resources/PSModule/1-PSModule_InstallModuleConfig.ps1 create mode 100644 DSC/Examples/Resources/PSModule/2-PSModule_InstallModuleTrustedConfig.ps1 create mode 100644 DSC/Examples/Resources/PSModule/3-PSModule_InstallModuleAllowClobberConfig.ps1 create mode 100644 DSC/Examples/Resources/PSModule/4-PSModule_InstallModuleSpecificVersionConfig.ps1 create mode 100644 DSC/Examples/Resources/PSModule/5-PSModule_InstallModuleWithinVersionRangeConfig.ps1 create mode 100644 DSC/Examples/Resources/PSModule/6-PSModule_UninstallModuleConfig.ps1 diff --git a/.MetaTestOptIn.json b/.MetaTestOptIn.json new file mode 100644 index 00000000..30245283 --- /dev/null +++ b/.MetaTestOptIn.json @@ -0,0 +1,11 @@ +[ + "Common Tests - Validate Module Files", + "Common Tests - Validate Script Files", + "Common Tests - Validate Example Files", + "Common Tests - Validate Example Files To Be Published", + "Common Tests - Required Script Analyzer Rules", + "Common Tests - New Error-Level Script Analyzer Rules", + "Common Tests - Custom Script Analyzer Rules", + "Common Tests - Validate Markdown Links", + "Common Tests - Relative Path Length" +] diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e30659f..d658af92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,63 +2,102 @@ ## Unreleased -* Update the AppVeyor CI test pipeline with a new job to run tests for +- Made the list entries in the CHANGELOG.md to use dash `-` throughout to + be consequent (before there was a mix of dashes and asterisk). +- Update the AppVeyor CI test pipeline with a new job to run tests for the DSC resources, primarily for the resource `PSModule`. The new job uses the test framework used for the DSC Resource Kit, the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) repository. -* Update .gitignore to ignore the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) +- Update .gitignore to ignore the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) test framework. When running unit test locally it is cloned into the local repository folder. -* Added module PowerShellGet.LocalizationHelper containing localization +- Added module PowerShellGet.LocalizationHelper containing localization helper functions for DSC resources, and unit tests for the helper functions. -* Moved helper functions for the DSC resource `PSModule` to the module +- Moved helper functions for the DSC resource `PSModule` to the module PowerShellGet.ResourceHelper. Added improved localization support, and code formatting against workspace VS Code settings. -* Changes to PSModule. - * Added improved localization support. - * Changed type on the parameters that had `[Switch]` to correctly use + - Cleaned up the code against the [DscResources style guideline](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md). + - Renamed helper functions in the PowerShellGet.ResourceHelper module + to use Verb-Noun. + - Refactored the error handling logic to use the localization helper + functions, and also so that the error handling could be tested. +- Changes to PSModule. + - Added improved localization support. + - Changed type on the parameters that had `[Switch]` to correctly use `Systen.Boolean` to match the schema.mof. - * Code formatting against workspace VS Code settings. - * Added unit tests. - * Added integration tests - * It is now possible to install a module and passing in `AllowClobber` + - Code formatting against workspace VS Code settings. + - Added unit tests. + - Added integration tests + - It is now possible to install a module and passing in `AllowClobber` when the modules package source is trusted (it already worked in other scenarios). -* Changed the AppVeyor CI build pipeline so it added the DSC resource + - Rephrased some of the localization strings. + - Cleaned up the code against the [DscResources style guideline](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md). + Suppressed some of the Script Analyzer rules that are not complaint + with the the Visual Studio Code workspace settings in this repository. + - Refactored the error handling logic to use the localization helper + functions, and also so that the error handling could be tested. + - Refactored the Get-TargetResource to return the correct hash table + when the current state is absent. + - Added new examples. +- Changed the AppVeyor CI build pipeline so it added the DSC resource `PSModule` and dependent helper modules (the `Modules` folder) to the AppVeyor artifact. +- Added the `.MetaTestOptIn.json` file to opt-in for a lot of common test + in the DscResource.Tests test framework that tests the DSC resources. +- The examples under the folder `DSC/Examples` will be [published to PowerShell Gallery](https://github.com/PowerShell/DscResource.Tests#publish-examples-to-powershell-gallery) + so that they show up in the gallery part of Azure State Configuration. + The examples are published under the account 'dscresourcekit' which is + owned by the PowerShell DSC Team (DSC Resource Kit). + - In the folder `DSC/Examples` a `.gitattributes` was added to make sure + the examples is always checkout out using CRLF. There is an issue + using `Test-ScriptFileInfo` when files is checkout out using only LF + which is the default in AppVeyor. + - In the file `appveyor.yml` the PowerShell Gallery API key was added + for the account 'dscresourcekit', which can only be decrypted using + the PowerShell AppVeyor account. + ## 2.0.4 + Bug Fix -* Remove PSGallery availability checks (#374) + +- Remove PSGallery availability checks (#374) ## 2.0.3 + Bug fixes and Improvements -* Fix CommandAlreadyAvailable error for PackageManagement module (#333) -* Remove trailing whitespace when value is not provided for Get-PSScriptInfoString (#337) (Thanks @thomasrayner) -* Expanded aliases for improved readability (#338) (Thanks @lazywinadmin) -* Improvements for Catalog tests (#343) -* Fix Update-ScriptInfoFile to preserve PrivateData (#346) (Thanks @tnieto88) -* Install modules with many commands faster (#351) + +- Fix CommandAlreadyAvailable error for PackageManagement module (#333) +- Remove trailing whitespace when value is not provided for Get-PSScriptInfoString (#337) (Thanks @thomasrayner) +- Expanded aliases for improved readability (#338) (Thanks @lazywinadmin) +- Improvements for Catalog tests (#343) +- Fix Update-ScriptInfoFile to preserve PrivateData (#346) (Thanks @tnieto88) +- Install modules with many commands faster (#351) New Features -* Tab completion for -Repository parameter (#339) and for Publish-Module -Name (#359) (Thanks @matt9ucci) + +- Tab completion for -Repository parameter (#339) and for Publish-Module -Name (#359) (Thanks @matt9ucci) ## 2.0.1 + Bug fixes + - Resolved Publish-Module doesn't report error but fails to publish module (#316) - Resolved CommandAlreadyAvailable error while installing the latest version of PackageManagement module (#333) ## 2.0.0 + Breaking Change + - Default installation scope for Install-Module, Install-Script, and Install-Package has changed. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times. For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser. - ## 1.6.7 Bug fixes + - Resolved Install/Save-Module error in PSCore 6.1.0-preview.4 on Ubuntu 18.04 OS (WSL/Azure) (#313) - Updated error message in Save-Module cmdlet when the specified path is not accessible (#313) - Added few additional verbose messages (#313) @@ -66,35 +105,41 @@ Bug fixes ## 1.6.6 Dependency Updates -* Add dependency on version 4.1.0 or newer of NuGet.exe -* Update NuGet.exe bootstrap URL to https://aka.ms/psget-nugetexe + +- Add dependency on version 4.1.0 or newer of NuGet.exe +- Update NuGet.exe bootstrap URL to https://aka.ms/psget-nugetexe Build and Code Cleanup Improvements -* Improved error handling in network connectivity tests. + +- Improved error handling in network connectivity tests. Bug fixes + - Change Update ModuleManifest so that prefix is not added to CmdletsToExport. - Change Update ModuleManifest so that parameters will not reset to default values. -- Specify AllowPrereleseVersions provider option only when AllowPrerelease is specified on the PowerShellGet cmdlets. +- Specify AllowPrereleaseVersions provider option only when AllowPrerelease is specified on the PowerShellGet cmdlets. ## 1.6.5 New features -* Allow Pester/PSReadline installation when signed by non-Microsoft certificate (#258) + +- Allow Pester/PSReadline installation when signed by non-Microsoft certificate (#258) - Whitelist installation of non-Microsoft signed Pester and PSReadline over Microsoft signed Pester and PSReadline. Build and Code Cleanup Improvements -* Splitting of functions (#229) (Thanks @Benny1007) + +- Splitting of functions (#229) (Thanks @Benny1007) - Moves private functions into respective private folder. - Moves public functions as defined in PSModule.psd1 into respective public folder. - Removes all functions from PSModule.psm1 file. - Dot sources the functions from PSModule.psm1 file. - Uses Export-ModuleMember to export the public functions from PSModule.psm1 file. -* Add build step to construct a single .psm1 file (#242) (Thanks @Benny1007) +- Add build step to construct a single .psm1 file (#242) (Thanks @Benny1007) - Merged public and private functions into one .psm1 file to increase load time performance. Bug fixes + - Fix null parameter error caused by MinimumVersion in Publish-PackageUtility (#201) - Change .ExternalHelp link from PSGet.psm1-help.xml to PSModule-help.xml in PSModule.psm1 file (#215) - Change Publish-* to allow version comparison instead of string comparison (#219) @@ -106,31 +151,35 @@ Bug fixes ## 1.6.0 New features -* Prerelease Version Support (#185) + +- Prerelease Version Support (#185) - Implemented prerelease versions functionality in PowerShellGet cmdlets. - Enables publishing, discovering, and installing the prerelease versions of modules and scripts from the PowerShell Gallery. - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/PrereleaseModule) -* Enabled publish cmdlets on PWSH and Nano Server (#196) +- Enabled publish cmdlets on PWSH and Nano Server (#196) - Dotnet command version 2.0.0 or newer should be installed by the user prior to using the publish cmdlets on PWSH and Windows Nano Server. - Users can install the dotnet command by following the instructions specified at https://aka.ms/dotnet-install-script . - On Windows, users can install the dotnet command by running *Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile '.\dotnet-install.ps1'; & '.\dotnet-install.ps1' -Channel Current -Version '2.0.0'* - Publish cmdlets on Windows PowerShell supports using the dotnet command for publishing operations. Breaking Change + - PWSH: Changed the installation location of AllUsers scope to the parent of $PSHOME instead of $PSHOME. It is the SHARED_MODULES folder on PWSH. Bug fixes + - Update HelpInfoURI to 'https://go.microsoft.com/fwlink/?linkid=855963' (#195) - Ensure MyDocumentsPSPath path is correct (#179) (Thanks @lwsrbrts) ## 1.5.0.0 New features -* Added support for modules requiring license acceptance (#150) + +- Added support for modules requiring license acceptance (#150) - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/RequireLicenseAcceptance) -* Added version for REQUIREDSCRIPTS (#162) +- Added version for REQUIREDSCRIPTS (#162) - Enabled following scenarios for REQUIREDSCRIPTS - [1.0] - RequiredVersion - [1.0,2.0] - Min and Max Version @@ -138,55 +187,59 @@ New features - 1.0 - Min Version Bug fixes -* Fixed empty version value in nuspec (#157) + +- Fixed empty version value in nuspec (#157) ## 1.1.3.2 -* Disabled PowerShellGet Telemetry on PS Core as PowerShell Telemetry APIs got removed in PowerShell Core beta builds. (#153) -* Fixed for DateTime format serialization issue. (#141) -* Update-ModuleManifest should add ExternalModuleDependencies value as a collection. (#129) + +- Disabled PowerShellGet Telemetry on PS Core as PowerShell Telemetry APIs got removed in PowerShell Core beta builds. (#153) +- Fixed for DateTime format serialization issue. (#141) +- Update-ModuleManifest should add ExternalModuleDependencies value as a collection. (#129) ## 1.1.3.1 New features -* Added `PrivateData` field to ScriptFileInfo. (#119) + +- Added `PrivateData` field to ScriptFileInfo. (#119) Bug fixes -* Fixed Add-Type issue in v6.0.0-beta.1 release of PowerShellCore. (#125, #124) -* Install-Script -Scope CurrentUser PATH changes should not require a reboot for new PS processes. (#124) - - Made changes to broadcast the Environment variable changes, so that other processes pick changes to Environment variables without having to reboot or logoff/logon. -* Changed `Get-EnvironmentVariable` to get the unexpanded version of `%path%`. (#117) -* Refactor credential parameter propagation to sub-functions. (#104) -* Added credential parameter to subsequent calls of `Publish-Module/Script`. (#93) - - This is needed when a module is published that has the RequiredModules attribute in the manifest on a repository that does not have anonymous access because the required module lookups will fail. + +- Fixed Add-Type issue in v6.0.0-beta.1 release of PowerShellCore. (#125, #124) +- Install-Script -Scope CurrentUser PATH changes should not require a reboot for new PS processes. (#124) + - Made changes to broadcast the Environment variable changes, so that other processes pick changes to Environment variables without having to reboot or logoff/logon. +- Changed `Get-EnvironmentVariable` to get the unexpanded version of `%path%`. (#117) +- Refactor credential parameter propagation to sub-functions. (#104) +- Added credential parameter to subsequent calls of `Publish-Module/Script`. (#93) + - This is needed when a module is published that has the RequiredModules attribute in the manifest on a repository that does not have anonymous access because the required module lookups will fail. ## 1.1.2.0 Bug fixes -* Renamed `PublishModuleIsNotSupportedOnNanoServer` errorid to `PublishModuleIsNotSupportedOnPowerShellCoreEdition`. (#44) - - Also renamed `PublishScriptIsNotSupportedOnNanoServer` to `PublishScriptIsNotSupportedOnPowerShellCoreEdition`. -* Fixed an issue in `Update-Module` and `Update-Script` cmdlets to show proper version of current item being updated in `Confirm`/`WhatIf` message. (#44) -* Updated `Test-ModuleInstalled` function to return single module instead of multiple modules. (#44) -* Updated `ModuleCommandAlreadyAvailable` error message to include all conflicting commands instead of one. (#44) - - Corresponding changes to collect the complete set of conflicting commands from the being installed. - - Also ensured that conflicting commands from PSModule.psm1 are ignored in the command collision analysis as Get-Command includes the commands from current local scope as well. -* Fixed '[Test-ScriptFileInfo] Fails on *NIX newlines (LF vs. CRLF)' (#18) +- Renamed `PublishModuleIsNotSupportedOnNanoServer` errorid to `PublishModuleIsNotSupportedOnPowerShellCoreEdition`. (#44) + - Also renamed `PublishScriptIsNotSupportedOnNanoServer` to `PublishScriptIsNotSupportedOnPowerShellCoreEdition`. +- Fixed an issue in `Update-Module` and `Update-Script` cmdlets to show proper version of current item being updated in `Confirm`/`WhatIf` message. (#44) +- Updated `Test-ModuleInstalled` function to return single module instead of multiple modules. (#44) +- Updated `ModuleCommandAlreadyAvailable` error message to include all conflicting commands instead of one. (#44) + - Corresponding changes to collect the complete set of conflicting commands from the being installed. + - Also ensured that conflicting commands from PSModule.psm1 are ignored in the command collision analysis as Get-Command includes the commands from current local scope as well. +- Fixed '[Test-ScriptFileInfo] Fails on *NIX newlines (LF vs. CRLF)' (#18) ## 1.1.1.0 Bug fixes -* Fixed 'Update-Module fails with `ModuleAuthenticodeSignature` error for modules with signed PSD1'. (#12) (#8) -* Fixed 'Properties of `AdditionalMetadata` are case-sensitive'. #7 -* Changed `ErrorAction` to `Ignore` for few cmdlet usages as they should not show up in ErrorVariable. - - For example, error returned by `Get-Command Test-FileCatalog` should be ignored. +- Fixed 'Update-Module fails with `ModuleAuthenticodeSignature` error for modules with signed PSD1'. (#12) (#8) +- Fixed 'Properties of `AdditionalMetadata` are case-sensitive'. #7 +- Changed `ErrorAction` to `Ignore` for few cmdlet usages as they should not show up in ErrorVariable. + - For example, error returned by `Get-Command Test-FileCatalog` should be ignored. ## 1.1.0.0 -* Initial release from GitHub. -* PowerShellCore support. -* Security enhancements including the enforcement of catalog-signed modules during installation. -* Authenticated Repository support. -* Proxy Authentication support. -* Responses to a number of user requests and issues. +- Initial release from GitHub. +- PowerShellCore support. +- Security enhancements including the enforcement of catalog-signed modules during installation. +- Authenticated Repository support. +- Proxy Authentication support. +- Responses to a number of user requests and issues. diff --git a/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 b/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 index 230c2a13..ae5cfbe3 100644 --- a/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 +++ b/DSC/DSCResources/MSFT_PSModule/MSFT_PSModule.psm1 @@ -24,12 +24,7 @@ $helperName = 'PowerShellGet.ResourceHelper' $dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" Import-Module -Name $dscResourcesFolderFilePath -# DSC Resource for the $CurrentProviderName. -$CurrentProviderName = "PowerShellGet" - -# Return the current state of the resource. -function Get-TargetResource { - <# +<# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell Gallery and install it on your computer. @@ -61,66 +56,84 @@ function Get-TargetResource { .PARAMETER SkipPublisherCheck Allows the installation of modules that have not been catalog signed. +#> +function Get-TargetResource { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. #> - + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-ForEachStatement', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] $Name, + [Parameter()] [System.String] - $Repository = "PSGallery", + $Repository = 'PSGallery', + [Parameter()] [System.String] $RequiredVersion, + [Parameter()] [System.String] $MaximumVersion, + [Parameter()] [System.String] $MinimumVersion, + [Parameter()] [System.Boolean] $Force, + [Parameter()] [System.Boolean] $AllowClobber, + [Parameter()] [System.Boolean] $SkipPublisherCheck ) - # Initialize the $Ensure variable. - $ensure = 'Absent' + $returnValue = @{ + Ensure = 'Absent' + Name = $Name + Repository = $Repository + Description = $null + Guid = $null + ModuleBase = $null + ModuleType = $null + Author = $null + InstalledVersion = $null + RequiredVersion = $RequiredVersion + MinimumVersion = $MinimumVersion + MaximumVersion = $MaximumVersion + Force = $Force + AllowClobber = $AllowClobber + SkipPublisherCheck = $SkipPublisherCheck + InstallationPolicy = $null + } + + Write-Verbose -Message ($localizedData.GetTargetResourceMessage -f $Name) - $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') # Get the module with the right version and repository properties. $modules = Get-RightModule @extractedArguments -ErrorAction SilentlyContinue -WarningAction SilentlyContinue # If the module is found, the count > 0 - if ($modules.count -gt 0) { - $ensure = 'Present' + if ($modules.Count -gt 0) { + Write-Verbose -Message ($localizedData.ModuleFound -f $Name) - Write-Verbose -Message ($localizedData.ModuleFound -f $($Name)) - } - else { - Write-Verbose -Message ($localizedData.ModuleNotFound -f $($Name)) - } - - Write-Debug -Message "Ensure of $($Name) module is $($ensure)" - - if ($ensure -eq 'Absent') { - $returnValue = @{ - Ensure = $ensure - Name = $Name - } - } - else { # Find a module with the latest version and return its properties. $latestModule = $modules[0] @@ -134,25 +147,31 @@ function Get-TargetResource { $repositoryName = Get-ModuleRepositoryName -Module $latestModule -ErrorAction SilentlyContinue -WarningAction SilentlyContinue $installationPolicy = Get-InstallationPolicy -RepositoryName $repositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - - $returnValue = @{ - Ensure = $ensure - Name = $Name - Repository = $repositoryName - Description = $latestModule.Description - Guid = $latestModule.Guid - ModuleBase = $latestModule.ModuleBase - ModuleType = $latestModule.ModuleType - Author = $latestModule.Author - InstalledVersion = $latestModule.Version - InstallationPolicy = if ($installationPolicy) {"Trusted"}else {"Untrusted"} + if ($installationPolicy) { + $installationPolicyReturnValue = 'Trusted' + } + else { + $installationPolicyReturnValue = 'Untrusted' } + + $returnValue.Ensure = 'Present' + $returnValue.Repository = $repositoryName + $returnValue.Description = $latestModule.Description + $returnValue.Guid = $latestModule.Guid + $returnValue.ModuleBase = $latestModule.ModuleBase + $returnValue.ModuleType = $latestModule.ModuleType + $returnValue.Author = $latestModule.Author + $returnValue.InstalledVersion = $latestModule.Version + $returnValue.InstallationPolicy = $installationPolicyReturnValue + } + else { + Write-Verbose -Message ($localizedData.ModuleNotFound -f $Name) } + return $returnValue } -function Test-TargetResource { - <# +<# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell Gallery and install it on your computer. @@ -190,50 +209,66 @@ function Test-TargetResource { .PARAMETER SkipPublisherCheck Allows the installation of modules that have not been catalog signed. +#> +function Test-TargetResource { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. #> - + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] [OutputType([System.Boolean])] param ( - [ValidateSet("Present", "Absent")] + [Parameter()] + [ValidateSet('Present', 'Absent')] [System.String] - $Ensure = "Present", + $Ensure = 'Present', - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] $Name, + [Parameter()] [System.String] - $Repository = "PSGallery", + $Repository = 'PSGallery', - [ValidateSet("Trusted", "Untrusted")] + [Parameter()] + [ValidateSet('Trusted', 'Untrusted')] [System.String] - $InstallationPolicy = "Untrusted", + $InstallationPolicy = 'Untrusted', + [Parameter()] [System.String] $RequiredVersion, + [Parameter()] [System.String] $MaximumVersion, + [Parameter()] [System.String] $MinimumVersion, + [Parameter()] [System.Boolean] $Force, + [Parameter()] [System.Boolean] $AllowClobber, + [Parameter()] [System.Boolean] $SkipPublisherCheck ) - Write-Debug -Message "Calling Test-TargetResource" + Write-Verbose -Message ($localizedData.TestTargetResourceMessage -f $Name) - $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') $status = Get-TargetResource @extractedArguments @@ -248,8 +283,7 @@ function Test-TargetResource { } } -function Set-TargetResource { - <# +<# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell Gallery and install it on your computer. @@ -287,69 +321,87 @@ function Set-TargetResource { .PARAMETER SkipPublisherCheck Allows the installation of modules that have not been catalog signed. +#> +function Set-TargetResource { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. #> - + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-ForEachStatement', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-TryStatement', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-CatchClause', '')] [CmdletBinding()] param ( - [ValidateSet("Present", "Absent")] + [Parameter()] + [ValidateSet('Present', 'Absent')] [System.String] - $Ensure = "Present", + $Ensure = 'Present', - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] $Name, + [Parameter()] [System.String] - $Repository = "PSGallery", + $Repository = 'PSGallery', - [ValidateSet("Trusted", "Untrusted")] + [Parameter()] + [ValidateSet('Trusted', 'Untrusted')] [System.String] - $InstallationPolicy = "Untrusted", + $InstallationPolicy = 'Untrusted', + [Parameter()] [System.String] $RequiredVersion, + [Parameter()] [System.String] $MaximumVersion, + [Parameter()] [System.String] $MinimumVersion, + [Parameter()] [System.Boolean] $Force, + [Parameter()] [System.Boolean] $AllowClobber, + [Parameter()] [System.Boolean] $SkipPublisherCheck ) # Validate the repository argument - if ($PSBoundParameters.ContainsKey("Repository")) { - ValidateArgument -Argument $Repository -Type "PackageSource" -ProviderName $CurrentProviderName -Verbose + if ($PSBoundParameters.ContainsKey('Repository')) { + Test-ParameterValue -Value $Repository -Type 'PackageSource' -ProviderName 'PowerShellGet' -Verbose } - if ($Ensure -ieq "Present") { + if ($Ensure -ieq 'Present') { # Version check - $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("MinimumVersion", "MaximumVersion", "RequiredVersion") - - ValidateVersionArgument @extractedArguments + $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ('MinimumVersion', 'MaximumVersion', 'RequiredVersion') - $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + Test-VersionParameter @extractedArguments - Write-Verbose -Message ($localizedData.StartFindmodule -f $($Name)) + try { + $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') - $modules = Find-Module @extractedArguments -ErrorVariable ev + Write-Verbose -Message ($localizedData.StartFindModule -f $Name) - if (-not $modules) { - ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.ModuleNotFoundInRepository -f $Name, $ev.Exception) ` - -ErrorId "ModuleNotFoundInRepository" ` - -ErrorCategory InvalidOperation + $modules = Find-Module @extractedArguments -ErrorVariable ev + } + catch { + $errorMessage = $script:localizedData.ModuleNotFoundInRepository -f $Name + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } $trusted = $null @@ -366,83 +418,77 @@ function Set-TargetResource { } } - # The respository is trusted, so we install it. - if ($trusted) { - Write-Verbose -Message ($localizedData.StartInstallModule -f $Name, $moduleFound.Version.toString(), $moduleFound.Repository) + try { + # The repository is trusted, so we install it. + if ($trusted) { + Write-Verbose -Message ($localizedData.StartInstallModule -f $Name, $moduleFound.Version.toString(), $moduleFound.Repository) - # Extract the installation options. - $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("Force", "AllowClobber", "SkipPublisherCheck") + # Extract the installation options. + $extractedSwitches = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters -ArgumentNames ('Force', 'AllowClobber', 'SkipPublisherCheck') - $moduleFound | Install-Module @extractedSwitches -ErrorVariable ev - } - # The repository is untrusted but user's installation policy is trusted, so we install it with a warning. - elseif ($InstallationPolicy -ieq 'Trusted') { - Write-Warning -Message ($localizedData.InstallationPolicyWarning -f $Name, $modules[0].Repository, $InstallationPolicy) + $moduleFound | Install-Module @extractedSwitches + } + # The repository is untrusted but user's installation policy is trusted, so we install it with a warning. + elseif ($InstallationPolicy -ieq 'Trusted') { + Write-Warning -Message ($localizedData.InstallationPolicyWarning -f $Name, $modules[0].Repository, $InstallationPolicy) - # Extract installation options (Force implied by InstallationPolicy). - $extractedSwitches = ExtractArguments -FunctionBoundParameters $PSBoundParameters -ArgumentNames ("AllowClobber", "SkipPublisherCheck") + # Extract installation options (Force implied by InstallationPolicy). + $extractedSwitches = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters -ArgumentNames ('AllowClobber', 'SkipPublisherCheck') - # If all the repositories are untrusted, we choose the first one. - $modules[0] | Install-Module @extractedSwitches -Force -ErrorVariable ev - } - # Both user and repository is untrusted - else { - ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.InstallationPolicyFailed -f $InstallationPolicy, "Untrusted") ` - -ErrorId "InstallationPolicyFailed" ` - -ErrorCategory InvalidOperation - } + # If all the repositories are untrusted, we choose the first one. + $modules[0] | Install-Module @extractedSwitches -Force + } + # Both user and repository is untrusted + else { + $errorMessage = $script:localizedData.InstallationPolicyFailed -f $InstallationPolicy, 'Untrusted' + New-InvalidOperationException -Message $errorMessage + } - if ($ev) { - ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.FailtoInstall -f $Name, $ev.Exception) ` - -ErrorId "FailtoInstall" ` - -ErrorCategory InvalidOperation + Write-Verbose -Message ($localizedData.InstalledSuccess -f $Name) } - else { - Write-Verbose -Message ($localizedData.InstalledSuccess -f $($Name)) + catch { + $errorMessage = $script:localizedData.FailToInstall -f $Name + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } } # Ensure=Absent else { - $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("Name", "Repository", "MinimumVersion", "MaximumVersion", "RequiredVersion") + $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') # Get the module with the right version and repository properties. - $modules = Get-RightModule @extractedArguments -ErrorVariable ev + $modules = Get-RightModule @extractedArguments - if ((-not $modules) -or $ev) { - ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.ModuleWithRightPropertyNotFound -f $Name, $ev.Exception) ` - -ErrorId "ModuleWithRightPropertyNotFound" ` - -ErrorCategory InvalidOperation + if (-not $modules) { + $errorMessage = $script:localizedData.ModuleWithRightPropertyNotFound -f $Name + New-InvalidOperationException -Message $errorMessage } foreach ($module in $modules) { # Get the path where the module is installed. $path = $module.ModuleBase - Write-Verbose -Message ($localizedData.StartUnInstallModule -f $($Name)) + Write-Verbose -Message ($localizedData.StartUnInstallModule -f $Name) - # There is no Uninstall-Module cmdlet exists, so we will remove the ModuleBase folder as an uninstall operation. - Microsoft.PowerShell.Management\Remove-Item -Path $path -Force -Recurse -ErrorVariable ev + try { + <# + There is no Uninstall-Module cmdlet for Windows PowerShell 4.0, + so we will remove the ModuleBase folder as an uninstall operation. + #> + Microsoft.PowerShell.Management\Remove-Item -Path $path -Force -Recurse - if ($ev) { - ThrowError -ExceptionName "System.InvalidOperationException" ` - -ExceptionMessage ($localizedData.FailtoUninstall -f $module.Name, $ev.Exception) ` - -ErrorId "FailtoUninstall" ` - -ErrorCategory InvalidOperation + Write-Verbose -Message ($localizedData.UnInstalledSuccess -f $module.Name) } - else { - Write-Verbose -Message ($localizedData.UnInstalledSuccess -f $($module.Name)) + catch { + $errorMessage = $script:localizedData.FailToUninstall -f $module.Name + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } } # foreach } # Ensure=Absent } -Function Get-RightModule { - <# +<# .SYNOPSIS This is a helper function. It returns the modules that meet the specified versions and the repository requirements. @@ -460,25 +506,37 @@ Function Get-RightModule { .PARAMETER Repository Specifies the name of the module source repository where the module can be found. +#> +function Get-RightModule { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. #> - + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-ForEachStatement', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Name, + [Parameter()] [System.String] $RequiredVersion, + [Parameter()] [System.String] $MinimumVersion, + [Parameter()] [System.String] $MaximumVersion, + [Parameter()] [System.String] $Repository ) @@ -491,11 +549,14 @@ Function Get-RightModule { return $null } - # As Get-Module does not take RequiredVersion, MinimumVersion, MaximumVersion, or Repository, below we need to check - # whether the modules are containing the right version and repository location. + <# + As Get-Module does not take RequiredVersion, MinimumVersion, MaximumVersion, or Repository, + below we need to check whether the modules are containing the right version and repository + location. + #> - $extractedArguments = ExtractArguments -FunctionBoundParameters $PSBoundParameters ` - -ArgumentNames ("MaximumVersion", "MinimumVersion", "RequiredVersion") + $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames ('MaximumVersion', 'MinimumVersion', 'RequiredVersion') $returnVal = @() foreach ($m in $modules) { @@ -508,22 +569,22 @@ Function Get-RightModule { } # Case 2 - a user provides RequiredVersion - elseif ($extractedArguments.ContainsKey("RequiredVersion")) { - # Check if it matches with the installedversion - $versionMatch = ($installedVersion -eq [System.Version]$RequiredVersion) + elseif ($extractedArguments.ContainsKey('RequiredVersion')) { + # Check if it matches with the installed version + $versionMatch = ($installedVersion -eq [System.Version] $RequiredVersion) } else { # Case 3 - a user provides MinimumVersion - if ($extractedArguments.ContainsKey("MinimumVersion")) { - $versionMatch = ($installedVersion -ge [System.Version]$extractedArguments['MinimumVersion']) + if ($extractedArguments.ContainsKey('MinimumVersion')) { + $versionMatch = ($installedVersion -ge [System.Version] $extractedArguments['MinimumVersion']) } # Case 4 - a user provides MaximumVersion - if ($extractedArguments.ContainsKey("MaximumVersion")) { - $isLessThanMax = ($installedVersion -le [System.Version]$extractedArguments['MaximumVersion']) + if ($extractedArguments.ContainsKey('MaximumVersion')) { + $isLessThanMax = ($installedVersion -le [System.Version] $extractedArguments['MaximumVersion']) - if ($extractedArguments.ContainsKey("MinimumVersion")) { + if ($extractedArguments.ContainsKey('MinimumVersion')) { $versionMatch = $versionMatch -and $isLessThanMax } else { @@ -534,16 +595,16 @@ Function Get-RightModule { # Case 5 - Both MinimumVersion and MaximumVersion are provided. It's covered by the above. # Do not return $false yet to allow the foreach to continue if (-not $versionMatch) { - Write-Verbose -Message ($localizedData.VersionMismatch -f $($Name), $($installedVersion)) + Write-Verbose -Message ($localizedData.VersionMismatch -f $Name, $installedVersion) $versionMatch = $false } } # Case 6 - Version matches but need to check if the module is from the right repository. if ($versionMatch) { - # a user does not provide Repository, we are good - if (-not $PSBoundParameters.ContainsKey("Repository")) { - Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") + # A user does not provide Repository, we are good + if (-not $PSBoundParameters.ContainsKey('Repository')) { + Write-Verbose -Message ($localizedData.ModuleFound -f "$Name $installedVersion") $returnVal += $m } else { @@ -551,7 +612,7 @@ Function Get-RightModule { $sourceName = Get-ModuleRepositoryName -Module $m if ($Repository -ieq $sourceName) { - Write-Verbose -Message ($localizedData.ModuleFound -f "$($Name) $($installedVersion)") + Write-Verbose -Message ($localizedData.ModuleFound -f "$Name $installedVersion") $returnVal += $m } else { @@ -560,33 +621,43 @@ Function Get-RightModule { } } } # foreach + return $returnVal } -Function Get-ModuleRepositoryName { - <# +<# .SYNOPSIS This is a helper function that returns the module's repository name. .PARAMETER Module Specifies the name of the PowerShell module. +#> +function Get-ModuleRepositoryName { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. #> - + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Object] $Module ) - # RepositorySourceLocation property is supported in PS V5 only. To work with the earlier PS version, we need to do a different way. - # PSGetModuleInfo.xml exists for any PS modules downloaded through PSModule provider. - $psGetModuleInfoFileName = "PSGetModuleInfo.xml" + <# + RepositorySourceLocation property is supported in PS V5 only. To work with the earlier + PowerShell version, we need to do a different way. PSGetModuleInfo.xml exists for any + PowerShell modules downloaded through PSModule provider. + #> + $psGetModuleInfoFileName = 'PSGetModuleInfo.xml' $psGetModuleInfoPath = Microsoft.PowerShell.Management\Join-Path -Path $Module.ModuleBase -ChildPath $psGetModuleInfoFileName - Write-Verbose -Message ($localizedData.FoundModulePath -f $($psGetModuleInfoPath)) + Write-Verbose -Message ($localizedData.FoundModulePath -f $psGetModuleInfoPath) if (Microsoft.PowerShell.Management\Test-path -Path $psGetModuleInfoPath) { $psGetModuleInfo = Microsoft.PowerShell.Utility\Import-Clixml -Path $psGetModuleInfoPath @@ -594,5 +665,3 @@ Function Get-ModuleRepositoryName { return $psGetModuleInfo.Repository } } - -Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource diff --git a/DSC/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 b/DSC/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 index 0eb5b1f7..c43e7661 100644 --- a/DSC/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 +++ b/DSC/DSCResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 @@ -11,26 +11,26 @@ # # culture = "en-US" ConvertFrom-StringData -StringData @' -###PSLOC - FailtoUninstall = Failed to uninstall the module '{0}'. Message: {1} - FailtoInstall = Failed to install the module '{0}'. Message: {1} + FailToUninstall = Failed to uninstall the module '{0}'. + FailToInstall = Failed to install the module '{0}'. InDesiredState = Resource '{0}' is in the desired state. NotInDesiredState = Resource '{0}' is not in the desired state. - ModuleFound = Module '{0}' found in the node. - ModuleNotFound = Module '{0}' not found in the node. - ModuleWithRightPropertyNotFound = Module '{0}' with the right version or other properties not found in the node. Message: {1} - ModuleNotFoundInRepository = Module '{0}' with the right version or other properties not found in the repository. Message: {1} - StartGetModule = Begin invoking Get-Module '{0}' - StartFindModule = Begin invoking Find-Module '{0}' + ModuleFound = Module '{0}' is found on the node. + ModuleNotFound = Module '{0}' is not found on the node. + ModuleWithRightPropertyNotFound = Module '{0}' with the right version or other properties not found in the node. + ModuleNotFoundInRepository = Module '{0}' with the right version or other properties not found in the repository. + StartGetModule = Begin invoking Get-Module '{0}'. + StartFindModule = Begin invoking Find-Module '{0}'. StartInstallModule = Begin invoking Install-Module '{0}' version '{1}' from '{2}' repository. - StartUnInstallModule = Begin invoking uninstall of the module '{0}' + StartUnInstallModule = Begin invoking Remove-Item to remove the module '{0}' from the file system. InstalledSuccess = Successfully installed the module '{0}' UnInstalledSuccess = Successfully uninstalled the module '{0}' VersionMismatch = The installed Module '{0}' has the version: '{1}' RepositoryMismatch = The installed Module '{0}' is from '{1}' repository. - FoundModulePath = Found the module path: '{0}' + FoundModulePath = Found the module path: '{0}'. MultipleModuleFound = Total: '{0}' modules found with the same name. Please use -RequiredVersion for filtering. Message: {1} - InstallationPolicyWarning = You are installing the module '{0}' from an untrusted repository' {1}'. Your current InstallationPolicy is '{2}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. - InstallationPolicyFailed = Failed in the installation policy. Your current InstallationPolicy is '{0}' and the repository is '{1}'. If you trust the repository, set the policy to "Trusted". "Untrusted" otherwise. -###PSLOC + InstallationPolicyWarning = You are installing the module '{0}' from an untrusted repository' {1}'. Your current InstallationPolicy is '{2}'. If you trust the repository, set the policy to "Trusted". + InstallationPolicyFailed = Failed in the installation policy. Your current InstallationPolicy is '{0}' and the repository is '{1}'. If you trust the repository, set the policy to "Trusted". + GetTargetResourceMessage = Getting the current state of the module '{0}'. + TestTargetResourceMessage = Determining if the module '{0}' is in the desired state. '@ diff --git a/DSC/Examples/.gitattributes b/DSC/Examples/.gitattributes new file mode 100644 index 00000000..92be83e2 --- /dev/null +++ b/DSC/Examples/.gitattributes @@ -0,0 +1 @@ +* text eol=crlf diff --git a/DSC/Examples/README.md b/DSC/Examples/README.md new file mode 100644 index 00000000..4db26804 --- /dev/null +++ b/DSC/Examples/README.md @@ -0,0 +1,7 @@ +# Examples + +## Resource examples + +These are the links to the examples for each individual resource. + +- [PSModule](Resources/PSModule) diff --git a/DSC/Examples/Resources/PSModule/1-PSModule_InstallModuleConfig.ps1 b/DSC/Examples/Resources/PSModule/1-PSModule_InstallModuleConfig.ps1 new file mode 100644 index 00000000..e8583e89 --- /dev/null +++ b/DSC/Examples/Resources/PSModule/1-PSModule_InstallModuleConfig.ps1 @@ -0,0 +1,73 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID 45d8677b-817f-4b2a-8d47-a802c4f758b1 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that installs a module. + + .DESCRIPTION + Configuration that installs a module. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER ModuleName + The name of the module to be downloaded and installed. + + .EXAMPLE + PSModule_InstallModuleConfig -ModuleName 'PSLogging' + + Compiles a configuration that downloads and installs the module 'PSLogging'. + + .EXAMPLE + $configurationParameters = @{ + ModuleName = 'PSLogging' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSModule_InstallModuleConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'PSLogging'. + + Replace the and with correct values. +#> +configuration PSModule_InstallModuleConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ModuleName + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSModule 'InstallModule' + { + Name = $ModuleName + } + } +} diff --git a/DSC/Examples/Resources/PSModule/2-PSModule_InstallModuleTrustedConfig.ps1 b/DSC/Examples/Resources/PSModule/2-PSModule_InstallModuleTrustedConfig.ps1 new file mode 100644 index 00000000..84b6635c --- /dev/null +++ b/DSC/Examples/Resources/PSModule/2-PSModule_InstallModuleTrustedConfig.ps1 @@ -0,0 +1,76 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID d16da19d-439a-4730-8e02-6928d6a8ed28 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that installs a module, and overrides the + current trust level for the package source. + + .DESCRIPTION + Configuration that installs a module, and overrides the + current trust level for the package source. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER ModuleName + The name of the module to be downloaded and installed. + + .EXAMPLE + PSModule_InstallModuleTrustedConfig -ModuleName 'PSLogging' + + Compiles a configuration that downloads and installs the module 'PSLogging'. + + .EXAMPLE + $configurationParameters = @{ + ModuleName = 'PSLogging' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSModule_InstallModuleTrustedConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'PSLogging'. + + Replace the and with correct values. +#> +configuration PSModule_InstallModuleTrustedConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ModuleName + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSModule 'InstallModuleAsTrusted' + { + Name = $ModuleName + InstallationPolicy = 'Trusted' + } + } +} diff --git a/DSC/Examples/Resources/PSModule/3-PSModule_InstallModuleAllowClobberConfig.ps1 b/DSC/Examples/Resources/PSModule/3-PSModule_InstallModuleAllowClobberConfig.ps1 new file mode 100644 index 00000000..44b4abab --- /dev/null +++ b/DSC/Examples/Resources/PSModule/3-PSModule_InstallModuleAllowClobberConfig.ps1 @@ -0,0 +1,74 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID 47eb256b-9c81-437d-9148-890bc94f15ed +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that installs a module and allows clobber. + + .DESCRIPTION + Configuration that installs a module and allows clobber. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER ModuleName + The name of the module to be downloaded and installed. + + .EXAMPLE + PSModule_InstallModuleAllowClobberConfig -ModuleName 'SqlServer' + + Compiles a configuration that downloads and installs the module 'SqlServer'. + + .EXAMPLE + $configurationParameters = @{ + ModuleName = 'SqlServer' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSModule_InstallModuleAllowClobberConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'SqlServer'. + + Replace the and with correct values. +#> +configuration PSModule_InstallModuleAllowClobberConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ModuleName + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSModule 'InstallModuleAndAllowClobber' + { + Name = $ModuleName + AllowClobber = $true + } + } +} diff --git a/DSC/Examples/Resources/PSModule/4-PSModule_InstallModuleSpecificVersionConfig.ps1 b/DSC/Examples/Resources/PSModule/4-PSModule_InstallModuleSpecificVersionConfig.ps1 new file mode 100644 index 00000000..67c3a244 --- /dev/null +++ b/DSC/Examples/Resources/PSModule/4-PSModule_InstallModuleSpecificVersionConfig.ps1 @@ -0,0 +1,83 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID cbed3f85-50cf-49ca-bde4-1b3ef0e33687 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that installs a module with a specific version. + + .DESCRIPTION + Configuration that installs a module with a specific version. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER ModuleName + The name of the module to be downloaded and installed. + + .PARAMETER RequiredVersion + The specific (required) version of the module to download and install. + + .EXAMPLE + PSModule_InstallModuleSpecificVersionConfig -ModuleName 'SqlServer' -RequiredVersion '21.1.18068' + + Compiles a configuration that downloads and installs the module 'SqlServer'. + + .EXAMPLE + $configurationParameters = @{ + ModuleName = 'SqlServer' + RequiredVersion = '21.1.18068' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSModule_InstallModuleSpecificVersionConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'SqlServer'. + + Replace the and with correct values. +#> +configuration PSModule_InstallModuleSpecificVersionConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ModuleName, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $RequiredVersion + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSModule 'InstallModuleAndAllowClobber' + { + Name = $ModuleName + RequiredVersion = $RequiredVersion + } + } +} diff --git a/DSC/Examples/Resources/PSModule/5-PSModule_InstallModuleWithinVersionRangeConfig.ps1 b/DSC/Examples/Resources/PSModule/5-PSModule_InstallModuleWithinVersionRangeConfig.ps1 new file mode 100644 index 00000000..65806691 --- /dev/null +++ b/DSC/Examples/Resources/PSModule/5-PSModule_InstallModuleWithinVersionRangeConfig.ps1 @@ -0,0 +1,93 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID b3a8515b-9164-4fc5-9df0-e883f6420a83 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that installs a module withing a specific version range. + + .DESCRIPTION + Configuration that installs a module withing a specific version range. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER ModuleName + The name of the module to be downloaded and installed. + + .PARAMETER MinimumVersion + The minimum version of the module to download and install. + + .PARAMETER MaximumVersion + The maximum version of the module to download and install. + + .EXAMPLE + PSModule_InstallModuleWithinVersionRangeConfig -ModuleName 'SqlServer' -MinimumVersion '21.0.17199' -MaximumVersion '21.1.18068' + + Compiles a configuration that downloads and installs the module 'SqlServer'. + + .EXAMPLE + $configurationParameters = @{ + ModuleName = 'SqlServer' + MinimumVersion = '21.0.17199' + MaximumVersion = '21.1.18068' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSModule_InstallModuleWithinVersionRangeConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'SqlServer'. + + Replace the and with correct values. +#> +configuration PSModule_InstallModuleWithinVersionRangeConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ModuleName, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $MinimumVersion, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $MaximumVersion + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSModule 'InstallModuleAndAllowClobber' + { + Name = $ModuleName + MinimumVersion = $MinimumVersion + MaximumVersion = $MaximumVersion + } + } +} diff --git a/DSC/Examples/Resources/PSModule/6-PSModule_UninstallModuleConfig.ps1 b/DSC/Examples/Resources/PSModule/6-PSModule_UninstallModuleConfig.ps1 new file mode 100644 index 00000000..b255a121 --- /dev/null +++ b/DSC/Examples/Resources/PSModule/6-PSModule_UninstallModuleConfig.ps1 @@ -0,0 +1,74 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID 83a844ed-4e23-427d-94c9-72bdcae0e1bb +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that uninstalls an installed module. + + .DESCRIPTION + Configuration that uninstalls an installed module. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER ModuleName + The name of the module to be uninstalled. + + .EXAMPLE + PSModule_UninstallModuleConfig -ModuleName 'PSLogging' + + Compiles a configuration that downloads and installs the module 'PSLogging'. + + .EXAMPLE + $configurationParameters = @{ + ModuleName = 'PSLogging' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSModule_UninstallModuleConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'PSLogging'. + + Replace the and with correct values. +#> +configuration PSModule_UninstallModuleConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ModuleName + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSModule 'InstallModule' + { + Ensure = 'Absent' + Name = $ModuleName + } + } +} diff --git a/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 b/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 index 36b8a1eb..c56fdefc 100644 --- a/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 +++ b/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 @@ -23,31 +23,31 @@ Import-Module -Name $dscResourcesFolderFilePath # Import Localization Strings $script:localizedData = Get-LocalizedData -ResourceName 'PowerShellGet.ResourceHelper' -ScriptRoot $PSScriptRoot -Function ExtractArguments { - <# +<# .SYNOPSIS This is a helper function that extract the parameters from a given table. .PARAMETER FunctionBoundParameters - Specifies the hashtable containing a set of parameters to be extracted. + Specifies the hash table containing a set of parameters to be extracted. .PARAMETER ArgumentNames Specifies a list of arguments you want to extract. - #> - +#> +function New-SplatParameterHashTable { [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.Collections.Hashtable] $FunctionBoundParameters, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String[]] $ArgumentNames ) - Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) + Write-Verbose -Message ($script:localizedData.CallingFunction -f $($MyInvocation.MyCommand)) $returnValue = @{} @@ -57,149 +57,101 @@ Function ExtractArguments { $returnValue.Add($arg, $FunctionBoundParameters[$arg]) } } - return $returnValue -} - -Function ThrowError { - <# - .SYNOPSIS - This is a helper function that throws an error. - - .PARAMETER ExceptionName - Specifies the type of errors, e.g. System.ArgumentException. - - .PARAMETER ExceptionMessage - Specifies the exception message. - - .PARAMETER ErrorId - Specifies an identifier of the error. - - .PARAMETER ErrorCategory - Specifies the error category, e.g., InvalidArgument defined in System.Management.Automation. - #> - [CmdletBinding()] - param - ( - [parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ExceptionName, - - [parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ExceptionMessage, - - [parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ErrorId, - - [parameter(Mandatory = $true)] - [ValidateNotNull()] - [System.Management.Automation.ErrorCategory] - $ErrorCategory - ) - - Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) - - $exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage; - $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, $ErrorId, $ErrorCategory, $null) - - throw $errorRecord + return $returnValue } -Function ValidateArgument { - <# +<# .SYNOPSIS - This is a helper function that validates the arguments. + This is a helper function that validate that a value is correct and used correctly. - .PARAMETER Argument - Specifies the argument to be validated. + .PARAMETER Value + Specifies the value to be validated. .PARAMETER Type Specifies the type of argument. - #> + .PARAMETER Type + Specifies the name of the provider. + + .OUTPUTS + None. Throws an error if the test fails. +#> +function Test-ParameterValue { [CmdletBinding()] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $Argument, + $Value, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Type, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ProviderName ) - Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) + Write-Verbose -Message ($script:localizedData.CallingFunction -f $($MyInvocation.MyCommand)) switch ($Type) { - "SourceUri" { + 'SourceUri' { # Checks whether given URI represents specific scheme # Most common schemes: file, http, https, ftp $scheme = @('http', 'https', 'file', 'ftp') - $newUri = $Argument -as [System.URI] - $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newuri.Scheme)) + $newUri = $Value -as [System.URI] + $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newUri.Scheme)) if ($returnValue -eq $false) { - ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` - -ErrorId "InValidUri" ` - -ErrorCategory InvalidArgument + $errorMessage = $script:localizedData.InValidUri -f $Value + New-InvalidArgumentException -ArgumentName $Type -Message $errorMessage } } - "DestinationPath" { - $returnValue = Test-Path -Path $Argument + + 'DestinationPath' { + $returnValue = Test-Path -Path $Value if ($returnValue -eq $false) { - ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.PathDoesNotExist -f $Argument)` - -ErrorId "PathDoesNotExist" ` - -ErrorCategory InvalidArgument + $errorMessage = $script:localizedData.PathDoesNotExist -f $Value + New-InvalidArgumentException -ArgumentName $Type -Message $errorMessage } } - "PackageSource" { - # Argument can be either the package source Name or source Uri. + + 'PackageSource' { + # Value can be either the package source Name or source Uri. # Check if the source is a Uri. - $uri = $Argument -as [System.URI] + $uri = $Value -as [System.URI] if ($uri -and $uri.AbsoluteURI) { # Check if it's a valid Uri. - ValidateArgument -Argument $Argument -Type "SourceUri" -ProviderName $ProviderName + Test-ParameterValue -Value $Value -Type 'SourceUri' -ProviderName $ProviderName } else { # Check if it's a registered package source name. - $source = PackageManagement\Get-PackageSource -Name $Argument -ProviderName $ProviderName -verbose -ErrorVariable ev + $source = PackageManagement\Get-PackageSource -Name $Value -ProviderName $ProviderName -ErrorVariable ev if ((-not $source) -or $ev) { # We do not need to throw error here as Get-PackageSource does already. - Write-Verbose -Message ($LocalizedData.SourceNotFound -f $source) + Write-Verbose -Message ($script:localizedData.SourceNotFound -f $source) } } } + default { - ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.UnexpectedArgument -f $Type)` - -ErrorId "UnexpectedArgument" ` - -ErrorCategory InvalidArgument + $errorMessage = $script:localizedData.UnexpectedArgument -f $Type + New-InvalidArgumentException -ArgumentName $Type -Message $errorMessage } } } -Function ValidateVersionArgument { - <# +<# .SYNOPSIS This is a helper function that does the version validation. @@ -211,22 +163,25 @@ Function ValidateVersionArgument { .PARAMETER MinimumVersion Provides the minimum version. - #> - +#> +function Test-VersionParameter { [CmdletBinding()] param ( + [Parameter()] [System.String] $RequiredVersion, + [Parameter()] [System.String] $MinimumVersion, + [Parameter()] [System.String] $MaximumVersion ) - Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.mycommand)) + Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.MyCommand)) $isValid = $false @@ -246,35 +201,34 @@ Function ValidateVersionArgument { # This is an invalid case. When RequiredVersion is provided, others are not allowed. so $isValid is false, which is already set in the init. if ($isValid -eq $false) { - ThrowError -ExceptionName "System.ArgumentException" ` - -ExceptionMessage ($LocalizedData.VersionError)` - -ErrorId "VersionError" ` - -ErrorCategory InvalidArgument + $errorMessage = $script:localizedData.VersionError + New-InvalidArgumentException ` + -ArgumentName 'RequiredVersion, MinimumVersion or MaximumVersion' ` + -Message $errorMessage } } -Function Get-InstallationPolicy { - <# +<# .SYNOPSIS - This is a helper function that retrives the InstallationPolicy from the given repository. + This is a helper function that retrieves the InstallationPolicy from the given repository. .PARAMETER RepositoryName Provides the repository Name. - #> - +#> +function Get-InstallationPolicy { [CmdletBinding()] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $RepositoryName ) - Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) + Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.MyCommand)) - $repositoryobj = PackageManagement\Get-PackageSource -Name $RepositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + $repositoryObject = PackageManagement\Get-PackageSource -Name $RepositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - if ($repositoryobj) { - return $repositoryobj.IsTrusted + if ($repositoryObject) { + return $repositoryObject.IsTrusted } } diff --git a/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 b/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 index 0a766936..e264d2f1 100644 --- a/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 +++ b/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 @@ -221,6 +221,7 @@ try { It 'Should return the same values as passed as parameters' { $getTargetResourceResult = Get-TargetResource -Name $mockModuleName $getTargetResourceResult.Name | Should -Be $mockModuleName + $getTargetResourceResult.Repository | Should -Be $mockRepositoryName Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 0 -Scope It @@ -231,7 +232,6 @@ try { $getTargetResourceResult = Get-TargetResource -Name $mockModuleName $getTargetResourceResult.Ensure | Should -Be 'Absent' - $getTargetResourceResult.Repository | Should -BeNullOrEmpty $getTargetResourceResult.Description | Should -BeNullOrEmpty $getTargetResourceResult.Guid | Should -BeNullOrEmpty $getTargetResourceResult.ModuleBase | Should -BeNullOrEmpty @@ -239,6 +239,12 @@ try { $getTargetResourceResult.Author | Should -BeNullOrEmpty $getTargetResourceResult.InstalledVersion | Should -BeNullOrEmpty $getTargetResourceResult.InstallationPolicy | Should -BeNullOrEmpty + $getTargetResourceResult.RequiredVersion | Should -BeNullOrEmpty + $getTargetResourceResult.MinimumVersion | Should -BeNullOrEmpty + $getTargetResourceResult.MaximumVersion | Should -BeNullOrEmpty + $getTargetResourceResult.Force | Should -Be $false + $getTargetResourceResult.AllowClobber | Should -Be $false + $getTargetResourceResult.SkipPublisherCheck | Should -Be $false Assert-MockCalled -CommandName Get-RightModule -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Get-ModuleRepositoryName -Exactly -Times 0 -Scope It @@ -291,7 +297,7 @@ try { Context 'When the Repository is ''PSGallery''' { BeforeAll { Mock -CommandName Get-InstallationPolicy -MockWith $mockGetInstallationPolicy_Trusted - Mock -CommandName ValidateArgument + Mock -CommandName Test-ParameterValue } It 'Should call the Install-Module with the correct parameters' { @@ -321,12 +327,14 @@ try { Context 'When the module name cannot be found' { BeforeAll { - Mock -CommandName Find-Module + Mock -CommandName Find-Module -MockWith { + throw 'Mocked error' + } } It 'Should throw the correct error' { { Set-TargetResource -Name $mockModuleName } | - Should -Throw ($localizedData.ModuleNotFoundInRepository -f $mockModuleName, '') + Should -Throw ($localizedData.ModuleNotFoundInRepository -f $mockModuleName) } } } @@ -355,7 +363,21 @@ try { It 'Should throw the correct error' { { Set-TargetResource -Name $mockModuleName -Ensure 'Absent' } | - Should -Throw ($localizedData.ModuleWithRightPropertyNotFound -f $mockModuleName, '') + Should -Throw ($localizedData.ModuleWithRightPropertyNotFound -f $mockModuleName) + } + } + + Context 'When a module cannot be removed' { + BeforeAll { + Mock -CommandName Get-RightModule -MockWith $mockGetRightModule_SingleModule + Mock -CommandName Remove-Item -MockWith { + throw 'Mock fail to remove module error' + } + } + + It 'Should throw the correct error' { + { Set-TargetResource -Name $mockModuleName -Ensure 'Absent' } | + Should -Throw ($localizedData.FailToUninstall -f $mockModuleName) } } } @@ -382,7 +404,7 @@ try { } } - It 'Should return the same values as passed as parameters' { + It 'Should return the state as $true' { $testTargetResourceResult = Test-TargetResource -Name $mockModuleName $testTargetResourceResult | Should -Be $true @@ -408,7 +430,7 @@ try { } } - It 'Should return the same values as passed as parameters' { + It 'Should return the state as $true' { $testTargetResourceResult = Test-TargetResource -Ensure 'Absent' -Name $mockModuleName $testTargetResourceResult | Should -Be $true @@ -436,7 +458,7 @@ try { } } - It 'Should return the same values as passed as parameters' { + It 'Should return the state as $false' { $testTargetResourceResult = Test-TargetResource -Name $mockModuleName $testTargetResourceResult | Should -Be $false @@ -462,7 +484,7 @@ try { } } - It 'Should return the same values as passed as parameters' { + It 'Should return the state as $false' { $testTargetResourceResult = Test-TargetResource -Ensure 'Absent' -Name $mockModuleName $testTargetResourceResult | Should -Be $false diff --git a/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 b/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 index aadb9c29..f4a5f255 100644 --- a/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 +++ b/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 @@ -17,7 +17,7 @@ Describe "$script:helperModuleName Unit Tests" { } InModuleScope $script:helperModuleName { - Describe 'ExtractArguments' { + Describe 'New-SplatParameterHashTable' { Context 'When specific parameters should be returned' { It 'Should return a hashtable with the correct values' { $mockPSBoundParameters = @{ @@ -27,7 +27,7 @@ Describe "$script:helperModuleName Unit Tests" { Property4 = '4' } - $extractArgumentsResult = ExtractArguments ` + $extractArgumentsResult = New-SplatParameterHashTable ` -FunctionBoundParameters $mockPSBoundParameters ` -ArgumentNames @('Property2', 'Property3') @@ -46,7 +46,7 @@ Describe "$script:helperModuleName Unit Tests" { Property1 = '1' } - $extractArgumentsResult = ExtractArguments ` + $extractArgumentsResult = New-SplatParameterHashTable ` -FunctionBoundParameters $mockPSBoundParameters ` -ArgumentNames @('Property2', 'Property3') @@ -60,7 +60,7 @@ Describe "$script:helperModuleName Unit Tests" { $mockPSBoundParameters = @{ } - $extractArgumentsResult = ExtractArguments ` + $extractArgumentsResult = New-SplatParameterHashTable ` -FunctionBoundParameters $mockPSBoundParameters ` -ArgumentNames @('Property2', 'Property3') @@ -70,101 +70,85 @@ Describe "$script:helperModuleName Unit Tests" { } } - Describe 'ThrowError' { - It 'Should throw the correct error' { - { - $mockedErrorMessage = 'mocked error' - $mockErrorId = 'MockedError' - $mockExceptionName = 'InvalidOperationException' - - ThrowError ` - -ExceptionName $mockExceptionName ` - -ExceptionMessage $mockedErrorMessage ` - -ErrorId $mockErrorId ` - -ErrorCategory 'InvalidOperation' - } | Should -Throw $mockedErrorMessage - } - } - - Describe 'ValidateArgument' { + Describe 'Test-ParameterValue' { BeforeAll { $mockProviderName = 'PowerShellGet' } - Context 'When passing a correct uri as ''Argument'' and type is ''SourceUri''' { + Context 'When passing a correct uri as ''Value'' and type is ''SourceUri''' { It 'Should not throw an error' { { - ValidateArgument ` - -Argument 'https://mocked.uri' ` + Test-ParameterValue ` + -Value 'https://mocked.uri' ` -Type 'SourceUri' ` -ProviderName $mockProviderName } | Should -Not -Throw } } - Context 'When passing an invalid uri as ''Argument'' and type is ''SourceUri''' { + Context 'When passing an invalid uri as ''Value'' and type is ''SourceUri''' { It 'Should throw the correct error' { - $mockArgument = 'mocked.uri' + $mockParameterName = 'mocked.uri' { - ValidateArgument ` - -Argument $mockArgument ` + Test-ParameterValue ` + -Value $mockParameterName ` -Type 'SourceUri' ` -ProviderName $mockProviderName - } | Should -Throw ($LocalizedData.InValidUri -f $mockArgument) + } | Should -Throw ($LocalizedData.InValidUri -f $mockParameterName) } } - Context 'When passing a correct path as ''Argument'' and type is ''DestinationPath''' { + Context 'When passing a correct path as ''Value'' and type is ''DestinationPath''' { It 'Should not throw an error' { { - ValidateArgument ` - -Argument 'TestDrive:\' ` + Test-ParameterValue ` + -Value 'TestDrive:\' ` -Type 'DestinationPath' ` -ProviderName $mockProviderName } | Should -Not -Throw } } - Context 'When passing an invalid path as ''Argument'' and type is ''DestinationPath''' { + Context 'When passing an invalid path as ''Value'' and type is ''DestinationPath''' { It 'Should throw the correct error' { - $mockArgument = 'TestDrive:\NonExistentPath' + $mockParameterName = 'TestDrive:\NonExistentPath' { - ValidateArgument ` - -Argument $mockArgument ` + Test-ParameterValue ` + -Value $mockParameterName ` -Type 'DestinationPath' ` -ProviderName $mockProviderName - } | Should -Throw ($LocalizedData.PathDoesNotExist -f $mockArgument) + } | Should -Throw ($LocalizedData.PathDoesNotExist -f $mockParameterName) } } - Context 'When passing a correct uri as ''Argument'' and type is ''PackageSource''' { + Context 'When passing a correct uri as ''Value'' and type is ''PackageSource''' { It 'Should not throw an error' { { - ValidateArgument ` - -Argument 'https://mocked.uri' ` + Test-ParameterValue ` + -Value 'https://mocked.uri' ` -Type 'PackageSource' ` -ProviderName $mockProviderName } | Should -Not -Throw } } - Context 'When passing an correct package source as ''Argument'' and type is ''PackageSource''' { + Context 'When passing an correct package source as ''Value'' and type is ''PackageSource''' { BeforeAll { - $mockArgument = 'PSGallery' + $mockParameterName = 'PSGallery' Mock -CommandName Get-PackageSource -MockWith { return New-Object -TypeName Object | - Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockArgument -PassThru | + Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockParameterName -PassThru | Add-Member -Name 'ProviderName' -MemberType NoteProperty -Value $mockProviderName -PassThru -Force } } It 'Should not throw an error' { { - ValidateArgument ` - -Argument $mockArgument ` + Test-ParameterValue ` + -Value $mockParameterName ` -Type 'PackageSource' ` -ProviderName $mockProviderName } | Should -Not -Throw @@ -175,15 +159,15 @@ Describe "$script:helperModuleName Unit Tests" { Context 'When passing type is ''PackageSource'' and passing a package source that does not exist' { BeforeAll { - $mockArgument = 'PSGallery' + $mockParameterName = 'PSGallery' Mock -CommandName Get-PackageSource } It 'Should not throw an error' { { - ValidateArgument ` - -Argument $mockArgument ` + Test-ParameterValue ` + -Value $mockParameterName ` -Type 'PackageSource' ` -ProviderName $mockProviderName } | Should -Not -Throw @@ -199,8 +183,8 @@ Describe "$script:helperModuleName Unit Tests" { It 'Should throw the correct error' { { - ValidateArgument ` - -Argument 'AnyArgument' ` + Test-ParameterValue ` + -Value 'AnyArgument' ` -Type $mockType ` -ProviderName $mockProviderName } | Should -Throw ($LocalizedData.UnexpectedArgument -f $mockType) @@ -208,23 +192,23 @@ Describe "$script:helperModuleName Unit Tests" { } } - Describe 'ValidateVersionArgument' { + Describe 'Test-VersionParameter' { Context 'When not passing in any parameters (using default values)' { It 'Should return true' { - ValidateVersionArgument | Should -BeTrue + Test-VersionParameter | Should -BeTrue } } Context 'When only ''RequiredVersion'' are passed' { It 'Should return true' { - ValidateVersionArgument -RequiredVersion '3.0.0.0' | Should -BeTrue + Test-VersionParameter -RequiredVersion '3.0.0.0' | Should -BeTrue } } Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { It 'Should throw the correct error' { { - ValidateVersionArgument ` + Test-VersionParameter ` -MinimumVersion '2.0.0.0' ` -MaximumVersion '1.0.0.0' } | Should -Throw $LocalizedData.VersionError @@ -234,7 +218,7 @@ Describe "$script:helperModuleName Unit Tests" { Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { It 'Should throw the correct error' { { - ValidateVersionArgument ` + Test-VersionParameter ` -MinimumVersion '2.0.0.0' ` -MaximumVersion '1.0.0.0' } | Should -Throw $LocalizedData.VersionError @@ -244,7 +228,7 @@ Describe "$script:helperModuleName Unit Tests" { Context 'When ''RequiredVersion'', ''MinimumVersion'', and ''MaximumVersion'' are passed' { It 'Should throw the correct error' { { - ValidateVersionArgument ` + Test-VersionParameter ` -RequiredVersion '3.0.0.0' ` -MinimumVersion '2.0.0.0' ` -MaximumVersion '1.0.0.0' diff --git a/appveyor.yml b/appveyor.yml index 95b052a1..1c9ad88f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -62,16 +62,22 @@ for: only: - TestCategory: DSC + environment: + gallery_api: + secure: 9ekJzfsPCDBkyLrfmov83XbbhZ6E2N3z+B/Io8NbDetbHc6hWS19zsDmy7t0Vvxv + install: - git clone https://github.com/PowerShell/DscResource.Tests %APPVEYOR_BUILD_FOLDER%\DSC\DscResource.Tests - ps: | Import-Module .\tools\build.psm1 Invoke-DscPowerShellGetAppVeyorInstallTask - test_script: - ps: Invoke-DscPowerShellGetAppVeyorTestTask + deploy_script: + - ps: Invoke-DscPowerShellGetAppVeyorDeployTask + # Upload the project along with TestResults as a zip archive on_finish: - ps: | diff --git a/tools/build.psm1 b/tools/build.psm1 index d74ff2fb..f9fdb897 100644 --- a/tools/build.psm1 +++ b/tools/build.psm1 @@ -474,6 +474,24 @@ function Set-AppVeyorLocationOriginalBuildFolder { Set-Location -Path $env:APPVEYOR_BUILD_FOLDER } +<# + .SYNOPSIS + This function removes any present PowerShellGet modules in $env:PSModulePath's. + + .NOTES + Used by other helper functions when testing DSC resources in AppVeyor. +#> +function Remove-ModulePowerShellGet { + [CmdletBinding()] + param() + + Remove-Module -Name PowerShellGet -Force + Get-Module -Name PowerShellGet -ListAvailable | ForEach-Object { + Remove-Item -Path (Split-Path -Path $_.Path -Parent) -Recurse -Force + } +} + + <# .SYNOPSIS This function runs all the necessary install steps to prepare the @@ -494,10 +512,7 @@ function Invoke-DscPowerShellGetAppVeyorInstallTask { Invoke-AppveyorInstallTask # Remove any present PowerShellGet modules so DSC integration tests don't see multiple modules. - Remove-Module -Name PowerShellGet -Force - Get-Module -Name PowerShellGet -ListAvailable | ForEach-Object { - Remove-Item -Path (Split-Path -Path $_.Path -Parent) -Recurse -Force - } + Remove-ModulePowerShellGet } catch { throw $_ @@ -542,3 +557,46 @@ function Invoke-DscPowerShellGetAppVeyorTestTask { Set-AppVeyorLocationOriginalBuildFolder } } + +<# + .SYNOPSIS + This function starts the deploy step for the DSC resources. The + deploy step only publishes the examples to the PowerShell Gallery + so they show up in the gallery part of Azure State Configuration. + + .NOTES + Publishes using the account 'dscresourcekit' which is owned by + PowerShell DSC Team (DSC Resource Kit). + Only runs on the master branch. +#> +function Invoke-DscPowerShellGetAppVeyorDeployTask { + [CmdletBinding()] + param() + + <# + Removes any present PowerShellGet modules so deployment + don't see multiple modules. + #> + Remove-ModulePowerShellGet + + <# + Removes the source folder so when the deploy step copies + the module folder it only see the resource in the 'dist' + folder created by the AppVeyor install task. + #> + Remove-Item -Path (Join-Path -Path $env:APPVEYOR_BUILD_FOLDER -ChildPath 'src') -Recurse -Force + + # Temporary change the build folder during testing of the DSC resources. + Set-AppVeyorLocationDscResourceModuleBuildFolder + + try { + Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" + Invoke-AppVeyorDeployTask -OptIn @('PublishExample') -ModuleRootPath (Split-Path -Path $env:APPVEYOR_BUILD_FOLDER -Parent) + } + catch { + throw $_ + } + finally { + Set-AppVeyorLocationOriginalBuildFolder + } +} From 1c677b8511cd15f6b5be282ce0b7e38220ae1a73 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 18 Feb 2019 10:07:07 +0100 Subject: [PATCH 17/21] PSRepository: New DSC resource (#426) --- CHANGELOG.md | 10 +- .../MSFT_PSRepository/MSFT_PSRepository.psm1 | 308 ++++++++ .../MSFT_PSRepository.schema.mfl | 18 + .../MSFT_PSRepository.schema.mof | 14 + .../en-US/MSFT_PSRepository.strings.psd1 | 22 + DSC/Examples/README.md | 1 + .../1-PSRepository_AddRepositoryConfig.ps1 | 78 +++ .../2-PSRepository_RemoveRepositoryConfig.ps1 | 74 ++ .../PowerShellGet.ResourceHelper.psm1 | 143 ++++ .../PowerShellGet.ResourceHelper.strings.psd1 | 9 +- .../Integration/MSFT_PSModule.config.ps1 | 73 +- .../MSFT_PSRepository.Integration.Tests.ps1 | 209 ++++++ .../Integration/MSFT_PSRepository.config.ps1 | 128 ++++ DSC/Tests/Unit/MSFT_PSRepository.Tests.ps1 | 452 ++++++++++++ .../PowerShellGet.ResourceHelper.Tests.ps1 | 655 +++++++++++++----- 15 files changed, 1942 insertions(+), 252 deletions(-) create mode 100644 DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.psm1 create mode 100644 DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl create mode 100644 DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof create mode 100644 DSC/DSCResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1 create mode 100644 DSC/Examples/Resources/PSRepository/1-PSRepository_AddRepositoryConfig.ps1 create mode 100644 DSC/Examples/Resources/PSRepository/2-PSRepository_RemoveRepositoryConfig.ps1 create mode 100644 DSC/Tests/Integration/MSFT_PSRepository.Integration.Tests.ps1 create mode 100644 DSC/Tests/Integration/MSFT_PSRepository.config.ps1 create mode 100644 DSC/Tests/Unit/MSFT_PSRepository.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index d658af92..38eb9d11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - Made the list entries in the CHANGELOG.md to use dash `-` throughout to be consequent (before there was a mix of dashes and asterisk). - Update the AppVeyor CI test pipeline with a new job to run tests for - the DSC resources, primarily for the resource `PSModule`. + the DSC resources. The new job uses the test framework used for the DSC Resource Kit, the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) repository. - Update .gitignore to ignore the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) @@ -41,9 +41,9 @@ - Refactored the Get-TargetResource to return the correct hash table when the current state is absent. - Added new examples. -- Changed the AppVeyor CI build pipeline so it added the DSC resource - `PSModule` and dependent helper modules (the `Modules` folder) to the - AppVeyor artifact. +- Changed the AppVeyor CI build pipeline so it adds the DSC resources + and dependent helper modules (the `Modules` folder) to the AppVeyor + artifact. - Added the `.MetaTestOptIn.json` file to opt-in for a lot of common test in the DscResource.Tests test framework that tests the DSC resources. - The examples under the folder `DSC/Examples` will be [published to PowerShell Gallery](https://github.com/PowerShell/DscResource.Tests#publish-examples-to-powershell-gallery) @@ -57,7 +57,7 @@ - In the file `appveyor.yml` the PowerShell Gallery API key was added for the account 'dscresourcekit', which can only be decrypted using the PowerShell AppVeyor account. - +- Added DSC resource PSRepository. ## 2.0.4 diff --git a/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.psm1 b/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.psm1 new file mode 100644 index 00000000..4bccac95 --- /dev/null +++ b/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.psm1 @@ -0,0 +1,308 @@ +$resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + +# Import localization helper functions. +$helperName = 'PowerShellGet.LocalizationHelper' +$dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" +Import-Module -Name $dscResourcesFolderFilePath + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_PSRepository' -ScriptRoot $PSScriptRoot + +# Import resource helper functions. +$helperName = 'PowerShellGet.ResourceHelper' +$dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" +Import-Module -Name $dscResourcesFolderFilePath + +<# + .SYNOPSIS + Returns the current state of the repository. + + .PARAMETER Name + Specifies the name of the repository to manage. +#> +function Get-TargetResource { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + $returnValue = @{ + Ensure = 'Absent' + Name = $Name + SourceLocation = $null + ScriptSourceLocation = $null + PublishLocation = $null + ScriptPublishLocation = $null + InstallationPolicy = $null + PackageManagementProvider = $null + Trusted = $false + Registered = $false + } + + Write-Verbose -Message ($localizedData.GetTargetResourceMessage -f $Name) + + $repository = Get-PSRepository -Name $Name -ErrorAction 'SilentlyContinue' + + if ($repository) { + $returnValue.Ensure = 'Present' + $returnValue.SourceLocation = $repository.SourceLocation + $returnValue.ScriptSourceLocation = $repository.ScriptSourceLocation + $returnValue.PublishLocation = $repository.PublishLocation + $returnValue.ScriptPublishLocation = $repository.ScriptPublishLocation + $returnValue.InstallationPolicy = $repository.InstallationPolicy + $returnValue.PackageManagementProvider = $repository.PackageManagementProvider + $returnValue.Trusted = $repository.Trusted + $returnValue.Registered = $repository.Registered + } + else { + Write-Verbose -Message ($localizedData.RepositoryNotFound -f $Name) + } + + return $returnValue +} + +<# + .SYNOPSIS + Determines if the repository is in the desired state. + + .PARAMETER Ensure + If the repository should be present or absent on the server + being configured. Default values is 'Present'. + + .PARAMETER Name + Specifies the name of the repository to manage. + + .PARAMETER SourceLocation + Specifies the URI for discovering and installing modules from + this repository. A URI can be a NuGet server feed, HTTP, HTTPS, + FTP or file location. + + .PARAMETER ScriptSourceLocation + Specifies the URI for the script source location. + + .PARAMETER PublishLocation + Specifies the URI of the publish location. For example, for + NuGet-based repositories, the publish location is similar + to http://someNuGetUrl.com/api/v2/Packages. + + .PARAMETER ScriptPublishLocation + Specifies the URI for the script publish location. + + .PARAMETER InstallationPolicy + Specifies the installation policy. Valid values are 'Trusted' + or 'Untrusted'. The default value is 'Untrusted'. + + .PARAMETER PackageManagementProvider + Specifies a OneGet package provider. Default value is 'NuGet'. +#> +function Test-TargetResource { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.String] + $SourceLocation, + + [Parameter()] + [System.String] + $ScriptSourceLocation, + + [Parameter()] + [System.String] + $PublishLocation, + + [Parameter()] + [System.String] + $ScriptPublishLocation, + + [Parameter()] + [ValidateSet('Trusted', 'Untrusted')] + [System.String] + $InstallationPolicy = 'Untrusted', + + [Parameter()] + [System.String] + $PackageManagementProvider = 'NuGet' + ) + + Write-Verbose -Message ($localizedData.TestTargetResourceMessage -f $Name) + + $returnValue = $false + + $getTargetResourceResult = Get-TargetResource -Name $Name + + if ($Ensure -eq $getTargetResourceResult.Ensure) { + if ($getTargetResourceResult.Ensure -eq 'Present' ) { + $returnValue = Test-DscParameterState ` + -CurrentValues $getTargetResourceResult ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck @( + 'SourceLocation' + 'ScriptSourceLocation' + 'PublishLocation' + 'ScriptPublishLocation' + 'InstallationPolicy' + 'PackageManagementProvider' + ) + } + else { + $returnValue = $true + } + } + + if ($returnValue) { + Write-Verbose -Message ($localizedData.InDesiredState -f $Name) + } + else { + Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name) + } + + return $returnValue +} + +<# + .SYNOPSIS + Creates, removes or updates the repository. + + .PARAMETER Ensure + If the repository should be present or absent on the server + being configured. Default values is 'Present'. + + .PARAMETER Name + Specifies the name of the repository to manage. + + .PARAMETER SourceLocation + Specifies the URI for discovering and installing modules from + this repository. A URI can be a NuGet server feed, HTTP, HTTPS, + FTP or file location. + + .PARAMETER ScriptSourceLocation + Specifies the URI for the script source location. + + .PARAMETER PublishLocation + Specifies the URI of the publish location. For example, for + NuGet-based repositories, the publish location is similar + to http://someNuGetUrl.com/api/v2/Packages. + + .PARAMETER ScriptPublishLocation + Specifies the URI for the script publish location. + + .PARAMETER InstallationPolicy + Specifies the installation policy. Valid values are 'Trusted' + or 'Untrusted'. The default value is 'Untrusted'. + + .PARAMETER PackageManagementProvider + Specifies a OneGet package provider. Default value is 'NuGet'. +#> +function Set-TargetResource { + <# + These suppressions are added because this repository have other Visual Studio Code workspace + settings than those in DscResource.Tests DSC test framework. + Only those suppression that contradict this repository guideline is added here. + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.String] + $SourceLocation, + + [Parameter()] + [System.String] + $ScriptSourceLocation, + + [Parameter()] + [System.String] + $PublishLocation, + + [Parameter()] + [System.String] + $ScriptPublishLocation, + + [Parameter()] + [ValidateSet('Trusted', 'Untrusted')] + [System.String] + $InstallationPolicy = 'Untrusted', + + [Parameter()] + [System.String] + $PackageManagementProvider = 'NuGet' + ) + + $getTargetResourceResult = Get-TargetResource -Name $Name + + # Determine if the repository should be present or absent. + if ($Ensure -eq 'Present') { + $repositoryParameters = New-SplatParameterHashTable ` + -FunctionBoundParameters $PSBoundParameters ` + -ArgumentNames @( + 'Name' + 'SourceLocation' + 'ScriptSourceLocation' + 'PublishLocation' + 'ScriptPublishLocation' + 'InstallationPolicy' + 'PackageManagementProvider' + ) + + # Determine if the repository is already present. + if ($getTargetResourceResult.Ensure -eq 'Present') { + Write-Verbose -Message ($localizedData.RepositoryExist -f $Name) + + # Repository exist, update the properties. + Set-PSRepository @repositoryParameters -ErrorAction 'Stop' + } + else { + Write-Verbose -Message ($localizedData.RepositoryDoesNotExist -f $Name) + + # Repository did not exist, create the repository. + Register-PSRepository @repositoryParameters -ErrorAction 'Stop' + } + } + else { + if ($getTargetResourceResult.Ensure -eq 'Present') { + Write-Verbose -Message ($localizedData.RemoveExistingRepository -f $Name) + + # Repository did exist, remove the repository. + Unregister-PSRepository -Name $Name -ErrorAction 'Stop' + } + } +} diff --git a/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl b/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl new file mode 100644 index 00000000..4e3610b7 --- /dev/null +++ b/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl @@ -0,0 +1,18 @@ +#pragma namespace("\\\\.\\root\\default") +instance of __namespace{ name="MS_409";}; +#pragma namespace("\\\\.\\root\\default\\MS_409") + +[AMENDMENT, LOCALE("MS_409")] +class MSFT_PSModule : OMI_BaseResource +{ + [Key, Description("Specifies the name of the repository to manage.") : Amended] String Name; + [Description("If the repository should be present or absent on the server being configured. Default values is 'Present'.") : Amended] String Ensure; + [Description("Specifies the URI for discovering and installing modules from this repository. A URI can be a NuGet server feed, HTTP, HTTPS, FTP or file location.") : Amended] String SourceLocation; + [Description("Specifies the URI for the script source location.") : Amended] String ScriptSourceLocation; + [Description("Specifies the URI of the publish location. For example, for NuGet-based repositories, the publish location is similar to http://someNuGetUrl.com/api/v2/Packages.") : Amended] String PublishLocation; + [Description("Specifies the URI for the script publish location.") : Amended] String ScriptPublishLocation; + [Description("Specifies the installation policy. Valid values are 'Trusted' or 'Untrusted'. The default value is 'Untrusted'.") : Amended] String InstallationPolicy; + [Description("Specifies a OneGet package provider. Default value is 'NuGet'.") : Amended] String PackageManagementProvider; + [Description("Specifies if the repository is trusted.") : Amended] Boolean Trusted; + [Description("Specifies if the repository is registered.") : Amended] Boolean Registered; +}; diff --git a/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof b/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof new file mode 100644 index 00000000..e94b7d0d --- /dev/null +++ b/DSC/DSCResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof @@ -0,0 +1,14 @@ +[ClassVersion("1.0.0.0"),FriendlyName("PSRepository")] +class MSFT_PSRepository : OMI_BaseResource +{ + [Key] String Name; + [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; + [Write] String SourceLocation; + [Write] String ScriptSourceLocation; + [Write] String PublishLocation; + [Write] String ScriptPublishLocation; + [Write, ValueMap{"Trusted","Untrusted"}, Values{"Trusted","Untrusted"}] String InstallationPolicy; + [Write] String PackageManagementProvider; + [Read] Boolean Trusted; + [Read] Boolean Registered; +}; diff --git a/DSC/DSCResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1 b/DSC/DSCResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1 new file mode 100644 index 00000000..81c7c755 --- /dev/null +++ b/DSC/DSCResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1 @@ -0,0 +1,22 @@ +# +# Copyright (c) Microsoft Corporation. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# culture = "en-US" +ConvertFrom-StringData -StringData @' + GetTargetResourceMessage = Return the current state of the repository '{0}'. + RepositoryNotFound = The repository '{0}' was not found. + TestTargetResourceMessage = Determining if the repository '{0}' is in the desired state. + InDesiredState = Repository '{0}' is in the desired state. + NotInDesiredState = Repository '{0}' is not in the desired state. + RepositoryExist = Updating the properties of the repository '{0}'. + RepositoryDoesNotExist = Creating the repository '{0}'. + RemoveExistingRepository = Removing the repository '{0}'. +'@ diff --git a/DSC/Examples/README.md b/DSC/Examples/README.md index 4db26804..a027a549 100644 --- a/DSC/Examples/README.md +++ b/DSC/Examples/README.md @@ -5,3 +5,4 @@ These are the links to the examples for each individual resource. - [PSModule](Resources/PSModule) +- [PSRepository](Resources/PSRepository) diff --git a/DSC/Examples/Resources/PSRepository/1-PSRepository_AddRepositoryConfig.ps1 b/DSC/Examples/Resources/PSRepository/1-PSRepository_AddRepositoryConfig.ps1 new file mode 100644 index 00000000..9a22a4f1 --- /dev/null +++ b/DSC/Examples/Resources/PSRepository/1-PSRepository_AddRepositoryConfig.ps1 @@ -0,0 +1,78 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID a1f8ee59-31b6-49be-9175-f7a49b5e03f1 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that installs a module. + + .DESCRIPTION + Configuration that installs a module. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER RepositoryName + The name of the repository that will be added. + + .EXAMPLE + PSRepository_AddRepositoryConfig -RepositoryName 'PSTestGallery' + + Compiles a configuration that downloads and installs the module 'PSLogging'. + + .EXAMPLE + $configurationParameters = @{ + RepositoryName = 'PSTestGallery' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSRepository_AddRepositoryConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'PSLogging'. + + Replace the and with correct values. +#> +configuration PSRepository_AddRepositoryConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $RepositoryName + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSRepository 'AddRepository' + { + Name = $RepositoryName + SourceLocation = 'https://www.poshtestgallery.com/api/v2/' + PublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' + ScriptSourceLocation = 'https://www.poshtestgallery.com/api/v2/items/psscript/' + ScriptPublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' + InstallationPolicy = 'Trusted' + } + } +} diff --git a/DSC/Examples/Resources/PSRepository/2-PSRepository_RemoveRepositoryConfig.ps1 b/DSC/Examples/Resources/PSRepository/2-PSRepository_RemoveRepositoryConfig.ps1 new file mode 100644 index 00000000..3dc9abfd --- /dev/null +++ b/DSC/Examples/Resources/PSRepository/2-PSRepository_RemoveRepositoryConfig.ps1 @@ -0,0 +1,74 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID c9a8b46f-12a6-46e1-8c6b-946ac9995aad +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/PowerShellGet/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/PowerShellGet +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module PowerShellGet + +<# + .SYNOPSIS + Configuration that installs a module. + + .DESCRIPTION + Configuration that installs a module. + + .PARAMETER NodeName + The names of one or more nodes to compile a configuration for. + Defaults to 'localhost'. + + .PARAMETER RepositoryName + The name of the repository that will be added. + + .EXAMPLE + PSRepository_RemoveRepositoryConfig -RepositoryName 'PSTestGallery' + + Compiles a configuration that downloads and installs the module 'PSLogging'. + + .EXAMPLE + $configurationParameters = @{ + RepositoryName = 'PSTestGallery' + } + Start-AzureRmAutomationDscCompilationJob -ResourceGroupName '' -AutomationAccountName '' -ConfigurationName 'PSRepository_RemoveRepositoryConfig' -Parameters $configurationParameters + + Compiles a configuration in Azure Automation that downloads and installs + the module 'PSLogging'. + + Replace the and with correct values. +#> +configuration PSRepository_RemoveRepositoryConfig +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $RepositoryName + ) + + Import-DscResource -ModuleName 'PowerShellGet' + + Node $nodeName + { + PSRepository 'AddRepository' + { + Ensure = 'Absent' + Name = $RepositoryName + } + } +} diff --git a/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 b/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 index c56fdefc..e79eaf34 100644 --- a/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 +++ b/DSC/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 @@ -232,3 +232,146 @@ function Get-InstallationPolicy { return $repositoryObject.IsTrusted } } + +<# + .SYNOPSIS + This method is used to compare current and desired values for any DSC resource. + + .PARAMETER CurrentValues + This is hash table of the current values that are applied to the resource. + + .PARAMETER DesiredValues + This is a PSBoundParametersDictionary of the desired values for the resource. + + .PARAMETER ValuesToCheck + This is a list of which properties in the desired values list should be checked. + If this is empty then all values in DesiredValues are checked. +#> +function Test-DscParameterState { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [System.Object] + $DesiredValues, + + [Parameter()] + [System.Array] + $ValuesToCheck + ) + + $returnValue = $true + + if (($DesiredValues.GetType().Name -ne 'HashTable') ` + -and ($DesiredValues.GetType().Name -ne 'CimInstance') ` + -and ($DesiredValues.GetType().Name -ne 'PSBoundParametersDictionary')) { + $errorMessage = $script:localizedData.PropertyTypeInvalidForDesiredValues -f $($DesiredValues.GetType().Name) + New-InvalidArgumentException -ArgumentName 'DesiredValues' -Message $errorMessage + } + + if (($DesiredValues.GetType().Name -eq 'CimInstance') -and ($null -eq $ValuesToCheck)) { + $errorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck + New-InvalidArgumentException -ArgumentName 'ValuesToCheck' -Message $errorMessage + } + + if (($null -eq $ValuesToCheck) -or ($ValuesToCheck.Count -lt 1)) { + $keyList = $DesiredValues.Keys + } + else { + $keyList = $ValuesToCheck + } + + $keyList | ForEach-Object -Process { + if (($_ -ne 'Verbose')) { + if (($CurrentValues.ContainsKey($_) -eq $false) ` + -or ($CurrentValues.$_ -ne $DesiredValues.$_) ` + -or (($DesiredValues.GetType().Name -ne 'CimInstance' -and $DesiredValues.ContainsKey($_) -eq $true) -and ($null -ne $DesiredValues.$_ -and $DesiredValues.$_.GetType().IsArray))) { + if ($DesiredValues.GetType().Name -eq 'HashTable' -or ` + $DesiredValues.GetType().Name -eq 'PSBoundParametersDictionary') { + $checkDesiredValue = $DesiredValues.ContainsKey($_) + } + else { + # If DesiredValue is a CimInstance. + $checkDesiredValue = $false + if (([System.Boolean]($DesiredValues.PSObject.Properties.Name -contains $_)) -eq $true) { + if ($null -ne $DesiredValues.$_) { + $checkDesiredValue = $true + } + } + } + + if ($checkDesiredValue) { + $desiredType = $DesiredValues.$_.GetType() + $fieldName = $_ + if ($desiredType.IsArray -eq $true) { + if (($CurrentValues.ContainsKey($fieldName) -eq $false) ` + -or ($null -eq $CurrentValues.$fieldName)) { + Write-Verbose -Message ($script:localizedData.PropertyValidationError -f $fieldName) -Verbose + + $returnValue = $false + } + else { + $arrayCompare = Compare-Object -ReferenceObject $CurrentValues.$fieldName ` + -DifferenceObject $DesiredValues.$fieldName + if ($null -ne $arrayCompare) { + Write-Verbose -Message ($script:localizedData.PropertiesDoesNotMatch -f $fieldName) -Verbose + + $arrayCompare | ForEach-Object -Process { + Write-Verbose -Message ($script:localizedData.PropertyThatDoesNotMatch -f $_.InputObject, $_.SideIndicator) -Verbose + } + + $returnValue = $false + } + } + } + else { + switch ($desiredType.Name) { + 'String' { + if (-not [System.String]::IsNullOrEmpty($CurrentValues.$fieldName) -or ` + -not [System.String]::IsNullOrEmpty($DesiredValues.$fieldName)) { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + 'Int32' { + if (-not ($DesiredValues.$fieldName -eq 0) -or ` + -not ($null -eq $CurrentValues.$fieldName)) { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + { $_ -eq 'Int16' -or $_ -eq 'UInt16'} { + if (-not ($DesiredValues.$fieldName -eq 0) -or ` + -not ($null -eq $CurrentValues.$fieldName)) { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + default { + Write-Warning -Message ($script:localizedData.UnableToCompareProperty ` + -f $fieldName, $desiredType.Name) + + $returnValue = $false + } + } + } + } + } + } + } + + return $returnValue +} diff --git a/DSC/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 b/DSC/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 index ec703201..8e3522fd 100644 --- a/DSC/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 +++ b/DSC/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 @@ -17,6 +17,13 @@ ConvertFrom-StringData -StringData @' VersionError = MinimumVersion should be less than the MaximumVersion. The MinimumVersion or MaximumVersion cannot be used with the RequiredVersion in the same command. UnexpectedArgument = Unexpected argument type: '{0}'. SourceNotFound = Source '{0}' not found. Please make sure you register it. - CallingFunction = "Call a function '{0}'". + CallingFunction = Calling function '{0}'. + PropertyTypeInvalidForDesiredValues = Property 'DesiredValues' must be either a [System.Collections.Hashtable], [CimInstance] or [PSBoundParametersDictionary]. The type detected was {0}. + PropertyTypeInvalidForValuesToCheck = If 'DesiredValues' is a CimInstance, then property 'ValuesToCheck' must contain a value. + PropertyValidationError = Expected to find an array value for property {0} in the current values, but it was either not present or was null. This has caused the test method to return false. + PropertiesDoesNotMatch = Found an array for property {0} in the current values, but this array does not match the desired state. Details of the changes are below. + PropertyThatDoesNotMatch = {0} - {1} + ValueOfTypeDoesNotMatch = {0} value for property {1} does not match. Current state is '{2}' and desired state is '{3}'. + UnableToCompareProperty = Unable to compare property {0} as the type {1} is not handled by the Test-SQLDSCParameterState cmdlet. ###PSLOC '@ diff --git a/DSC/Tests/Integration/MSFT_PSModule.config.ps1 b/DSC/Tests/Integration/MSFT_PSModule.config.ps1 index e667268d..d2ef24a9 100644 --- a/DSC/Tests/Integration/MSFT_PSModule.config.ps1 +++ b/DSC/Tests/Integration/MSFT_PSModule.config.ps1 @@ -32,47 +32,22 @@ else <# .SYNOPSIS - Changes the package source 'PSGallery' to not trusted. + Changes the repository (package source) 'PSGallery' to not trusted. .NOTES - Since the module is installed by SYSTEM as default, the package - source 'PSGallery' must be trusted for SYSTEM for some of the - tests. + Since the module is installed by SYSTEM as default this is done in + case the PSGallery is already trusted for SYSTEM. #> Configuration MSFT_PSModule_SetPackageSourceAsNotTrusted_Config { - Import-DscResource -ModuleName PSDscResources + Import-DscResource -ModuleName 'PowerShellGet' node $AllNodes.NodeName { - Script 'TrustPackageSourcePSGallery' + PSRepository 'Integration_Test' { - SetScript = { - Write-Verbose -Message 'Setting package source ''PSGallery'' as trusted' - Set-PackageSource -Name 'PSGallery' -Trusted:$false - } - - TestScript = { - Write-Verbose -Message 'Test if the package source ''PSGallery'' is trusted.' - - <# - This takes the string of the $GetScript parameter and creates - a new script block (during runtime in the resource) and then - runs that script block. - #> - $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - - return $getScriptResult.Result -eq $false - } - - GetScript = { - Write-Verbose -Message 'Return the current state of package source ''PSGallery''.' - $trusted = (Get-PackageSource -Name 'PSGallery').IsTrusted - - return @{ - Result = $trusted - } - } + Name = 'PSGallery' + InstallationPolicy = 'Untrusted' } } } @@ -118,7 +93,7 @@ Configuration MSFT_PSModule_UninstallModule1_Config <# .SYNOPSIS - Changes the package source 'PSGallery' to trusted. + Changes the repository (package source) 'PSGallery' to trusted. .NOTES Since the module is installed by SYSTEM as default, the package @@ -127,38 +102,14 @@ Configuration MSFT_PSModule_UninstallModule1_Config #> Configuration MSFT_PSModule_SetPackageSourceAsTrusted_Config { - Import-DscResource -ModuleName PSDscResources + Import-DscResource -ModuleName 'PowerShellGet' node $AllNodes.NodeName { - Script 'TrustPackageSourcePSGallery' + PSRepository 'Integration_Test' { - SetScript = { - Write-Verbose -Message 'Setting package source ''PSGallery'' as trusted' - Set-PackageSource -Name 'PSGallery' -Trusted - } - - TestScript = { - Write-Verbose -Message 'Test if the package source ''PSGallery'' is trusted.' - - <# - This takes the string of the $GetScript parameter and creates - a new script block (during runtime in the resource) and then - runs that script block. - #> - $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - - return $getScriptResult.Result -eq $true - } - - GetScript = { - Write-Verbose -Message 'Return the current state of package source ''PSGallery''.' - $trusted = (Get-PackageSource -Name 'PSGallery').IsTrusted - - return @{ - Result = $trusted - } - } + Name = 'PSGallery' + InstallationPolicy = 'Trusted' } } } diff --git a/DSC/Tests/Integration/MSFT_PSRepository.Integration.Tests.ps1 b/DSC/Tests/Integration/MSFT_PSRepository.Integration.Tests.ps1 new file mode 100644 index 00000000..95c63cc1 --- /dev/null +++ b/DSC/Tests/Integration/MSFT_PSRepository.Integration.Tests.ps1 @@ -0,0 +1,209 @@ +<# + .SYNOPSIS + Integration tests for DSC resource PSModule. + + .NOTES + The header and footer was removed that is usually part of the + DscResource.Tests integration test template. + The header was adding the project folder as a PSModulePath which + collide with the PowerShellGet test framework that copies the + module to the regular PowerShell Module folder. +#> + +$script:dscResourceFriendlyName = 'PSRepository' +$script:dcsResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region Integration Tests +$configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" +. $configurationFile + +Describe "$($script:dcsResourceName)_Integration" { + $configurationName = "$($script:dcsResourceName)_AddRepository_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Name + $resourceCurrentState.SourceLocation | Should -Be $ConfigurationData.AllNodes.TestSourceLocation + $resourceCurrentState.PublishLocation | Should -Be $ConfigurationData.AllNodes.TestPublishLocation + $resourceCurrentState.ScriptSourceLocation | Should -Be $ConfigurationData.AllNodes.TestScriptSourceLocation + $resourceCurrentState.ScriptPublishLocation | Should -Be $ConfigurationData.AllNodes.TestScriptPublishLocation + $resourceCurrentState.InstallationPolicy | Should -Be 'Trusted' + $resourceCurrentState.PackageManagementProvider | Should -Be 'NuGet' + $resourceCurrentState.Trusted | Should -Be $true + $resourceCurrentState.Registered | Should -Be $true + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_InstallTestModule_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } + + $configurationName = "$($script:dcsResourceName)_ChangeRepository_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Name + $resourceCurrentState.SourceLocation | Should -Be $ConfigurationData.AllNodes.SourceLocation + $resourceCurrentState.PublishLocation | Should -Be $ConfigurationData.AllNodes.PublishLocation + $resourceCurrentState.ScriptSourceLocation | Should -Be $ConfigurationData.AllNodes.ScriptSourceLocation + $resourceCurrentState.ScriptPublishLocation | Should -Be $ConfigurationData.AllNodes.ScriptPublishLocation + $resourceCurrentState.InstallationPolicy | Should -Be 'Untrusted' + $resourceCurrentState.PackageManagementProvider | Should -Be $ConfigurationData.AllNodes.PackageManagementProvider + $resourceCurrentState.Trusted | Should -Be $false + $resourceCurrentState.Registered | Should -Be $true + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } + + $configurationName = "$($script:dcsResourceName)_RemoveRepository_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Name + $resourceCurrentState.SourceLocation | Should -BeNullOrEmpty + $resourceCurrentState.PublishLocation | Should -BeNullOrEmpty + $resourceCurrentState.ScriptSourceLocation | Should -BeNullOrEmpty + $resourceCurrentState.ScriptPublishLocation | Should -BeNullOrEmpty + $resourceCurrentState.InstallationPolicy | Should -BeNullOrEmpty + $resourceCurrentState.PackageManagementProvider | Should -BeNullOrEmpty + $resourceCurrentState.Trusted | Should -Be $false + $resourceCurrentState.Registered | Should -Be $false + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } + } +} +#endregion diff --git a/DSC/Tests/Integration/MSFT_PSRepository.config.ps1 b/DSC/Tests/Integration/MSFT_PSRepository.config.ps1 new file mode 100644 index 00000000..de969bcb --- /dev/null +++ b/DSC/Tests/Integration/MSFT_PSRepository.config.ps1 @@ -0,0 +1,128 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + <# + Allows reading the configuration data from a JSON file + for real testing scenarios outside of the CI. + #> + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + + Name = 'PSTestGallery' + + TestSourceLocation = 'https://www.poshtestgallery.com/api/v2/' + TestPublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' + TestScriptSourceLocation = 'https://www.poshtestgallery.com/api/v2/items/psscript/' + TestScriptPublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' + + # Using these URI's to get a stable and accessible site, not to test a real scenario. + SourceLocation = 'https://www.nuget.org/api/v2' + PublishLocation = 'https://www.nuget.org/api/v2/package' + ScriptSourceLocation = 'https://www.nuget.org/api/v2/items/psscript/' + ScriptPublishLocation = 'https://www.nuget.org/api/v2/package' + + <# + Currently there are no default package management providers + that supports the feature 'supports-powershell-modules', so + it is not possible to test switching to another provider. + Note: PowerShellGet provider is not + #> + PackageManagementProvider = 'NuGet' + + TestModuleName = 'ContosoServer' + } + ) + } +} + +<# + .SYNOPSIS + Adds a repository. +#> +Configuration MSFT_PSRepository_AddRepository_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSRepository 'Integration_Test' + { + Name = $Node.Name + SourceLocation = $Node.TestSourceLocation + PublishLocation = $Node.TestPublishLocation + ScriptSourceLocation = $Node.TestScriptSourceLocation + ScriptPublishLocation = $Node.TestScriptPublishLocation + InstallationPolicy = 'Trusted' + } + } +} + +<# + .SYNOPSIS + Installs a module with default parameters from the new repository. +#> +Configuration MSFT_PSRepository_InstallTestModule_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSModule 'Integration_Test' + { + Name = $Node.TestModuleName + Repository = $Node.Name + } + } +} + +<# + .SYNOPSIS + Changes the properties of the repository. +#> +Configuration MSFT_PSRepository_ChangeRepository_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSRepository 'Integration_Test' + { + Name = $Node.Name + SourceLocation = $Node.SourceLocation + PublishLocation = $Node.PublishLocation + ScriptSourceLocation = $Node.ScriptSourceLocation + ScriptPublishLocation = $Node.ScriptPublishLocation + PackageManagementProvider = $Node.PackageManagementProvider + InstallationPolicy = 'Untrusted' + } + } +} + +<# + .SYNOPSIS + Removes the repository. +#> +Configuration MSFT_PSRepository_RemoveRepository_Config +{ + Import-DscResource -ModuleName 'PowerShellGet' + + node $AllNodes.NodeName + { + PSRepository 'Integration_Test' + { + Ensure = 'Absent' + Name = $Node.Name + } + } +} diff --git a/DSC/Tests/Unit/MSFT_PSRepository.Tests.ps1 b/DSC/Tests/Unit/MSFT_PSRepository.Tests.ps1 new file mode 100644 index 00000000..3a9323ba --- /dev/null +++ b/DSC/Tests/Unit/MSFT_PSRepository.Tests.ps1 @@ -0,0 +1,452 @@ +#region HEADER +# This must be same name as the root folder, and module manifest. +$script:DSCModuleName = 'DSC' +$script:DSCResourceName = 'MSFT_PSRepository' + +# Unit Test Template Version: 1.2.4 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -ResourceType 'Mof' ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup { +} + +function Invoke-TestCleanup { + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try { + Invoke-TestSetup + + InModuleScope $script:DSCResourceName { + $mockRepositoryName = 'PSTestGallery' + $mockSourceLocation = 'https://www.poshtestgallery.com/api/v2/' + $mockPublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' + $mockScriptSourceLocation = 'https://www.poshtestgallery.com/api/v2/items/psscript/' + $mockScriptPublishLocation = 'https://www.poshtestgallery.com/api/v2/package/' + $mockPackageManagementProvider = 'NuGet' + $mockInstallationPolicy_Trusted = 'Trusted' + $mockInstallationPolicy_NotTrusted = 'Untrusted' + + $mockRepository = New-Object -TypeName Object | + Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockRepositoryName -PassThru | + Add-Member -Name 'SourceLocation' -MemberType NoteProperty -Value $mockSourceLocation -PassThru | + Add-Member -Name 'ScriptSourceLocation' -MemberType NoteProperty -Value $mockScriptSourceLocation -PassThru | + Add-Member -Name 'PublishLocation' -MemberType NoteProperty -Value $mockPublishLocation -PassThru | + Add-Member -Name 'ScriptPublishLocation' -MemberType NoteProperty -Value $mockScriptPublishLocation -PassThru | + Add-Member -Name 'InstallationPolicy' -MemberType NoteProperty -Value $mockInstallationPolicy_Trusted -PassThru | + Add-Member -Name 'PackageManagementProvider' -MemberType NoteProperty -Value $mockPackageManagementProvider -PassThru | + Add-Member -Name 'Trusted' -MemberType NoteProperty -Value $true -PassThru | + Add-Member -Name 'Registered' -MemberType NoteProperty -Value $true -PassThru -Force + + $mockGetPSRepository = { + return @($mockRepository) + } + + Describe 'MSFT_PSRepository\Get-TargetResource' -Tag 'Get' { + Context 'When the system is in the desired state' { + Context 'When the configuration is present' { + BeforeAll { + Mock -CommandName Get-PSRepository -MockWith $mockGetPSRepository + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource -Name $mockRepositoryName + $getTargetResourceResult.Name | Should -Be $mockRepositoryName + + Assert-MockCalled -CommandName Get-PSRepository -Exactly -Times 1 -Scope It + } + + It 'Should return the correct values for the other properties' { + $getTargetResourceResult = Get-TargetResource -Name $mockRepositoryName + + $getTargetResourceResult.Ensure | Should -Be 'Present' + $getTargetResourceResult.SourceLocation | Should -Be $mockRepository.SourceLocation + $getTargetResourceResult.ScriptSourceLocation | Should -Be $mockRepository.ScriptSourceLocation + $getTargetResourceResult.PublishLocation | Should -Be $mockRepository.PublishLocation + $getTargetResourceResult.ScriptPublishLocation | Should -Be $mockRepository.ScriptPublishLocation + $getTargetResourceResult.InstallationPolicy | Should -Be $mockRepository.InstallationPolicy + $getTargetResourceResult.PackageManagementProvider | Should -Be $mockRepository.PackageManagementProvider + $getTargetResourceResult.Trusted | Should -Be $true + $getTargetResourceResult.Registered | Should -Be $true + + Assert-MockCalled -CommandName Get-PSRepository -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration is absent' { + BeforeAll { + Mock -CommandName Get-PSRepository + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource -Name $mockRepositoryName + $getTargetResourceResult.Name | Should -Be $mockRepositoryName + + Assert-MockCalled -CommandName Get-PSRepository -Exactly -Times 1 -Scope It + } + + It 'Should return the correct values for the other properties' { + $getTargetResourceResult = Get-TargetResource -Name $mockRepositoryName + + $getTargetResourceResult.Ensure | Should -Be 'Absent' + $getTargetResourceResult.SourceLocation | Should -BeNullOrEmpty + $getTargetResourceResult.ScriptSourceLocation | Should -BeNullOrEmpty + $getTargetResourceResult.PublishLocation | Should -BeNullOrEmpty + $getTargetResourceResult.ScriptPublishLocation | Should -BeNullOrEmpty + $getTargetResourceResult.InstallationPolicy | Should -BeNullOrEmpty + $getTargetResourceResult.PackageManagementProvider | Should -BeNullOrEmpty + $getTargetResourceResult.Trusted | Should -Be $false + $getTargetResourceResult.Registered | Should -Be $false + + Assert-MockCalled -CommandName Get-PSRepository -Exactly -Times 1 -Scope It + } + } + } + } + + Describe 'MSFT_PSRepository\Set-TargetResource' -Tag 'Set' { + Context 'When the system is not in the desired state' { + BeforeAll { + Mock -CommandName Register-PSRepository + Mock -CommandName Unregister-PSRepository + Mock -CommandName Set-PSRepository + } + + Context 'When the configuration should be present' { + Context 'When the repository does not exist' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockRepositoryName + SourceLocation = $null + ScriptSourceLocation = $null + PublishLocation = $null + ScriptPublishLocation = $null + InstallationPolicy = $null + PackageManagementProvider = $null + Trusted = $false + Registered = $false + } + } + } + + It 'Should return call the correct mocks' { + $setTargetResourceParameters = @{ + Name = $mockRepository.Name + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Register-PSRepository -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Unregister-PSRepository -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-PSRepository -Exactly -Times 0 -Scope It + } + } + + Context 'When the repository do exist but with wrong properties' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockRepository.Name + SourceLocation = 'https://www.powershellgallery.com/api/v2/' + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + Trusted = $mockRepository.Trusted + Registered = $mockRepository.Registered + } + } + } + + It 'Should return call the correct mocks' { + $setTargetResourceParameters = @{ + Name = $mockRepository.Name + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Register-PSRepository -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Unregister-PSRepository -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-PSRepository -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the configuration should be absent' { + Context 'When the repository do exist' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockRepository.Name + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + Trusted = $mockRepository.Trusted + Registered = $mockRepository.Registered + } + } + } + + It 'Should return call the correct mocks' { + $setTargetResourceParameters = @{ + Ensure = 'Absent' + Name = $mockRepositoryName + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Register-PSRepository -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Unregister-PSRepository -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-PSRepository -Exactly -Times 0 -Scope It + } + } + } + } + } + + Describe 'MSFT_PSRepository\Test-TargetResource' -Tag 'Test' { + Context 'When the system is in the desired state' { + Context 'When the configuration is present' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockRepository.Name + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + Trusted = $mockRepository.Trusted + Registered = $mockRepository.Registered + } + } + } + + It 'Should return the state as $true' { + $testTargetResourceResult = Test-TargetResource -Name $mockRepositoryName + $testTargetResourceResult | Should -Be $true + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration is absent' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockRepositoryName + SourceLocation = $null + ScriptSourceLocation = $null + PublishLocation = $null + ScriptPublishLocation = $null + InstallationPolicy = $null + PackageManagementProvider = $null + Trusted = $false + Registered = $false + } + } + } + + It 'Should return the state as $true' { + $testTargetResourceResult = Test-TargetResource -Ensure 'Absent' -Name $mockRepositoryName + $testTargetResourceResult | Should -Be $true + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the system is not in the desired state' { + Context 'When the configuration should be present' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockRepositoryName + SourceLocation = $null + ScriptSourceLocation = $null + PublishLocation = $null + ScriptPublishLocation = $null + InstallationPolicy = $null + PackageManagementProvider = $null + Trusted = $false + Registered = $false + } + } + } + + It 'Should return the state as $false' { + $testTargetResourceParameters = @{ + Name = $mockRepository.Name + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When a property is not in desired state' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockRepository.Name + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + Trusted = $mockRepository.Trusted + Registered = $mockRepository.Registered + } + } + } + + $defaultTestCase = @{ + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + } + + $testCaseSourceLocationIsMissing = $defaultTestCase.Clone() + $testCaseSourceLocationIsMissing['TestName'] = 'SourceLocation is missing' + $testCaseSourceLocationIsMissing['SourceLocation'] = 'https://www.powershellgallery.com/api/v2/' + + $testCaseScriptSourceLocationIsMissing = $defaultTestCase.Clone() + $testCaseScriptSourceLocationIsMissing['TestName'] = 'ScriptSourceLocation is missing' + $testCaseScriptSourceLocationIsMissing['ScriptSourceLocation'] = 'https://www.powershellgallery.com/api/v2/items/psscript/' + + $testCasePublishLocationIsMissing = $defaultTestCase.Clone() + $testCasePublishLocationIsMissing['TestName'] = 'PublishLocation is missing' + $testCasePublishLocationIsMissing['PublishLocation'] = 'https://www.powershellgallery.com/api/v2/package/' + + $testCaseScriptPublishLocationIsMissing = $defaultTestCase.Clone() + $testCaseScriptPublishLocationIsMissing['TestName'] = 'ScriptPublishLocation is missing' + $testCaseScriptPublishLocationIsMissing['ScriptPublishLocation'] = 'https://www.powershellgallery.com/api/v2/package/' + + $testCaseInstallationPolicyIsMissing = $defaultTestCase.Clone() + $testCaseInstallationPolicyIsMissing['TestName'] = 'InstallationPolicy is missing' + $testCaseInstallationPolicyIsMissing['InstallationPolicy'] = $mockInstallationPolicy_NotTrusted + + $testCasePackageManagementProviderIsMissing = $defaultTestCase.Clone() + $testCasePackageManagementProviderIsMissing['TestName'] = 'PackageManagementProvider is missing' + $testCasePackageManagementProviderIsMissing['PackageManagementProvider'] = 'PSGallery' + + $testCases = @( + $testCaseSourceLocationIsMissing + $testCaseScriptSourceLocationIsMissing + $testCasePublishLocationIsMissing + $testCaseScriptPublishLocationIsMissing + $testCaseInstallationPolicyIsMissing + $testCasePackageManagementProviderIsMissing + ) + + It 'Should return the state as $false when the correct ' -TestCases $testCases { + param + ( + $SourceLocation, + $ScriptSourceLocation, + $PublishLocation, + $ScriptPublishLocation, + $InstallationPolicy, + $PackageManagementProvider + ) + + $testTargetResourceParameters = @{ + Name = $mockRepositoryName + SourceLocation = $SourceLocation + ScriptSourceLocation = $ScriptSourceLocation + PublishLocation = $PublishLocation + ScriptPublishLocation = $ScriptPublishLocation + InstallationPolicy = $InstallationPolicy + PackageManagementProvider = $PackageManagementProvider + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration should be absent' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockRepositoryName + SourceLocation = $mockRepository.SourceLocation + ScriptSourceLocation = $mockRepository.ScriptSourceLocation + PublishLocation = $mockRepository.PublishLocation + ScriptPublishLocation = $mockRepository.ScriptPublishLocation + InstallationPolicy = $mockRepository.InstallationPolicy + PackageManagementProvider = $mockRepository.PackageManagementProvider + Trusted = $mockRepository.Trusted + Registered = $mockRepository.Registered + } + } + } + + It 'Should return the state as $false' { + $testTargetResourceResult = Test-TargetResource -Ensure 'Absent' -Name $mockRepositoryName + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + } + } +} +finally { + Invoke-TestCleanup +} diff --git a/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 b/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 index f4a5f255..8e0ddf36 100644 --- a/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 +++ b/DSC/Tests/Unit/PowerShellGet.ResourceHelper.Tests.ps1 @@ -6,281 +6,566 @@ $script:helperModuleName = 'PowerShellGet.ResourceHelper' -Describe "$script:helperModuleName Unit Tests" { - BeforeAll { - $resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent - $dscResourcesFolderFilePath = Join-Path -Path (Join-Path -Path $resourceModuleRoot -ChildPath 'Modules') ` - -ChildPath $script:helperModuleName - - Import-Module -Name (Join-Path -Path $dscResourcesFolderFilePath ` - -ChildPath "$script:helperModuleName.psm1") -Force +$resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent +$dscResourcesFolderFilePath = Join-Path -Path (Join-Path -Path $resourceModuleRoot -ChildPath 'Modules') ` + -ChildPath $script:helperModuleName + +Import-Module -Name (Join-Path -Path $dscResourcesFolderFilePath ` + -ChildPath "$script:helperModuleName.psm1") -Force + +InModuleScope $script:helperModuleName { + Describe 'New-SplatParameterHashTable' { + Context 'When specific parameters should be returned' { + It 'Should return a hashtable with the correct values' { + $mockPSBoundParameters = @{ + Property1 = '1' + Property2 = '2' + Property3 = '3' + Property4 = '4' + } + + $extractArgumentsResult = New-SplatParameterHashTable ` + -FunctionBoundParameters $mockPSBoundParameters ` + -ArgumentNames @('Property2', 'Property3') + + $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] + $extractArgumentsResult.Count | Should -Be 2 + $extractArgumentsResult.ContainsKey('Property2') | Should -BeTrue + $extractArgumentsResult.ContainsKey('Property3') | Should -BeTrue + $extractArgumentsResult.Property2 | Should -Be '2' + $extractArgumentsResult.Property3 | Should -Be '3' + } + } + + Context 'When the specific parameters to be returned does not exist' { + It 'Should return an empty hashtable' { + $mockPSBoundParameters = @{ + Property1 = '1' + } + + $extractArgumentsResult = New-SplatParameterHashTable ` + -FunctionBoundParameters $mockPSBoundParameters ` + -ArgumentNames @('Property2', 'Property3') + + $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] + $extractArgumentsResult.Count | Should -Be 0 + } + } + + Context 'When and empty hashtable is passed in the parameter FunctionBoundParameters' { + It 'Should return an empty hashtable' { + $mockPSBoundParameters = @{ + } + + $extractArgumentsResult = New-SplatParameterHashTable ` + -FunctionBoundParameters $mockPSBoundParameters ` + -ArgumentNames @('Property2', 'Property3') + + $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] + $extractArgumentsResult.Count | Should -Be 0 + } + } } - InModuleScope $script:helperModuleName { - Describe 'New-SplatParameterHashTable' { - Context 'When specific parameters should be returned' { - It 'Should return a hashtable with the correct values' { - $mockPSBoundParameters = @{ - Property1 = '1' - Property2 = '2' - Property3 = '3' - Property4 = '4' - } + Describe 'Test-ParameterValue' { + BeforeAll { + $mockProviderName = 'PowerShellGet' + } + + Context 'When passing a correct uri as ''Value'' and type is ''SourceUri''' { + It 'Should not throw an error' { + { + Test-ParameterValue ` + -Value 'https://mocked.uri' ` + -Type 'SourceUri' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + } + } + + Context 'When passing an invalid uri as ''Value'' and type is ''SourceUri''' { + It 'Should throw the correct error' { + $mockParameterName = 'mocked.uri' - $extractArgumentsResult = New-SplatParameterHashTable ` - -FunctionBoundParameters $mockPSBoundParameters ` - -ArgumentNames @('Property2', 'Property3') + { + Test-ParameterValue ` + -Value $mockParameterName ` + -Type 'SourceUri' ` + -ProviderName $mockProviderName + } | Should -Throw ($LocalizedData.InValidUri -f $mockParameterName) + } + } + + Context 'When passing a correct path as ''Value'' and type is ''DestinationPath''' { + It 'Should not throw an error' { + { + Test-ParameterValue ` + -Value 'TestDrive:\' ` + -Type 'DestinationPath' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + } + } + + Context 'When passing an invalid path as ''Value'' and type is ''DestinationPath''' { + It 'Should throw the correct error' { + $mockParameterName = 'TestDrive:\NonExistentPath' + + { + Test-ParameterValue ` + -Value $mockParameterName ` + -Type 'DestinationPath' ` + -ProviderName $mockProviderName + } | Should -Throw ($LocalizedData.PathDoesNotExist -f $mockParameterName) + } + } + + Context 'When passing a correct uri as ''Value'' and type is ''PackageSource''' { + It 'Should not throw an error' { + { + Test-ParameterValue ` + -Value 'https://mocked.uri' ` + -Type 'PackageSource' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + } + } - $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] - $extractArgumentsResult.Count | Should -Be 2 - $extractArgumentsResult.ContainsKey('Property2') | Should -BeTrue - $extractArgumentsResult.ContainsKey('Property3') | Should -BeTrue - $extractArgumentsResult.Property2 | Should -Be '2' - $extractArgumentsResult.Property3 | Should -Be '3' + Context 'When passing an correct package source as ''Value'' and type is ''PackageSource''' { + BeforeAll { + $mockParameterName = 'PSGallery' + + Mock -CommandName Get-PackageSource -MockWith { + return New-Object -TypeName Object | + Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockParameterName -PassThru | + Add-Member -Name 'ProviderName' -MemberType NoteProperty -Value $mockProviderName -PassThru -Force } } - Context 'When the specific parameters to be returned does not exist' { - It 'Should return an empty hashtable' { - $mockPSBoundParameters = @{ - Property1 = '1' - } + It 'Should not throw an error' { + { + Test-ParameterValue ` + -Value $mockParameterName ` + -Type 'PackageSource' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } + + Context 'When passing type is ''PackageSource'' and passing a package source that does not exist' { + BeforeAll { + $mockParameterName = 'PSGallery' + + Mock -CommandName Get-PackageSource + } + + It 'Should not throw an error' { + { + Test-ParameterValue ` + -Value $mockParameterName ` + -Type 'PackageSource' ` + -ProviderName $mockProviderName + } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } + + Context 'When passing invalid type in parameter ''Type''' { + BeforeAll { + $mockType = 'UnknownType' + } - $extractArgumentsResult = New-SplatParameterHashTable ` - -FunctionBoundParameters $mockPSBoundParameters ` - -ArgumentNames @('Property2', 'Property3') + It 'Should throw the correct error' { + { + Test-ParameterValue ` + -Value 'AnyArgument' ` + -Type $mockType ` + -ProviderName $mockProviderName + } | Should -Throw ($LocalizedData.UnexpectedArgument -f $mockType) + } + } + } + + Describe 'Test-VersionParameter' { + Context 'When not passing in any parameters (using default values)' { + It 'Should return true' { + Test-VersionParameter | Should -BeTrue + } + } + + Context 'When only ''RequiredVersion'' are passed' { + It 'Should return true' { + Test-VersionParameter -RequiredVersion '3.0.0.0' | Should -BeTrue + } + } - $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] - $extractArgumentsResult.Count | Should -Be 0 + Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { + It 'Should throw the correct error' { + { + Test-VersionParameter ` + -MinimumVersion '2.0.0.0' ` + -MaximumVersion '1.0.0.0' + } | Should -Throw $LocalizedData.VersionError + } + } + + Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { + It 'Should throw the correct error' { + { + Test-VersionParameter ` + -MinimumVersion '2.0.0.0' ` + -MaximumVersion '1.0.0.0' + } | Should -Throw $LocalizedData.VersionError + } + } + + Context 'When ''RequiredVersion'', ''MinimumVersion'', and ''MaximumVersion'' are passed' { + It 'Should throw the correct error' { + { + Test-VersionParameter ` + -RequiredVersion '3.0.0.0' ` + -MinimumVersion '2.0.0.0' ` + -MaximumVersion '1.0.0.0' + } | Should -Throw $LocalizedData.VersionError + } + } + } + + Describe 'Get-InstallationPolicy' { + Context 'When the package source exist, and is trusted' { + BeforeAll { + Mock -CommandName Get-PackageSource -MockWith { + return New-Object -TypeName Object | + Add-Member -Name 'IsTrusted' -MemberType NoteProperty -Value $true -PassThru -Force } } - Context 'When and empty hashtable is passed in the parameter FunctionBoundParameters' { - It 'Should return an empty hashtable' { - $mockPSBoundParameters = @{ - } + It 'Should return true' { + Get-InstallationPolicy -RepositoryName 'PSGallery' | Should -BeTrue - $extractArgumentsResult = New-SplatParameterHashTable ` - -FunctionBoundParameters $mockPSBoundParameters ` - -ArgumentNames @('Property2', 'Property3') + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } + } - $extractArgumentsResult | Should -BeOfType [System.Collections.Hashtable] - $extractArgumentsResult.Count | Should -Be 0 + Context 'When the package source exist, and is not trusted' { + BeforeAll { + Mock -CommandName Get-PackageSource -MockWith { + return New-Object -TypeName Object | + Add-Member -Name 'IsTrusted' -MemberType NoteProperty -Value $false -PassThru -Force } } + + It 'Should return false' { + + + Get-InstallationPolicy -RepositoryName 'PSGallery' | Should -BeFalse + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + } } - Describe 'Test-ParameterValue' { + Context 'When the package source does not exist' { BeforeAll { - $mockProviderName = 'PowerShellGet' + Mock -CommandName Get-PackageSource + } + + It 'Should return $null' { + Get-InstallationPolicy -RepositoryName 'Unknown' | Should -BeNullOrEmpty + + Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It } + } + } - Context 'When passing a correct uri as ''Value'' and type is ''SourceUri''' { - It 'Should not throw an error' { - { - Test-ParameterValue ` - -Value 'https://mocked.uri' ` - -Type 'SourceUri' ` - -ProviderName $mockProviderName - } | Should -Not -Throw + Describe 'Testing Test-DscParameterState' -Tag TestDscParameterState { + Context -Name 'When passing values' -Fixture { + It 'Should return true for two identical tables' { + $mockDesiredValues = @{ Example = 'test' } + + $testParameters = @{ + CurrentValues = $mockDesiredValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $true } - Context 'When passing an invalid uri as ''Value'' and type is ''SourceUri''' { - It 'Should throw the correct error' { - $mockParameterName = 'mocked.uri' + It 'Should return false when a value is different for [System.String]' { + $mockCurrentValues = @{ Example = [System.String]'something' } + $mockDesiredValues = @{ Example = [System.String]'test' } - { - Test-ParameterValue ` - -Value $mockParameterName ` - -Type 'SourceUri' ` - -ProviderName $mockProviderName - } | Should -Throw ($LocalizedData.InValidUri -f $mockParameterName) + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $false } - Context 'When passing a correct path as ''Value'' and type is ''DestinationPath''' { - It 'Should not throw an error' { - { - Test-ParameterValue ` - -Value 'TestDrive:\' ` - -Type 'DestinationPath' ` - -ProviderName $mockProviderName - } | Should -Not -Throw + It 'Should return false when a value is different for [System.Int32]' { + $mockCurrentValues = @{ Example = [System.Int32]1 } + $mockDesiredValues = @{ Example = [System.Int32]2 } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $false } - Context 'When passing an invalid path as ''Value'' and type is ''DestinationPath''' { - It 'Should throw the correct error' { - $mockParameterName = 'TestDrive:\NonExistentPath' + It 'Should return false when a value is different for [Int16]' { + $mockCurrentValues = @{ Example = [System.Int16]1 } + $mockDesiredValues = @{ Example = [System.Int16]2 } - { - Test-ParameterValue ` - -Value $mockParameterName ` - -Type 'DestinationPath' ` - -ProviderName $mockProviderName - } | Should -Throw ($LocalizedData.PathDoesNotExist -f $mockParameterName) + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $false } - Context 'When passing a correct uri as ''Value'' and type is ''PackageSource''' { - It 'Should not throw an error' { - { - Test-ParameterValue ` - -Value 'https://mocked.uri' ` - -Type 'PackageSource' ` - -ProviderName $mockProviderName - } | Should -Not -Throw + It 'Should return false when a value is different for [UInt16]' { + $mockCurrentValues = @{ Example = [System.UInt16]1 } + $mockDesiredValues = @{ Example = [System.UInt16]2 } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $false } - Context 'When passing an correct package source as ''Value'' and type is ''PackageSource''' { - BeforeAll { - $mockParameterName = 'PSGallery' + It 'Should return false when a value is missing' { + $mockCurrentValues = @{ } + $mockDesiredValues = @{ Example = 'test' } - Mock -CommandName Get-PackageSource -MockWith { - return New-Object -TypeName Object | - Add-Member -Name 'Name' -MemberType NoteProperty -Value $mockParameterName -PassThru | - Add-Member -Name 'ProviderName' -MemberType NoteProperty -Value $mockProviderName -PassThru -Force - } + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } - It 'Should not throw an error' { - { - Test-ParameterValue ` - -Value $mockParameterName ` - -Type 'PackageSource' ` - -ProviderName $mockProviderName - } | Should -Not -Throw + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return true when only a specified value matches, but other non-listed values do not' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = 'true' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } - Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('Example') } + + Test-DscParameterState @testParameters | Should -Be $true } - Context 'When passing type is ''PackageSource'' and passing a package source that does not exist' { - BeforeAll { - $mockParameterName = 'PSGallery' + It 'Should return false when only specified values do not match, but other non-listed values do ' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = 'true' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } - Mock -CommandName Get-PackageSource + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('SecondExample') } - It 'Should not throw an error' { - { - Test-ParameterValue ` - -Value $mockParameterName ` - -Type 'PackageSource' ` - -ProviderName $mockProviderName - } | Should -Not -Throw + Test-DscParameterState @testParameters | Should -Be $false + } + + It 'Should return false when an empty hash table is used in the current values' { + $mockCurrentValues = @{ } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } - Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $false } - Context 'When passing invalid type in parameter ''Type''' { - BeforeAll { - $mockType = 'UnknownType' + It 'Should return true when evaluating a table against a CimInstance' { + $mockCurrentValues = @{ Handle = '0'; ProcessId = '1000' } + + $mockWin32ProcessProperties = @{ + Handle = 0 + ProcessId = 1000 } - It 'Should throw the correct error' { - { - Test-ParameterValue ` - -Value 'AnyArgument' ` - -Type $mockType ` - -ProviderName $mockProviderName - } | Should -Throw ($LocalizedData.UnexpectedArgument -f $mockType) + $mockNewCimInstanceParameters = @{ + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' + ClientOnly = $true } - } - } - Describe 'Test-VersionParameter' { - Context 'When not passing in any parameters (using default values)' { - It 'Should return true' { - Test-VersionParameter | Should -BeTrue + $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('Handle', 'ProcessId') } + + Test-DscParameterState @testParameters | Should -Be $true } - Context 'When only ''RequiredVersion'' are passed' { - It 'Should return true' { - Test-VersionParameter -RequiredVersion '3.0.0.0' | Should -BeTrue + It 'Should return false when evaluating a table against a CimInstance and a value is wrong' { + $mockCurrentValues = @{ Handle = '1'; ProcessId = '1000' } + + $mockWin32ProcessProperties = @{ + Handle = 0 + ProcessId = 1000 + } + + $mockNewCimInstanceParameters = @{ + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' + ClientOnly = $true + } + + $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = @('Handle', 'ProcessId') } + + Test-DscParameterState @testParameters | Should -Be $false } - Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { - It 'Should throw the correct error' { - { - Test-VersionParameter ` - -MinimumVersion '2.0.0.0' ` - -MaximumVersion '1.0.0.0' - } | Should -Throw $LocalizedData.VersionError + It 'Should return true when evaluating a hash table containing an array' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = @('1', '2') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $true } - Context 'When ''MinimumVersion'' has a lower version than ''MaximumVersion''' { - It 'Should throw the correct error' { - { - Test-VersionParameter ` - -MinimumVersion '2.0.0.0' ` - -MaximumVersion '1.0.0.0' - } | Should -Throw $LocalizedData.VersionError + It 'Should return false when evaluating a hash table containing an array with wrong values' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = @('A', 'B') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $false } - Context 'When ''RequiredVersion'', ''MinimumVersion'', and ''MaximumVersion'' are passed' { - It 'Should throw the correct error' { - { - Test-VersionParameter ` - -RequiredVersion '3.0.0.0' ` - -MinimumVersion '2.0.0.0' ` - -MaximumVersion '1.0.0.0' - } | Should -Throw $LocalizedData.VersionError + It 'Should return false when evaluating a hash table containing an array, but the CurrentValues are missing an array' { + $mockCurrentValues = @{ Example = 'test' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + Test-DscParameterState @testParameters | Should -Be $false } - } - Describe 'Get-InstallationPolicy' { - Context 'When the package source exist, and is trusted' { - BeforeAll { - Mock -CommandName Get-PackageSource -MockWith { - return New-Object -TypeName Object | - Add-Member -Name 'IsTrusted' -MemberType NoteProperty -Value $true -PassThru -Force - } + It 'Should return false when evaluating a hash table containing an array, but the property i CurrentValues is $null' { + $mockCurrentValues = @{ Example = 'test'; SecondExample = $null } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } + + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } - It 'Should return true' { - Get-InstallationPolicy -RepositoryName 'PSGallery' | Should -BeTrue + Test-DscParameterState @testParameters | Should -Be $false + } + } + + Context -Name 'When passing invalid types for DesiredValues' -Fixture { + It 'Should throw the correct error when DesiredValues is of wrong type' { + $mockCurrentValues = @{ Example = 'something' } + $mockDesiredValues = 'NotHashTable' - Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues } + + $mockCorrectErrorMessage = ($script:localizedData.PropertyTypeInvalidForDesiredValues -f $testParameters.DesiredValues.GetType().Name) + { Test-DscParameterState @testParameters } | Should -Throw $mockCorrectErrorMessage } - Context 'When the package source exist, and is not trusted' { - BeforeAll { - Mock -CommandName Get-PackageSource -MockWith { - return New-Object -TypeName Object | - Add-Member -Name 'IsTrusted' -MemberType NoteProperty -Value $false -PassThru -Force + It 'Should write a warning when DesiredValues contain an unsupported type' { + Mock -CommandName Write-Warning -Verifiable + + # This is a dummy type to test with a type that could never be a correct one. + class MockUnknownType { + [ValidateNotNullOrEmpty()] + [System.String] + $Property1 + + [ValidateNotNullOrEmpty()] + [System.String] + $Property2 + + MockUnknownType() { } } - It 'Should return false' { + $mockCurrentValues = @{ Example = New-Object -TypeName MockUnknownType } + $mockDesiredValues = @{ Example = New-Object -TypeName MockUnknownType } + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + } - Get-InstallationPolicy -RepositoryName 'PSGallery' | Should -BeFalse + Test-DscParameterState @testParameters | Should -Be $false - Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It - } + Assert-MockCalled -CommandName Write-Warning -Exactly -Times 1 } + } + + Context -Name 'When passing an CimInstance as DesiredValue and ValuesToCheck is $null' -Fixture { + It 'Should throw the correct error' { + $mockCurrentValues = @{ Example = 'something' } - Context 'When the package source does not exist' { - BeforeAll { - Mock -CommandName Get-PackageSource + $mockWin32ProcessProperties = @{ + Handle = 0 + ProcessId = 1000 } - It 'Should return $null' { - Get-InstallationPolicy -RepositoryName 'Unknown' | Should -BeNullOrEmpty + $mockNewCimInstanceParameters = @{ + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' + ClientOnly = $true + } + + $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters - Assert-MockCalled -CommandName Get-PackageSource -Exactly -Times 1 -Scope It + $testParameters = @{ + CurrentValues = $mockCurrentValues + DesiredValues = $mockDesiredValues + ValuesToCheck = $null } + + $mockCorrectErrorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck + { Test-DscParameterState @testParameters } | Should -Throw $mockCorrectErrorMessage } } + + Assert-VerifiableMock } } From 1ed49142f572d2d216edf7ca59fa21b62c010fa9 Mon Sep 17 00:00:00 2001 From: Michael Greene Date: Thu, 21 Feb 2019 08:56:15 -0600 Subject: [PATCH 18/21] Add DSC Unit tests to BVT It appears no one is verifying the PowerShellGet DSC resources. I would like to request that we add tests, starting with just the basic unit tests, to daily build validation. THIS IS LIKELY TO BREAK THE BUILD TEMPORARILY. --- DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 b/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 index e264d2f1..f840f1c1 100644 --- a/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 +++ b/DSC/Tests/Unit/MSFT_PSModule.Tests.ps1 @@ -97,7 +97,7 @@ try { return $mockGalleryModule } - Describe 'MSFT_PSModule\Get-TargetResource' -Tag 'Get' { + Describe 'MSFT_PSModule\Get-TargetResource' -Tag 'Get','BVT' { Context 'When the system is in the desired state' { Context 'When the configuration is present' { Context 'When the module is trusted' { @@ -254,7 +254,7 @@ try { } } - Describe 'MSFT_PSModule\Set-TargetResource' -Tag 'Set' { + Describe 'MSFT_PSModule\Set-TargetResource' -Tag 'Set','BVT' { Context 'When the system is not in the desired state' { Context 'When the configuration should be present' { BeforeAll { @@ -384,7 +384,7 @@ try { } } - Describe 'MSFT_PSModule\Test-TargetResource' -Tag 'Test' { + Describe 'MSFT_PSModule\Test-TargetResource' -Tag 'Test','BVT' { Context 'When the system is in the desired state' { Context 'When the configuration is present' { BeforeEach { From 87783e9a0b85ccd71c6e730640546585d1a40c3b Mon Sep 17 00:00:00 2001 From: Reece Dunham Date: Fri, 22 Feb 2019 21:32:06 -0500 Subject: [PATCH 19/21] Cleaned up Travis CI config (#429) --- .travis.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08014986..b71ac0bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ - language: csharp dotnet: 2.0.2 mono: none @@ -37,14 +36,4 @@ install: script: - echo "TRAVIS_EVENT_TYPE value $TRAVIS_EVENT_TYPE" - - ./tools/travis.sh - - - - - - - - - - + - bash ./tools/travis.sh From 6926ad42e61fc54c5f5b1cdc9e9d8dfceccc2c80 Mon Sep 17 00:00:00 2001 From: Edwin Young Date: Mon, 25 Feb 2019 12:12:53 -0800 Subject: [PATCH 20/21] Fix #78 (#421) --- Tests/PSGetTestUtils.psm1 | 257 +++++----- Tests/PSGetUpdateModule.Tests.ps1 | 113 +++-- Tests/PSGetUpdateScript.Tests.ps1 | 89 ++-- Tests/Pester.PrereleaseVersion.Tests.ps1 | 459 +++++++++--------- src/PowerShellGet/PSGet.Resource.psd1 | 44 +- .../functions/Get-InstallationScope.ps1 | 27 ++ .../public/psgetfunctions/Update-Module.ps1 | 69 +-- .../public/psgetfunctions/Update-Script.ps1 | 30 +- 8 files changed, 558 insertions(+), 530 deletions(-) create mode 100644 src/PowerShellGet/private/functions/Get-InstallationScope.ps1 diff --git a/Tests/PSGetTestUtils.psm1 b/Tests/PSGetTestUtils.psm1 index e2692473..270892d2 100644 --- a/Tests/PSGetTestUtils.psm1 +++ b/Tests/PSGetTestUtils.psm1 @@ -49,7 +49,7 @@ if($script:IsInbox) $script:MyDocumentsPSPath = if($script:MyDocumentsFolderPath) { Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsFolderPath -ChildPath "WindowsPowerShell" - } + } else { Microsoft.PowerShell.Management\Join-Path -Path $env:USERPROFILE -ChildPath "Documents\WindowsPowerShell" @@ -61,7 +61,7 @@ elseif($script:IsCoreCLR) { $script:MyDocumentsPSPath = if($script:MyDocumentsFolderPath) { Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsFolderPath -ChildPath 'PowerShell' - } + } else { Microsoft.PowerShell.Management\Join-Path -Path $HOME -ChildPath "Documents\PowerShell" @@ -91,8 +91,8 @@ $script:ProgramDataExePath = Microsoft.PowerShell.Management\Join-Path -Path $sc $script:ApplocalDataExePath = Microsoft.PowerShell.Management\Join-Path -Path $script:PSGetAppLocalPath -ChildPath $script:NuGetExeName $script:moduleSourcesFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:PSGetAppLocalPath -ChildPath 'PSRepositories.xml' -# PowerShellGetFormatVersion will be incremented when we change the .nupkg format structure. -# PowerShellGetFormatVersion is in the form of Major.Minor. +# PowerShellGetFormatVersion will be incremented when we change the .nupkg format structure. +# PowerShellGetFormatVersion is in the form of Major.Minor. # Minor is incremented for the backward compatible format change. # Major is incremented for the breaking change. $script:CurrentPSGetFormatVersion = "1.0" @@ -151,9 +151,9 @@ function GetAndSet-PSGetTestGalleryDetails $psgetModule = Import-Module -Name PowerShellGet -PassThru -Scope Local $ResolvedLocalSource = & $psgetModule Resolve-Location -Location $SourceUri -LocationParameterName 'SourceLocation' - if($ResolvedLocalSource -and - $PSVersionTable.PSVersion -ge '5.0.0' -and - [System.Environment]::OSVersion.Version -ge "6.2.9200.0" -and + if($ResolvedLocalSource -and + $PSVersionTable.PSVersion -ge '5.0.0' -and + [System.Environment]::OSVersion.Version -ge "6.2.9200.0" -and $PSCulture -eq 'en-US') { $SourceUri = $SourceUri @@ -237,7 +237,7 @@ function Install-NuGetBinaries } } - if($script:NuGetProvider -and + if($script:NuGetProvider -and (($script:NuGetExePath -and (Microsoft.PowerShell.Management\Test-Path -Path $script:NuGetExePath)) -or ($script:DotnetCommandPath -and (Microsoft.PowerShell.Management\Test-Path -Path $script:DotnetCommandPath)))) { @@ -256,8 +256,8 @@ function Install-NuGetBinaries & $psgetModule Install-NuGetClientBinaries -Force -BootstrapNuGetExe -CallerPSCmdlet $PSCmdlet $script:NuGetProvider = PackageManagement\Get-PackageProvider -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | - Microsoft.PowerShell.Core\Where-Object { - $_.Name -eq $script:NuGetProviderName -and + Microsoft.PowerShell.Core\Where-Object { + $_.Name -eq $script:NuGetProviderName -and $_.Version -ge $script:NuGetProviderVersion } @@ -277,11 +277,11 @@ function Install-NuGetBinaries # NuGet.exe does not work if it is under $env:WINDIR, so skipping it from the Get-Command results $nugetCmd = Microsoft.PowerShell.Core\Get-Command -Name $script:NuGetExeName ` -ErrorAction SilentlyContinue ` - -WarningAction SilentlyContinue | - Microsoft.PowerShell.Core\Where-Object { - $_.Path -and + -WarningAction SilentlyContinue | + Microsoft.PowerShell.Core\Where-Object { + $_.Path -and ((Microsoft.PowerShell.Management\Split-Path -Path $_.Path -Leaf) -eq $script:NuGetExeName) -and - (-not $_.Path.StartsWith($env:windir, [System.StringComparison]::OrdinalIgnoreCase)) + (-not $_.Path.StartsWith($env:windir, [System.StringComparison]::OrdinalIgnoreCase)) } | Microsoft.PowerShell.Utility\Select-Object -First 1 if($nugetCmd -and $nugetCmd.Path) @@ -300,15 +300,15 @@ function Install-NuGetBinaries } else { if($script:IsWindows) { - $DotnetCommandPath = Join-Path -Path $env:LocalAppData -ChildPath Microsoft | + $DotnetCommandPath = Join-Path -Path $env:LocalAppData -ChildPath Microsoft | Join-Path -ChildPath dotnet | Join-Path -ChildPath dotnet.exe - if($DotnetCommandPath -and + if($DotnetCommandPath -and -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $DotnetCommandPath -PathType Leaf)) { $DotnetCommandPath = Join-Path -Path $env:ProgramFiles -ChildPath dotnet | Join-Path -ChildPath dotnet.exe } - } - else { + } + else { $DotnetCommandPath = '/usr/local/bin/dotnet' } @@ -330,9 +330,9 @@ function Remove-NuGetExe if (Microsoft.PowerShell.Management\Test-Path -Path $script:ApplocalDataExePath) { Remove-Item -Path $script:ApplocalDataExePath -Force -Confirm:$false -WhatIf:$false - } + } - $DotnetCmd = Microsoft.PowerShell.Core\Get-Command -Name 'dotnet.exe' -All -ErrorAction Ignore -WarningAction SilentlyContinue + $DotnetCmd = Microsoft.PowerShell.Core\Get-Command -Name 'dotnet.exe' -All -ErrorAction Ignore -WarningAction SilentlyContinue if ($DotnetCmd -and $DotnetCmd.path) { # Dotnet can be stored in multiple locations, so test each path @@ -348,7 +348,7 @@ function Remove-NuGetExe $script:NuGetExePath = $null if ($script:IsWindows) { - # Changes the environment so that dotnet and nuget files are temporarily removed + # Changes the environment so that dotnet and nuget files are temporarily removed $SourceLocations = Get-Command dotnet*, nuget* | ForEach-Object { if ($_.Source) { Split-Path -Path $_.Source -Parent @@ -367,7 +367,7 @@ function Remove-NuGetExe $PathElements = $currentValue -split ';' | Where-Object {$_ -and ($sourceLocations -notcontains $_.TrimEnd('\'))} & $psgetModule Set-EnvironmentVariable -Name 'PATH' -Value ($PathElements -join ';') -Target $script:EnvironmentVariableTarget.Process - } + } } } @@ -400,12 +400,12 @@ function CreateAndPublish-TestScript param( [Parameter(Mandatory=$true)] [string] - $Name, - + $Name, + [Parameter(Mandatory=$true)] [string] $NuGetApiKey, - + [Parameter(Mandatory=$true)] [string] $Repository, @@ -457,7 +457,7 @@ function CreateAndPublish-TestScript if($RequiredScripts) { $params['RequiredScripts'] = $RequiredScripts } if($ExternalModuleDependencies) { $params['ExternalModuleDependencies'] = $ExternalModuleDependencies } if($ExternalScriptDependencies) { $params['ExternalScriptDependencies'] = $ExternalScriptDependencies } - + New-ScriptFileInfo @params Add-Content -Path $scriptFilePath -Value @" @@ -485,12 +485,12 @@ function CreateAndPublishTestModule param( [Parameter(Mandatory=$true)] [string] - $ModuleName, - + $ModuleName, + [Parameter(Mandatory=$true)] [string] $NuGetApiKey, - + [Parameter(Mandatory=$true)] [string] $Repository, @@ -507,13 +507,13 @@ function CreateAndPublishTestModule [Parameter()] [string] - $ModulesPath = $script:TempPath + $ModulesPath = $script:TempPath ) $ModuleBase = Join-Path $ModulesPath $ModuleName $null = New-Item -Path $ModuleBase -ItemType Directory -Force - - # To create a module manifest for $ModuleName with some dependencies in NestedModules and RequiredModules, + + # To create a module manifest for $ModuleName with some dependencies in NestedModules and RequiredModules, # the dependency module should be available under one of the specified path in $env:PSModulePath. # Creating dummy module folders for them and will delete them after publishing the $ModuleName @@ -535,7 +535,7 @@ function CreateAndPublishTestModule $ModuleToBeAvailable_Version = "1.0" if($ModuleToBeAvailable.GetType().ToString() -eq 'System.Collections.Hashtable') - { + { $ModuleToBeAvailable_Name = $ModuleToBeAvailable.ModuleName if($ModuleToBeAvailable.Keys -Contains "RequiredVersion") @@ -567,15 +567,15 @@ function CreateAndPublishTestModule } Set-Content "$ModuleBase\$ModuleName.psm1" -Value "function Get-$ModuleName { Get-Date }" - + $NestedModules += "$ModuleName.psm1" - + try { foreach($version in $Versions) { $tags = @('Tag1','Tag2', "Tag-$ModuleName-$version") - + $exportedFunctions = '*' if($ModuleName -match "ModuleWithDependencies*") { @@ -618,9 +618,9 @@ function CreateAndPublishTestModule $params['Tags'] = $tags $params['LicenseUri'] = "https://$ModuleName.com/license" $params['IconUri'] = "https://$ModuleName.com/icon" - $params['ProjectUri'] = "https://$ModuleName.com" + $params['ProjectUri'] = "https://$ModuleName.com" } - + $null = Publish-Module @params } } @@ -634,15 +634,15 @@ function CreateAndPublishTestModule function PublishDscTestModule { [cmdletbinding()] - param( + param( [Parameter(Mandatory=$true)] [string] - $ModuleName, - + $ModuleName, + [Parameter(Mandatory=$true)] [string] $NuGetApiKey, - + [Parameter(Mandatory=$true)] [string] $Repository, @@ -662,28 +662,28 @@ function PublishDscTestModule Copy-Item -Path "$TestModulesBase\$ModuleName" -Destination $TempModulesPath -Recurse -Force $ModuleBase = Join-Path $TempModulesPath $ModuleName - # Create binary module - $content = @" - using System; - using System.Management.Automation; - namespace PSGetTestModule - { - [Cmdlet("Test","PSGetTestCmdlet")] - public class PSGetTestCmdlet : PSCmdlet - { - [Parameter] - public int a { - get; - set; - } - protected override void ProcessRecord() - { - String s = "Value is :" + a; - WriteObject(s); - } - } - } -"@ + # Create binary module + $content = @" + using System; + using System.Management.Automation; + namespace PSGetTestModule + { + [Cmdlet("Test","PSGetTestCmdlet")] + public class PSGetTestCmdlet : PSCmdlet + { + [Parameter] + public int a { + get; + set; + } + protected override void ProcessRecord() + { + String s = "Value is :" + a; + WriteObject(s); + } + } + } +"@ $assemblyName = "psgettestbinary_$(Get-Random).dll" $testBinaryPath = "$ModuleBase\$assemblyName" @@ -715,7 +715,7 @@ function PublishDscTestModule -NestedModules "$ModuleName.psm1",".\$assemblyName" ` -Description 'Temp Description KeyWord1 Keyword2 Keyword3' ` } - + $null = Publish-Module -Path $ModuleBase ` -NuGetApiKey $NuGetApiKey ` -Repository $Repository ` @@ -734,12 +734,12 @@ function CreateAndPublishTestModuleWithVersionFormat param( [Parameter(Mandatory=$true)] [string] - $ModuleName, - + $ModuleName, + [Parameter(Mandatory=$true)] [string] $NuGetApiKey, - + [Parameter(Mandatory=$true)] [string] $Repository, @@ -757,13 +757,13 @@ function CreateAndPublishTestModuleWithVersionFormat [string] $ModulesPath = $script:TempPath ) - + $repo = Get-PSRepository -Name $Repository -ErrorVariable err if($err) { Throw $err } - + $ModuleBase = Join-Path $ModulesPath $ModuleName if ($PSGetFormatVersion -eq '1.0') @@ -777,7 +777,7 @@ function CreateAndPublishTestModuleWithVersionFormat } $null = New-Item -Path $ModuleBase -ItemType Directory -Force - + Set-Content "$ModuleBase\$ModuleName.psm1" -Value "function Get-$ModuleName { Get-Date }" foreach($version in $Versions) @@ -854,7 +854,7 @@ function Publish-PSGetExtModule [Parameter()] [string[]] $Tags, - + [Parameter()] [Uri] $LicenseUri, @@ -862,7 +862,7 @@ function Publish-PSGetExtModule [Parameter()] [Uri] $IconUri, - + [Parameter()] [Uri] $ProjectUri @@ -870,34 +870,34 @@ function Publish-PSGetExtModule Install-NuGetBinaries - if($PSModuleInfo.PrivateData -and - ($PSModuleInfo.PrivateData.GetType().ToString() -eq "System.Collections.Hashtable") -and + if($PSModuleInfo.PrivateData -and + ($PSModuleInfo.PrivateData.GetType().ToString() -eq "System.Collections.Hashtable") -and $PSModuleInfo.PrivateData["PSData"] -and ($PSModuleInfo.PrivateData["PSData"].GetType().ToString() -eq "System.Collections.Hashtable") ) { if( -not $Tags -and $PSModuleInfo.PrivateData.PSData["Tags"]) - { + { $Tags = $PSModuleInfo.PrivateData.PSData.Tags } if( -not $ReleaseNotes -and $PSModuleInfo.PrivateData.PSData["ReleaseNotes"]) - { + { $ReleaseNotes = $PSModuleInfo.PrivateData.PSData.ReleaseNotes } if( -not $LicenseUri -and $PSModuleInfo.PrivateData.PSData["LicenseUri"]) - { + { $LicenseUri = $PSModuleInfo.PrivateData.PSData.LicenseUri } if( -not $IconUri -and $PSModuleInfo.PrivateData.PSData["IconUri"]) - { + { $IconUri = $PSModuleInfo.PrivateData.PSData.IconUri } if( -not $ProjectUri -and $PSModuleInfo.PrivateData.PSData["ProjectUri"]) - { + { $ProjectUri = $PSModuleInfo.PrivateData.PSData.ProjectUri } } @@ -944,7 +944,7 @@ function Publish-PSGetExtModule "@ -# When packaging we must build something. +# When packaging we must build something. # So, we are building an empty assembly called NotUsed, and discarding it. $CsprojContent = @" @@ -960,7 +960,7 @@ $CsprojContent = @" $csprojBasePath = $null try - { + { $NupkgPath = Join-Path -Path $NugetPackageRoot -ChildPath "$($PSModuleInfo.Name).$($PSModuleInfo.Version.ToString()).nupkg" if($script:DotnetCommandPath) { @@ -972,11 +972,11 @@ $CsprojContent = @" else { $NuspecPath = Join-Path -Path $NugetPackageRoot -ChildPath "$($PSModuleInfo.Name).nuspec" } - + # Remove existing nuspec and nupkg files Remove-Item $NupkgPath -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false Remove-Item $NuspecPath -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false - + Set-Content -Value $nuspec -Path $NuspecPath # Create .nupkg file @@ -996,7 +996,7 @@ $CsprojContent = @" if($LASTEXITCODE) { - $message = $LocalizedData.FailedToCreateCompressedModule -f ($output) + $message = $LocalizedData.FailedToCreateCompressedModule -f ($output) Write-Error -Message $message -ErrorId "FailedToCreateCompressedModule" -Category InvalidOperation return } @@ -1012,16 +1012,16 @@ $CsprojContent = @" $output = & $script:DotnetCommandPath $ArgumentList } else { - $output = & $script:NuGetExePath push $NupkgPath -source $Destination -NonInteractive -ApiKey $NugetApiKey + $output = & $script:NuGetExePath push $NupkgPath -source $Destination -NonInteractive -ApiKey $NugetApiKey } if($LASTEXITCODE) { - $message = $LocalizedData.FailedToPublish -f ($output) + $message = $LocalizedData.FailedToPublish -f ($output) Write-Error -Message $message -ErrorId "FailedToPublishTheModule" -Category InvalidOperation } else { - $message = $LocalizedData.PublishedSuccessfully -f ($PSModuleInfo.Name, $Destination) + $message = $LocalizedData.PublishedSuccessfully -f ($PSModuleInfo.Name, $Destination) Write-Verbose -Message $message } } @@ -1053,17 +1053,17 @@ function Get-EscapedString function Uninstall-Module { - Param( + Param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] - $Name + $Name ) - Get-Module $Name -ListAvailable | Foreach-Object { - - Remove-Module $_ -Force -ErrorAction SilentlyContinue; - + Get-Module $Name -ListAvailable | Foreach-Object { + + Remove-Module $_ -Force -ErrorAction SilentlyContinue; + # Check if the module got installed with SxS version feature on PS 5.0 or later. if($_.ModuleBase.EndsWith("$($_.Version)", [System.StringComparison]::OrdinalIgnoreCase)) { @@ -1079,7 +1079,7 @@ function Uninstall-Module function RemoveItem { - Param( + Param( [string] $path ) @@ -1092,7 +1092,7 @@ function RemoveItem function Set-PSGallerySourceLocation { - Param( + Param( [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string] @@ -1126,7 +1126,7 @@ function Set-PSGallerySourceLocation } else { $PSGetModuleSources = [ordered]@{} } - + $moduleSource = New-Object PSCustomObject -Property ([ordered]@{ Name = $Name SourceLocation = $Location @@ -1142,7 +1142,7 @@ function Set-PSGallerySourceLocation $moduleSource.PSTypeNames.Insert(0, "Microsoft.PowerShell.Commands.PSRepository") $PSGetModuleSources.Add($Name, $moduleSource) - + if(-not (Test-Path $script:PSGetAppLocalPath)) { $null = New-Item -Path $script:PSGetAppLocalPath ` @@ -1156,7 +1156,7 @@ function Set-PSGallerySourceLocation Export-Clixml -InputObject $PSGetModuleSources ` -Path $script:moduleSourcesFilePath ` -Force -Confirm:$false -WhatIf:$false - + $null = Import-PackageProvider -Name PowerShellGet -Force } @@ -1201,10 +1201,10 @@ function Reset-PATHVariableForScriptsInstallLocation $scopePath = $script:MyDocumentsScriptsPath $target = $script:EnvironmentVariableTarget.User } - + $scopePathEndingWithBackSlash = "$scopePath\" $psgetModule = Import-Module -Name PowerShellGet -PassThru -Scope Local -Verbose:$VerbosePreference - + if(-not $OnlyProcessPathVariable) { # Scope specific PATH @@ -1253,10 +1253,10 @@ function Reset-PATHVariableForScriptsInstallLocation $currentValue = & $psgetModule Get-EnvironmentVariable -Name 'PATH' -Target $target $pathsInCurrentValue = ($currentValue -split ';') | Where-Object {$_} - if (($pathsInCurrentValue -contains $scopePath) -or + if (($pathsInCurrentValue -contains $scopePath) -or ($pathsInCurrentValue -contains $scopePathEndingWithBackSlash)) { - $pathsInCurrentValueAfterRemovingScopePath = $pathsInCurrentValue | Where-Object { + $pathsInCurrentValueAfterRemovingScopePath = $pathsInCurrentValue | Where-Object { ($_ -ne $scopePath) -and ($_ -ne $scopePathEndingWithBackSlash) } @@ -1316,12 +1316,12 @@ function Set-PATHVariableForScriptsInstallLocation if((($currentPATHValue -split ';') -notcontains $scopePath) -and (($currentPATHValue -split ';') -notcontains $scopePathEndingWithBackSlash)) { - # To ensure that the installed script is immediately usable, + # To ensure that the installed script is immediately usable, # we need to add the scope path to the PATH enviroment variable. & $psgetModule Set-EnvironmentVariable -Name 'PATH' ` -Value "$currentPATHValue;$scopePath" ` -Target $envVariableTarget - + $AddedToPath = $true } } @@ -1335,12 +1335,12 @@ function Set-PATHVariableForScriptsInstallLocation if((($currentPATHValue -split ';') -notcontains $scopePath) -and (($currentPATHValue -split ';') -notcontains $scopePathEndingWithBackSlash)) { - # To ensure that the installed script is immediately usable, + # To ensure that the installed script is immediately usable, # we need to add the scope path to the PATH enviroment variable. & $psgetModule Set-EnvironmentVariable -Name 'PATH' ` -Value "$currentPATHValue;$scopePath" ` -Target $target - + $AddedToPath = $true } } @@ -1356,9 +1356,9 @@ function Get-CodeSigningCert ) $cert = $null; - $scriptName = Join-Path $script:TempPath "$([IO.Path]::GetRandomFileName()).ps1" - "get-date" >$scriptName - $cert = @(get-childitem cert:\CurrentUser\My -codesigning | Where-Object {(Set-AuthenticodeSignature $scriptName -cert $_).Status -eq "Valid"})[0]; + $scriptName = Join-Path $script:TempPath "$([IO.Path]::GetRandomFileName()).ps1" + "get-date" >$scriptName + $cert = @(get-childitem cert:\CurrentUser\My -codesigning | Where-Object {(Set-AuthenticodeSignature $scriptName -cert $_).Status -eq "Valid"})[0]; if ((-not $cert) -and $IncludeLocalMachineCerts) { $cert = @(get-childitem cert:\LocalMachine\My -codesigning | Where-Object {(Set-AuthenticodeSignature $scriptName -cert $_).Status -eq "Valid"})[0]; } @@ -1427,7 +1427,7 @@ ValidityPeriodUnits = "1" %szOID_BASIC_CONSTRAINTS% = "{text}ca=1&pathlength=0" Critical = %szOID_BASIC_CONSTRAINTS% "@ - $certInf | out-file ca.inf -force + $certInf | out-file ca.inf -force Cleanup-CACert -CACert $CACert certreq -new .\ca.inf ca.cer $mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText @@ -1467,7 +1467,7 @@ function Create-CodeSigningCert [string] $CertRA = "PSCatalog Test Root Authority" ) - + if (!(Test-Path $storeName)) { New-Item $storeName -Verbose -Force @@ -1509,7 +1509,7 @@ ValidityPeriodUnits = "1" %szOID_BASIC_CONSTRAINTS% = "{text}ca=0" %szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_CODE_SIGNING%" "@ - $certInf | out-file signing.inf -force + $certInf | out-file signing.inf -force [void](Cleanup-CodeSigningCert -Subject $subject) Create-CACert -CACert $CertRA certreq -new -q -cert $CertRA .\signing.inf signing.cer @@ -1529,7 +1529,7 @@ function Get-LocalModulePath() { # Ensure the local directory is at the front of the psmodulepath so that we test that instead of some other version on the system function Add-LocalTreeInPSModulePath() { # we are in repo\tests, module is in repo\src\PowerShellGet - # add repo\src to $psmodulepath so PowerShellGet is found + # add repo\src to $psmodulepath so PowerShellGet is found $modulepath= Get-LocalModulePath Write-Verbose "Ensure we load PowerShellGet from $modulepath" @@ -1568,7 +1568,7 @@ function Set-TestEnvironment() { $expectedProviderPath = Join-Path -Path $expectedModuleBase -ChildPath "PSModule.psm1" Write-Verbose "Ensure we load PowerShellGet from $expectedModuleBase" - + $psgetmodule = Import-Module -Name PowerShellGet -PassThru -Scope Global -Force if($psgetmodule.ModuleBase -ne $expectedModuleBase) { Write-Warning "Loading PowerShellGet from $($psgetmodule.ModuleBase), but the PowerShellGet under development is in $expectedModuleBase." @@ -1576,7 +1576,7 @@ function Set-TestEnvironment() { Write-Verbose "Ensure we load PowerShellGet Provider from $expectedModuleBase" $psgetprovider = Import-PackageProvider -Name PowerShellGet -Force - + if($psgetprovider.ProviderPath -ne $expectedProviderPath) { Write-Warning "Loading PowerShellGet Package Provider from $($psgetprovider.ProviderPath), but the PowerShellGet under development is in $expectedModuleBase." } @@ -1610,4 +1610,27 @@ function Set-TestEnvironment() { function Remove-TestEnvironment { Remove-LocalTreeInPSModulePath -} \ No newline at end of file +} + +function Invoke-WithoutAdminPrivileges +{ + [CmdletBinding()] + param($commandLine) + + $tempFile = "$env:temp\$(New-Guid).txt" + $errFile = "$env:temp\$(New-Guid)-err.txt" + $wrappedCommandLine = "& { $commandLine } > $tempFile 2> $errFile" + Write-Verbose "Executing $wrappedCommandLine" + $bytes = [System.Text.Encoding]::Unicode.GetBytes($wrappedCommandLine) + $encodedCommand = [Convert]::ToBase64String($bytes) + + $processName = (get-process -Id $pid).ProcessName + Start-Process "runas.exe" -ArgumentList ("/trustlevel:0x20000", "`"$processName -encodedcommand $encodedcommand`"") -Wait + Get-Content $tempFile + $errors = Get-Content $errFile + if($errors) { + Write-Error "Errors from child command: $errors" + } + Remove-Item $tempFile -Force -ErrorAction SilentlyContinue + Remove-Item $errFile -Force -ErrorAction SilentlyContinue +} diff --git a/Tests/PSGetUpdateModule.Tests.ps1 b/Tests/PSGetUpdateModule.Tests.ps1 index f6224a72..8bf09008 100644 --- a/Tests/PSGetUpdateModule.Tests.ps1 +++ b/Tests/PSGetUpdateModule.Tests.ps1 @@ -40,15 +40,6 @@ function SuiteSetup { PSGetTestUtils\Uninstall-Module ContosoServer PSGetTestUtils\Uninstall-Module ContosoClient - if($PSEdition -ne 'Core') - { - $script:userName = "PSGetUser" - $password = "Password1" - $null = net user $script:userName $password /add - $secstr = ConvertTo-SecureString $password -AsPlainText -Force - $script:credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $script:userName, $secstr - } - $script:assertTimeOutms = 20000 } function SuiteCleanup { @@ -63,17 +54,73 @@ function SuiteCleanup { # Import the PowerShellGet provider to reload the repositories. $null = Import-PackageProvider -Name PowerShellGet -Force +} - if($PSEdition -ne 'Core') - { - # Delete the user - net user $script:UserName /delete | Out-Null - # Delete the user profile - $userProfile = (Get-WmiObject -Class Win32_UserProfile | Where-Object {$_.LocalPath -match $script:UserName}) - if($userProfile) - { - RemoveItem $userProfile.LocalPath - } +Describe UpdateModuleFromAlternateRepo -Tags 'BVT' { + BeforeAll { + SuiteSetup + } + + AfterAll { + SuiteCleanup + } + + AfterEach { + PSGetTestUtils\Uninstall-Module ContosoServer + PSGetTestUtils\Uninstall-Module ContosoClient + } + + It "Check that removing a slash from a repo doesn't break update" { + $withSlash = "https://www.poshtestgallery.com/api/v2/" + $noSlash = "https://www.poshtestgallery.com/api/v2" + #Write-Host (Get-PSRepository | Out-String) + (Get-PSRepository PSGallery).SourceLocation | Should Be $withSlash + + Install-Module ContosoServer -RequiredVersion 1.0 + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $withSlash + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + + # now update where PSGallery Source Location is + Set-PSGallerySourceLocation -Location $noSlash + #Write-Host (Get-PSRepository | Out-String) + (Get-PSRepository PSGallery).SourceLocation | Should Be $noSlash + + # reload powershellget to force-update cached repository info + Import-Module PowerShellGet -Force + + # now try and update module isntalled using other SourceLocation + Update-Module ContosoServer -RequiredVersion 2.0 -ErrorAction Stop + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $noSlash + (Get-InstalledModule ContosoServer).Version | Should Be 2.0 + } + + It "Check that adding a slash to a repo doesn't break update" { + $withSlash = "https://www.poshtestgallery.com/api/v2/" + $noSlash = "https://www.poshtestgallery.com/api/v2" + #Write-Host (Get-PSRepository | Out-String) + + Set-PSGallerySourceLocation -Location $noSlash + + (Get-PSRepository PSGallery).SourceLocation | Should Be $noSlash + + Install-Module ContosoServer -RequiredVersion 1.0 + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $noSlash + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + + # now update where PSGallery Source Location is + Set-PSGallerySourceLocation -Location $withSlash + #Write-Host (Get-PSRepository | Out-String) + (Get-PSRepository PSGallery).SourceLocation | Should Be $withSlash + + # reload powershellget to force-update cached repository info + Import-Module PowerShellGet -Force + + # now try and update module isntalled using other SourceLocation + Update-Module ContosoServer -RequiredVersion 2.0 -ErrorAction Stop + #Write-Host (Get-InstalledModule ContosoServer -AllVersions | Format-List | Out-String) + (Get-InstalledModule ContosoServer).RepositorySourceLocation | Should Be $withSlash + (Get-InstalledModule ContosoServer).Version | Should Be 2.0 } } @@ -402,7 +449,9 @@ Describe PowerShell.PSGet.UpdateModuleTests -Tags 'BVT','InnerLoop' { It "UpdateAllModules" { Install-Module ContosoClient -RequiredVersion 1.0 Install-Module ContosoServer -RequiredVersion 1.0 - Update-Module + Update-Module -ErrorVariable err -ErrorAction SilentlyContinue + #if we have other modules not from test repo they will error, keep the noise down but complain about real problems + $err | ? { $_.FullyQualifiedErrorId -notmatch "SourceNotFound" } | % { Write-Error $_ } if(Test-ModuleSxSVersionSupport) { @@ -648,22 +697,17 @@ Describe PowerShell.PSGet.UpdateModuleTests.P2 -Tags 'P2','OuterLoop' { # # Action: Install a module as admin and try to update it as non-admin user # - # Expected Result: should fail with an error + # Expected Result: should successfully save module to currentuser scope # - It "AdminPrivilegesAreRequiredForUpdatingAllUsersModule" { + It "AdminPrivilegesAreNotRequiredForUpdatingAllUsersModule" { Install-Module -Name ContosoServer -RequiredVersion 1.0 - $NonAdminConsoleOutput = Join-Path ([System.IO.Path]::GetTempPath()) 'nonadminconsole-out.txt' - Start-Process "$PSHOME\PowerShell.exe" -ArgumentList '$null = Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser; - $null = Import-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; - Update-Module -Name ContosoServer' ` - -Credential $script:credential ` - -Wait ` - -WorkingDirectory $PSHOME ` - -RedirectStandardOutput $NonAdminConsoleOutput - waitFor {Test-Path $NonAdminConsoleOutput} -timeoutInMilliseconds $script:assertTimeOutms -exceptionMessage "Install-Module on non-admin console failed to complete" - $content = Get-Content $NonAdminConsoleOutput - Assert ($content -match "AdminPrivilegesAreRequiredForUpdate") "update-module should fail when non-admin user is trying to update a module installed to alluser scope, $content" - RemoveItem $NonAdminConsoleOutput + $content = Invoke-WithoutAdminPrivileges (@' +Import-Module "{0}\PowerShellGet.psd1" -Force +Update-Module -Name ContosoServer +'@ -f (Get-Module PowerShellGet).ModuleBase) + + $updatedModule = Get-InstalledModule ContosoServer + Assert ($updatedModule.Version -gt 1.0) "Module wasn't updated" } ` -Skip:$( $whoamiValue = (whoami) @@ -672,7 +716,6 @@ Describe PowerShell.PSGet.UpdateModuleTests.P2 -Tags 'P2','OuterLoop' { ($whoamiValue -eq "NT AUTHORITY\LOCAL SERVICE") -or ($whoamiValue -eq "NT AUTHORITY\NETWORK SERVICE") -or ($env:APPVEYOR_TEST_PASS -eq 'True') -or - ($PSEdition -eq 'Core') -or ($PSVersionTable.PSVersion -lt '4.0.0') ) diff --git a/Tests/PSGetUpdateScript.Tests.ps1 b/Tests/PSGetUpdateScript.Tests.ps1 index 218cbb93..4c9f63d9 100644 --- a/Tests/PSGetUpdateScript.Tests.ps1 +++ b/Tests/PSGetUpdateScript.Tests.ps1 @@ -18,8 +18,8 @@ function SuiteSetup { Import-Module "$PSScriptRoot\PSGetTestUtils.psm1" -WarningAction SilentlyContinue Import-Module "$PSScriptRoot\Asserts.psm1" -WarningAction SilentlyContinue - $script:ProgramFilesScriptsPath = Get-AllUsersScriptsPath - $script:MyDocumentsScriptsPath = Get-CurrentUserScriptsPath + $script:ProgramFilesScriptsPath = Get-AllUsersScriptsPath + $script:MyDocumentsScriptsPath = Get-CurrentUserScriptsPath $script:PSGetLocalAppDataPath = Get-PSGetLocalAppDataPath $script:TempPath = Get-TempPath @@ -41,17 +41,6 @@ function SuiteSetup { Get-InstalledScript -Name Fabrikam-ServerScript -ErrorAction SilentlyContinue | Uninstall-Script -Force Get-InstalledScript -Name Fabrikam-ClientScript -ErrorAction SilentlyContinue | Uninstall-Script -Force - if($PSEdition -ne 'Core') - { - $script:userName = "PSGetUser" - $password = "Password1" - $null = net user $script:userName $password /add - $secstr = ConvertTo-SecureString $password -AsPlainText -Force - $script:credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $script:userName, $secstr - } - - $script:assertTimeOutms = 20000 - # Create temp folder for saving the scripts $script:TempSavePath = Join-Path -Path $script:TempPath -ChildPath "PSGet_$(Get-Random)" $null = New-Item -Path $script:TempSavePath -ItemType Directory -Force @@ -73,17 +62,6 @@ function SuiteCleanup { # Import the PowerShellGet provider to reload the repositories. $null = Import-PackageProvider -Name PowerShellGet -Force - if($PSEdition -ne 'Core') - { - # Delete the user - net user $script:UserName /delete | Out-Null - # Delete the user profile - $userProfile = (Get-WmiObject -Class Win32_UserProfile | Where-Object {$_.LocalPath -match $script:UserName}) - if($userProfile) - { - RemoveItem $userProfile.LocalPath - } - } RemoveItem $script:TempSavePath @@ -343,7 +321,7 @@ Describe PowerShell.PSGet.UpdateScriptTests -Tags 'BVT','InnerLoop' { $DateTimeBeforeUpdate = Get-Date Update-Script Fabrikam-* -Force -ErrorVariable MyError Assert ($MyError.Count -eq 0) "There should not be any error when updating multiple scripts with wildcard in name, $MyError" - + $res = Get-InstalledScript -Name Fabrikam-ServerScript -MinimumVersion '1.1' Assert ($res -and ($res.Name -eq "Fabrikam-ServerScript") -and ($res.Version -gt [Version]"1.0")) "Update-Script should update when wildcard specified in name" @@ -415,33 +393,25 @@ Describe PowerShell.PSGet.UpdateScriptTests -Tags 'BVT','InnerLoop' { # # Action: Install a script as admin and try to update it as non-admin user # - # Expected Result: should fail with an error + # Expected Result: should pass, installing the update in currentuser scope # - It "AdminPrivilegesAreRequiredForUpdatingAllUsersScript" { + It "AdminPrivilegesAreNotRequiredForUpdatingAllUsersScript" { Install-Script -Name Fabrikam-ServerScript -RequiredVersion 1.0 -Scope AllUsers - $NonAdminConsoleOutput = Join-Path ([System.IO.Path]::GetTempPath()) 'nonadminconsole-out.txt' - Start-Process "$PSHOME\PowerShell.exe" -ArgumentList '$null = Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser; - $null = Import-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; - Update-Script -Name Fabrikam-ServerScript' ` - -Credential $script:credential ` - -Wait ` - -WorkingDirectory $PSHOME ` - -RedirectStandardOutput $NonAdminConsoleOutput - - waitFor {Test-Path $NonAdminConsoleOutput} -timeoutInMilliseconds $script:assertTimeOutms -exceptionMessage "Install-Script on non-admin console failed to complete" - $content = Get-Content $NonAdminConsoleOutput - Assert ($content -match 'AdminPrivilegesAreRequiredForUpdate') "Update-Script should fail when non-admin user is trying to update a script installed to allusers scope, $content" - RemoveItem $NonAdminConsoleOutput - } ` + $content = Invoke-WithoutAdminPrivileges (@' + Import-Module "{0}\PowerShellGet.psd1" -Force -Passthru | select ModuleBase + Update-Script -Name Fabrikam-ServerScript +'@ -f (Get-Module PowerShellGet).ModuleBase) + + $updatedScript = Get-InstalledScript Fabrikam-ServerScript + Assert ($updatedScript.Version -gt 1.0) "Update-Script failed to updated script running as non-admin: $content" + } ` -Skip:$( $whoamiValue = (whoami) - ($whoamiValue -eq "NT AUTHORITY\SYSTEM") -or ($whoamiValue -eq "NT AUTHORITY\LOCAL SERVICE") -or ($whoamiValue -eq "NT AUTHORITY\NETWORK SERVICE") -or ($env:APPVEYOR_TEST_PASS -eq 'True') -or - ($PSEdition -eq 'Core') -or - ($PSVersionTable.PSVersion -lt '4.0.0') + ($PSVersionTable.PSVersion -lt '4.0.0') ) # Purpose: UpdateScriptWithLowerReqVersionShouldNotUpdate @@ -451,7 +421,7 @@ Describe PowerShell.PSGet.UpdateScriptTests -Tags 'BVT','InnerLoop' { # Expected Result: Script should not be downgraded to 1.0 # It "UpdateScriptWithLowerReqVersionShouldNotUpdate" { - Install-Script Fabrikam-ServerScript + Install-Script Fabrikam-ServerScript -Force Update-Script Fabrikam-ServerScript -RequiredVersion 1.0 $res = Get-InstalledScript Fabrikam-ServerScript Assert ($res.Name -eq "Fabrikam-ServerScript" -and $res.Version -gt [Version]"1.0") "Update-Script should not downgrade the script version with -RequiredVersion, Name: $($res.Name), Version: $($res.Version)" @@ -483,7 +453,8 @@ Describe PowerShell.PSGet.UpdateScriptTests -Tags 'BVT','InnerLoop' { It "UpdateAllScripts" { Install-Script Fabrikam-ClientScript -RequiredVersion 1.0 Install-Script Fabrikam-ServerScript -RequiredVersion 1.0 - Update-Script + Update-Script -ErrorAction SilentlyContinue -ErrorVariable err + $err | ? { $_.FullyQualifiedErrorId -notmatch "NoMatchFoundForCriteria"} | % { Write-Error $_ } $res = Get-InstalledScript -Name Fabrikam-ServerScript,Fabrikam-ClientScript Assert (($res.Count -eq 2) -and ($res[0].Version -gt [Version]"1.0") -and ($res[1].Version -gt [Version]"1.0")) "Multiple script should be updated" @@ -498,11 +469,11 @@ Describe PowerShell.PSGet.UpdateScriptTests -Tags 'BVT','InnerLoop' { # It "UpdateMultipleScriptsWithForce" { Install-Script Fabrikam-ClientScript,Fabrikam-ServerScript - - $MyError = $null + + $MyError = $null Update-Script Fabrikam-ClientScript,Fabrikam-ServerScript -Force -ErrorVariable MyError Assert ($MyError.Count -eq 0) "There should not be any error from force update for multiple scripts, $MyError" - + $res = Get-InstalledScript Fabrikam-ServerScript Assert (($res.Name -eq 'Fabrikam-ServerScript') -and ($res.Version -gt [Version]"1.0")) "Update-Script should update when multiple scripts are specified" @@ -518,6 +489,7 @@ Describe PowerShell.PSGet.UpdateScriptTests -Tags 'BVT','InnerLoop' { # It "UpdateScriptUnderCurrentUserScope" { $scriptName = 'Fabrikam-ServerScript' + Install-Script $scriptName -Scope CurrentUser -RequiredVersion 1.0 Update-Script $scriptName @@ -531,17 +503,22 @@ Describe PowerShell.PSGet.UpdateScriptTests -Tags 'BVT','InnerLoop' { # # Action: Install a script with AllUsers scope then update it # - # Expected Result: updated script should be under AllUsers windows powershell scripts folder + # Expected Result: updated script should be under AllUsers windows powershell scripts folder for an admin on Windows Powershell, currentuser otherwise # It "UpdateScriptUnderAllUsersScope" { $scriptName = 'Fabrikam-ServerScript' + $shouldBeInAllUsers = ($PSVersionTable.PSVersion -lt "5.0" -or $PSEdition -eq 'Desktop') # when running these tests we always need to be an admin Install-Script $scriptName -Scope AllUsers -RequiredVersion 1.0 Update-Script $scriptName $res = Get-InstalledScript $scriptName - Assert (($res.Name -eq $scriptName) -and ($res.Version -gt [Version]"1.0")) "Update-Script should update the script installed to current user scope, $res" - AssertEquals $res.InstalledLocation $script:ProgramFilesScriptsPath "Update-Script should update the script installed to current user scope, updated script base: $($res.InstalledLocation)" + Assert (($res.Name -eq $scriptName) -and ($res.Version -gt [Version]"1.0")) "Update-Script should update the script, $res" + if ($shouldBeInAllUsers) { + AssertEquals $res.InstalledLocation $script:ProgramFilesScriptsPath "Update-Script should put update in all users scope, but updated script base: $($res.InstalledLocation)" + } else { + AssertEquals $res.InstalledLocation $script:MyDocumentsScriptsPath "Update-Script should put update in current user scope, updated script base: $($res.InstalledLocation)" + } } } @@ -578,11 +555,11 @@ Describe PowerShell.PSGet.UpdateScriptTests.P1 -Tags 'P1','OuterLoop' { $DepencyNames = $res1.Dependencies.Name $res2 = Find-Script -Name $ScriptName -IncludeDependencies -MaximumVersion "1.0" -MinimumVersion "0.1" Assert ($res2.Count -ge ($DepencyNames.Count+1)) "Find-Script with -IncludeDependencies returned wrong results, $res2" - + Install-Script -Name $ScriptName -MaximumVersion "1.0" -MinimumVersion "0.1" $ActualScriptDetails = Get-InstalledScript -Name $ScriptName -RequiredVersion $res1.Version AssertNotNull $ActualScriptDetails "$ScriptName script with dependencies is not installed properly" - + $NamesToUninstall += $res2.Name $res2 | ForEach-Object { @@ -595,7 +572,7 @@ Describe PowerShell.PSGet.UpdateScriptTests.P1 -Tags 'P1','OuterLoop' { # Find the latest available version $res3 = Find-Script -Name $ScriptName -IncludeDependencies - + Update-Script -Name $ScriptName $NamesToUninstall += $res3.Name @@ -616,5 +593,5 @@ Describe PowerShell.PSGet.UpdateScriptTests.P1 -Tags 'P1','OuterLoop' { PowerShellGet\Uninstall-Module $_ -Force -ErrorAction SilentlyContinue } } - } -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') + } -Skip:$($PSVersionTable.PSVersion -lt '5.0.0') } diff --git a/Tests/Pester.PrereleaseVersion.Tests.ps1 b/Tests/Pester.PrereleaseVersion.Tests.ps1 index 1d2d05d9..6d4abb4c 100644 --- a/Tests/Pester.PrereleaseVersion.Tests.ps1 +++ b/Tests/Pester.PrereleaseVersion.Tests.ps1 @@ -5,7 +5,7 @@ function RegisterTestRepository { $testRepoRegistered = Get-PSRepository -Name $TestRepositoryName -ErrorAction SilentlyContinue if (-not $testRepoRegistered) { Register-PSRepository -Name $TestRepositoryName -SourceLocation $TestRepositorySource -InstallationPolicy Trusted - + $testRepoRegistered = Get-PSRepository -Name $TestRepositoryName if (-not $testRepoRegistered) @@ -37,7 +37,7 @@ $script:AddedCurrentUserInstallPath = Set-PATHVariableForScriptsInstallLocation $script:ProgramFilesModulesPath = Get-AllUsersModulesPath $script:MyDocumentsModulesPath = Get-CurrentUserModulesPath $null = New-Item -Path $script:MyDocumentsModulesPath -ItemType Directory -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -$script:ProgramFilesScriptsPath = Get-AllUsersScriptsPath +$script:ProgramFilesScriptsPath = Get-AllUsersScriptsPath $script:MyDocumentsScriptsPath = Get-CurrentUserScriptsPath $script:PSGetLocalAppDataPath = Get-PSGetLocalAppDataPath @@ -116,7 +116,7 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { $TypesToProcess = "types","typesTwo" $FormatsToPorcess = "formats","formatsTwo" $RequiredAssemblies = "system.management.automation" - $ModuleList = 'Microsoft.PowerShell.Management', + $ModuleList = 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility' $FunctionsToExport = "function1","function2" $AliasesToExport = "alias1","alias2" @@ -162,19 +162,19 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { if(($PSVersionTable.PSVersion -ge '3.0.0') -or ($PSVersionTable.Version -le '4.0.0')) { - New-ModuleManifest -path $script:testManifestPath -Confirm:$false + New-ModuleManifest -path $script:testManifestPath -Confirm:$false Update-ModuleManifest @ParamsV3 -Confirm:$false } elseif($PSVersionTable.PSVersion -ge '5.0.0') { - New-ModuleManifest -path $script:testManifestPath -Confirm:$false + New-ModuleManifest -path $script:testManifestPath -Confirm:$false Update-ModuleManifest @ParamsV5 -Confirm:$false } $newModuleInfo = Test-ModuleManifest -Path $script:testManifestPath - $newModuleInfo.Guid | Should Be $Guid + $newModuleInfo.Guid | Should Be $Guid $newModuleInfo.Author | Should Be $Author $newModuleInfo.CompanyName | Should Be $CompanyName $newModuleInfo.CopyRight | Should Be $CopyRight @@ -200,21 +200,21 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { { ($newModuleInfo.Tags -contains $Tags[0]) | Should Be "True" ($newModuleInfo.Tags -contains $Tags[1]) | Should Be "True" - $newModuleInfo.ProjectUri | Should Be $ProjectUri + $newModuleInfo.ProjectUri | Should Be $ProjectUri $newModuleInfo.LicenseUri | Should Be $LicenseUri - $newModuleInfo.IconUri | Should Be $IconUri - $newModuleInfo.ReleaseNotes | Should Be $ReleaseNotes + $newModuleInfo.IconUri | Should Be $IconUri + $newModuleInfo.ReleaseNotes | Should Be $ReleaseNotes $newModuleInfo.PrivateData.PSData.Prerelease | Should Be $Prerelease } - + $newModuleInfo.HelpInfoUri | Should Be $HelpInfoURI ($newModuleInfo.PrivateData.PSData.ExternalModuleDependencies -contains $ExternalModuleDependencies[0]) | Should Be "True" ($newModuleInfo.PrivateData.PSData.ExternalModuleDependencies -contains $ExternalModuleDependencies[1]) | Should Be "True" } ` - -Skip:$($IsWindows -eq $False) + -Skip:$($IsWindows -eq $False) It UpdateModuleManifestWithInvalidPrereleaseString { - $Prerelease = "alpha+001" + $Prerelease = "alpha+001" $Version = "3.2.1" $expectedErrorMessage = $LocalizedData.InvalidCharactersInPrereleaseString -f $Prerelease @@ -226,10 +226,10 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { } $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId - } + } It UpdateModuleManifestWithInvalidPrereleaseString2 { - $Prerelease = "alpha-beta.01" + $Prerelease = "alpha-beta.01" $Version = "3.2.1" $expectedErrorMessage = $LocalizedData.InvalidCharactersInPrereleaseString -f $Prerelease @@ -241,10 +241,10 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { } $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId - } + } It UpdateModuleManifestWithInvalidPrereleaseString3 { - $Prerelease = "alpha.1" + $Prerelease = "alpha.1" $Version = "3.2.1" $expectedErrorMessage = $LocalizedData.InvalidCharactersInPrereleaseString -f $Prerelease @@ -256,10 +256,10 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { } $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId - } + } It UpdateModuleManifestWithInvalidPrereleaseString4 { - $Prerelease = "error.0.0.0.1" + $Prerelease = "error.0.0.0.1" $Version = "3.2.1" $expectedErrorMessage = $LocalizedData.InvalidCharactersInPrereleaseString -f $Prerelease @@ -271,10 +271,10 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { } $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId - } + } It UpdateModuleManifestWithPrereleaseStringAndShortModuleVersion { - $Prerelease = "alpha001" + $Prerelease = "alpha001" $Version = "3.2" $expectedErrorMessage = $LocalizedData.IncorrectVersionPartsCountForPrereleaseStringUsage -f $Version @@ -286,10 +286,10 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { } $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId - } - + } + It UpdateModuleManifestWithPrereleaseStringAndLongModuleVersion { - $Prerelease = "alpha001" + $Prerelease = "alpha001" $Version = "3.2.1.1" $expectedErrorMessage = $LocalizedData.IncorrectVersionPartsCountForPrereleaseStringUsage -f $Version @@ -301,26 +301,26 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { } $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId - } + } It UpdateModuleManifestWithValidPrereleaseAndModuleVersion { - $Prerelease = "alpha001" + $Prerelease = "alpha001" $Version = "3.2.1" - New-ModuleManifest -path $script:testManifestPath + New-ModuleManifest -path $script:testManifestPath Update-ModuleManifest -Path $script:testManifestPath -Prerelease $Prerelease -ModuleVersion $Version -Confirm:$false $newModuleInfo = Test-ModuleManifest -Path $script:testManifestPath $newModuleInfo.Version | Should -Match $Version $newModuleInfo.PrivateData.PSData.Prerelease | Should -Match $Prerelease - } + } It UpdateModuleManifestWithValidPrereleaseAndModuleVersion2 { - $Prerelease = "gamma001" + $Prerelease = "gamma001" $Version = "3.2.1" - New-ModuleManifest -path $script:testManifestPath + New-ModuleManifest -path $script:testManifestPath Update-ModuleManifest -Path $script:testManifestPath -Prerelease "-$Prerelease" -ModuleVersion $Version -Confirm:$false $newModuleInfo = Test-ModuleManifest -Path $script:testManifestPath @@ -331,19 +331,19 @@ Describe "--- Update-ModuleManifest ---" -Tags 'Module','BVT','InnerLoop' { } Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { - # Not executing these tests on MacOS as + # Not executing these tests on MacOS as # the total execution time is exceeding allowed 50 min in TravisCI daily builds. if($IsMacOS) { return } - + BeforeAll { # Create file-based repository from scratch $script:PSGalleryRepoPath = Join-Path -Path $script:TempPath -ChildPath 'PSGalleryRepo' RemoveItem $script:PSGalleryRepoPath $null = New-Item -Path $script:PSGalleryRepoPath -ItemType Directory -Force - + # Backup existing repositories config file $script:moduleSourcesFilePath= Join-Path $script:PSGetLocalAppDataPath "PSRepositories.xml" $script:moduleSourcesBackupFilePath = Join-Path $script:PSGetLocalAppDataPath "PSRepositories.xml_$(get-random)_backup" @@ -351,16 +351,16 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { { Rename-Item $script:moduleSourcesFilePath $script:moduleSourcesBackupFilePath -Force } - + # Set file-based repo as default PSGallery repo Set-PSGallerySourceLocation -Location $script:PSGalleryRepoPath -PublishLocation $script:PSGalleryRepoPath - + $modSource = Get-PSRepository -Name "PSGallery" $modSource.SourceLocation | Should Be $script:PSGalleryRepoPath - $modSource.PublishLocation | Should Be $script:PSGalleryRepoPath - + $modSource.PublishLocation | Should Be $script:PSGalleryRepoPath + $script:ApiKey="TestPSGalleryApiKey" - + # Create temp module to be published $script:TempModulesPath = Join-Path -Path $script:TempPath -ChildPath "PSGet_$(Get-Random)" $null = New-Item -Path $script:TempModulesPath -ItemType Directory -Force @@ -379,10 +379,10 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { { RemoveItem $script:moduleSourcesFilePath } - + # Import the PowerShellGet provider to reload the repositories. $null = Import-PackageProvider -Name PowerShellGet -Force - + RemoveItem $script:PSGalleryRepoPath RemoveItem $script:TempModulesPath } @@ -397,11 +397,11 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { RemoveItem "$script:PublishModuleBase\*" } - + It "PublishModuleSameVersionHigherPrerelease" { $version = "1.0.0" $prerelease = "-alpha001" - + New-ModuleManifest -Path $script:PublishModuleNamePSD1FilePath -ModuleVersion $version -Description "$script:PublishModuleName module" -NestedModules "$script:PublishModuleName.psm1" Update-ModuleManifest -Path $script:PublishModuleNamePSD1FilePath -Prerelease $prerelease @@ -409,7 +409,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { Copy-Item $script:PublishModuleBase $script:ProgramFilesModulesPath -Recurse -Force Publish-Module -Path $script:PublishModuleBase -NuGetApiKey $script:ApiKey -ReleaseNotes "$script:PublishModuleName release notes" -Tags PSGet -LicenseUri "http://$script:PublishModuleName.com/license" -ProjectUri "http://$script:PublishModuleName.com" -WarningAction SilentlyContinue - + $psgetItemInfo = Find-Module -Name $script:PublishModuleName -RequiredVersion $($version + $prerelease) -AllowPrerelease $psgetItemInfo.Name | Should Be $script:PublishModuleName $psgetItemInfo.Version | Should Match $($version + $prerelease) @@ -424,12 +424,12 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { #Copy module to $script:ProgramFilesModulesPath Copy-Item $script:PublishModuleBase $script:ProgramFilesModulesPath -Recurse -Force - + $scriptBlock = { Publish-Module -Path $script:PublishModuleBase -NuGetApiKey $script:ApiKey -ReleaseNotes "$script:PublishModuleName release notes" -Tags PSGet -LicenseUri "http://$script:PublishModuleName.com/license" -ProjectUri "http://$script:PublishModuleName.com" -WarningAction SilentlyContinue } $scriptBlock | Should Not Throw - + $psgetItemInfo = Find-Module $script:PublishModuleName -RequiredVersion $($version + $prerelease) -AllowPrerelease $psgetItemInfo.Name | Should Be $script:PublishModuleName $psgetItemInfo.Version | Should Match $($version + $prerelease) @@ -467,7 +467,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { $psgetItemInfo.AdditionalMetadata | Should Not Be $null $psgetItemInfo.AdditionalMetadata.IsPrerelease | Should Match "true" } - + It "PublishModuleWithoutForceSameVersionLowerPrerelease" { $version = "1.0.0" $prerelease = "-beta002" @@ -486,7 +486,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { # Publish lower prerelease version $prerelease2 = "-alpha001" Update-ModuleManifest -Path $script:PublishModuleNamePSD1FilePath -Prerelease $prerelease2 - + $scriptBlock = { Publish-Module -Path $script:PublishModuleBase -NuGetApiKey $script:ApiKey } @@ -518,7 +518,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { $expectedFullyQualifiedErrorId = "ModuleVersionIsAlreadyAvailableInTheGallery,Publish-Module" $scriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId } - + It "PublishModuleSameVersionNoPrerelease" { $version = "1.0.0" $prerelease = "-alpha001" @@ -555,7 +555,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { New-ModuleManifest -Path $script:PublishModuleNamePSD1FilePath -ModuleVersion $version -Description "$script:PublishModuleName module" -NestedModules "$script:PublishModuleName.psm1" Update-ModuleManifest -Path $script:PublishModuleNamePSD1FilePath - + Publish-Module -Path $script:PublishModuleBase -NuGetApiKey $script:ApiKey -WarningAction SilentlyContinue $psgetItemInfo = Find-Module $script:PublishModuleName -RequiredVersion $version -AllowPrerelease @@ -606,7 +606,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { } It "PublishModuleWithInvalidPrereleaseString" { - + # Create manifest without using Update-ModuleManifest, it will throw validation error. $invalidPrereleaseModuleManifestContent = @" @{ @@ -819,7 +819,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { $expectedFullyQualifiedErrorId = "IncorrectVersionPartsCountForPrereleaseStringUsage,Publish-Module" $scriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId } - + It "PublishModuleWithPrereleaseStringAndLongVersion" { # Create manifest without using Update-ModuleManifest, it will throw validation error. @@ -910,7 +910,7 @@ Describe "--- Publish-Module ---" -Tags 'Module','P1','OuterLoop' { } It "PublishModuleWithEmptyPrereleaseFieldShouldSucceed" { - + # Create manifest without using Update-ModuleManifest $validPrereleaseModuleManifestContent = @" @{ @@ -976,7 +976,7 @@ Describe "--- Find-Module ---" -Tags 'Module','P1','OuterLoop' { $psgetModuleInfo.AdditionalMetadata.IsPrerelease | Should Match "true" $psgetModuleInfo.Version | Should Match '-' } - + It FindModuleAllowPrereleaseAllVersions { $results = Find-Module -Name $PrereleaseTestModule -Repository $TestRepositoryName -AllowPrerelease -AllVersions @@ -984,7 +984,7 @@ Describe "--- Find-Module ---" -Tags 'Module','P1','OuterLoop' { $results | Where-Object { ($_.AdditionalMetadata.IsPrerelease -eq $true) -and ($_.Version -match '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 $results | Where-Object { ($_.AdditionalMetadata.IsPrerelease -eq $false) -and ($_.Version -notmatch '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 } - + It FindModuleAllVersionsShouldReturnOnlyStableVersions { $results = Find-Module -Name $PrereleaseTestModule -Repository $TestRepositoryName -AllVersions @@ -1036,7 +1036,7 @@ Describe "--- Find-DscResource ---" -Tags 'Module','BVT','InnerLoop' { $psgetCommandInfo.PSGetModuleInfo.AdditionalMetadata.IsPrerelease | Should Match "true" $psgetCommandInfo.PSGetModuleInfo.Version | Should Match '-' } - + It FindDscResourceAllowPrereleaseAllVersions { $results = Find-DscResource -Name $DscResourceInPrereleaseTestModule -Repository $TestRepositoryName -AllowPrerelease -AllVersions -ModuleName $DscTestModule @@ -1044,7 +1044,7 @@ Describe "--- Find-DscResource ---" -Tags 'Module','BVT','InnerLoop' { $results | Where-Object { ($_.PSGetModuleInfo.AdditionalMetadata.IsPrerelease -eq $true) -and ($_.PSGetModuleInfo.Version -match '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 $results | Where-Object { ($_.PSGetModuleInfo.AdditionalMetadata.IsPrerelease -eq $false) -and ($_.PSGetModuleInfo.Version -notmatch '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 } - + It FindDscResourceAllVersionsShouldReturnOnlyStableVersions { $results = Find-DscResource -Name $DscResourceInPrereleaseTestModule -Repository $TestRepositoryName -AllVersions -ModuleName $DscTestModule @@ -1139,7 +1139,7 @@ Describe "--- Find-Command ---" -Tags 'Module','BVT','InnerLoop' { } Describe "--- Find-RoleCapability ---" -Tags 'Module','BVT','InnerLoop' { - + It FindRoleCapabilityReturnsLatestStableVersion { $psgetCommandInfo = Find-RoleCapability -Name $RoleCapabilityInPrereleaseTestModule -Repository $TestRepositoryName @@ -1161,7 +1161,7 @@ Describe "--- Find-RoleCapability ---" -Tags 'Module','BVT','InnerLoop' { $psgetCommandInfo.PSGetModuleInfo.AdditionalMetadata.IsPrerelease | Should Match "true" $psgetCommandInfo.PSGetModuleInfo.Version | Should Match '-' } - + It FindRoleCapabilityAllowPrereleaseAllVersions { $results = Find-RoleCapability -Name $RoleCapabilityInPrereleaseTestModule -Repository $TestRepositoryName -AllowPrerelease -AllVersions -ModuleName $DscTestModule @@ -1169,7 +1169,7 @@ Describe "--- Find-RoleCapability ---" -Tags 'Module','BVT','InnerLoop' { $results | Where-Object { ($_.PSGetModuleInfo.AdditionalMetadata.IsPrerelease -eq $true) -and ($_.PSGetModuleInfo.Version -match '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 $results | Where-Object { ($_.PSGetModuleInfo.AdditionalMetadata.IsPrerelease -eq $false) -and ($_.PSGetModuleInfo.Version -notmatch '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 } - + It FindRoleCapabilityAllVersionsShouldReturnOnlyStableVersions { $results = Find-RoleCapability -Name $RoleCapabilityInPrereleaseTestModule -Repository $TestRepositoryName -AllVersions -ModuleName $DscTestModule @@ -1201,7 +1201,7 @@ Describe "--- Find-RoleCapability ---" -Tags 'Module','BVT','InnerLoop' { } Describe "--- Install-Module ---" -Tags 'Module','P1','OuterLoop' { - + BeforeAll { PSGetTestUtils\Uninstall-Module TestPackage PSGetTestUtils\Uninstall-Module DscTestModule @@ -1213,9 +1213,9 @@ Describe "--- Install-Module ---" -Tags 'Module','P1','OuterLoop' { } - # Piping tests + # Piping tests #-------------- - + It "PipeFindToInstallModuleByNameAllowPrerelease" { Find-Module -Name $PrereleaseTestModule -AllowPrerelease -Repository $TestRepositoryName | Install-Module $res = Get-InstalledModule -Name $PrereleaseTestModule @@ -1246,7 +1246,7 @@ Describe "--- Install-Module ---" -Tags 'Module','P1','OuterLoop' { } $script | Should Throw } - + # Find-Command | Install-Module #-------------------------------- It "PipeFindCommandToInstallModuleByNameAllowPrerelease" { @@ -1345,10 +1345,10 @@ Describe "--- Install-Module ---" -Tags 'Module','P1','OuterLoop' { } $script | Should Throw } - - # Regular Install Tests + + # Regular Install Tests #----------------------- It "InstallPrereleaseModuleByName" { @@ -1359,7 +1359,7 @@ Describe "--- Install-Module ---" -Tags 'Module','P1','OuterLoop' { $res.Name | Should Be $PrereleaseTestModule $res.Version | Should Be $PrereleaseModuleLatestPrereleaseVersion } - + It "InstallSpecificPrereleaseModuleVersionByNameWithAllowPrerelease" { Install-Module -Name $PrereleaseTestModule -RequiredVersion $PrereleaseModuleMiddleVersion -AllowPrerelease -Repository $TestRepositoryName $res = Get-InstalledModule -Name $PrereleaseTestModule @@ -1368,7 +1368,7 @@ Describe "--- Install-Module ---" -Tags 'Module','P1','OuterLoop' { $res.Name | Should Be $PrereleaseTestModule $res.Version | Should Be $PrereleaseModuleMiddleVersion } - + It "InstallSpecificPrereleaseModuleVersionByNameWithoutAllowPrerelease" { $script = { Install-Module -Name $PrereleaseTestModule -RequiredVersion $PrereleaseModuleMiddleVersion -Repository $TestRepositoryName @@ -1378,7 +1378,7 @@ Describe "--- Install-Module ---" -Tags 'Module','P1','OuterLoop' { } Describe "--- Save-Module ---" -Tags 'Module','BVT','InnerLoop' { - + BeforeAll { PSGetTestUtils\Uninstall-Module TestPackage PSGetTestUtils\Uninstall-Module DscTestModule @@ -1389,9 +1389,9 @@ Describe "--- Save-Module ---" -Tags 'Module','BVT','InnerLoop' { PSGetTestUtils\Uninstall-Module DscTestModule } - # Piping tests + # Piping tests #-------------- - + It "PipeFindToSaveModuleByNameAllowPrerelease" { Find-Module -Name $PrereleaseTestModule -AllowPrerelease -Repository $TestRepositoryName | Save-Module -LiteralPath $script:MyDocumentsModulesPath $res = Get-InstalledModule -Name $PrereleaseTestModule @@ -1522,9 +1522,9 @@ Describe "--- Save-Module ---" -Tags 'Module','BVT','InnerLoop' { $script | Should Throw } - # Regular Save Tests + # Regular Save Tests #----------------------- - + It "SavePrereleaseModuleByName" { Save-Module -Name $PrereleaseTestModule -AllowPrerelease -Repository $TestRepositoryName -LiteralPath $script:MyDocumentsModulesPath $res = Get-InstalledModule -Name $PrereleaseTestModule @@ -1533,7 +1533,7 @@ Describe "--- Save-Module ---" -Tags 'Module','BVT','InnerLoop' { $res.Name | Should Be $PrereleaseTestModule $res.Version | Should Be $PrereleaseModuleLatestPrereleaseVersion } - + It "SaveSpecificPrereleaseModuleVersionByNameWithAllowPrerelease" { Save-Module -Name $PrereleaseTestModule -RequiredVersion $PrereleaseModuleMiddleVersion -AllowPrerelease -Repository $TestRepositoryName -LiteralPath $script:MyDocumentsModulesPath $res = Get-InstalledModule -Name $PrereleaseTestModule @@ -1544,7 +1544,7 @@ Describe "--- Save-Module ---" -Tags 'Module','BVT','InnerLoop' { $res.AdditionalMetadata | Should Not Be $null $res.AdditionalMetadata.IsPrerelease | Should Match "true" } - + It "SaveSpecificPrereleaseModuleVersionByNameWithoutAllowPrerelease" { $script = { Save-Module -Name $PrereleaseTestModule -RequiredVersion $PrereleaseModuleMiddleVersion -Repository $TestRepositoryName -LiteralPath $script:MyDocumentsModulesPath @@ -1554,7 +1554,7 @@ Describe "--- Save-Module ---" -Tags 'Module','BVT','InnerLoop' { } Describe "--- Update-Module ---" -Tags 'Module','BVT','InnerLoop' { - + BeforeAll { PSGetTestUtils\Uninstall-Module TestPackage } @@ -1609,7 +1609,7 @@ Describe "--- Update-Module ---" -Tags 'Module','BVT','InnerLoop' { $res.AdditionalMetadata.IsPrerelease | Should Match "false" } - # (In place update): prerelease to prerelease, same root version. (ex. 2.0.0-beta500 --> 2.0.0-gamma300) + # (In place update): prerelease to prerelease, same root version. (ex. 2.0.0-beta500 --> 2.0.0-gamma300) It "UpdateModuleSameVersionPrereleaseToPrereleaseInPlaceUpdate" { Install-Module $PrereleaseTestModule -RequiredVersion $PrereleaseModuleMiddleVersion -AllowPrerelease -Repository $TestRepositoryName Update-Module $PrereleaseTestModule -RequiredVersion "2.0.0-gamma300" -AllowPrerelease # Should update to latest prerelease version 2.0.0-gamma300 @@ -1656,7 +1656,7 @@ Describe "--- Update-Module ---" -Tags 'Module','BVT','InnerLoop' { } Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { - + BeforeAll { PSGetTestUtils\Uninstall-Module TestPackage } @@ -1668,7 +1668,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { It UninstallPrereleaseModuleOneVersion { $moduleName = "TestPackage" - + PowerShellGet\Install-Module -Name $moduleName -RequiredVersion $PrereleaseModuleMiddleVersion -AllowPrerelease -Repository $TestRepositoryName -Force $mod = Get-InstalledModule -Name $moduleName $mod | Should Not Be $null @@ -1687,7 +1687,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { else { $mod.Name | Should Be $moduleName - } + } PowerShellGet\Uninstall-Module -Name $moduleName $installedModules = Get-InstalledModule -Name $moduleName -AllVersions -ErrorAction SilentlyContinue @@ -1725,7 +1725,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { $mod2.Version | Should Match $PrereleaseModuleLatestPrereleaseVersion $mod2.AdditionalMetadata | Should Not Be $null $mod2.AdditionalMetadata.IsPrerelease | Should Match "true" - + $modules2 = Get-InstalledModule -Name $moduleName -AllVersions if($PSVersionTable.PSVersion -gt '5.0.0') @@ -1735,7 +1735,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { else { $mod2.Name | Should Be $moduleName - } + } PowerShellGet\Uninstall-Module -Name $moduleName -AllVersions $installedModules = Get-InstalledModule -Name $moduleName -AllVersions -ErrorAction SilentlyContinue @@ -1745,7 +1745,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { It UninstallPrereleaseModuleUsingRequiredVersion { $moduleName = "TestPackage" - + PowerShellGet\Install-Module -Name $moduleName -RequiredVersion $PrereleaseModuleMiddleVersion -AllowPrerelease -Repository $TestRepositoryName -Force $mod = Get-InstalledModule -Name $moduleName $mod | Should Not Be $null @@ -1764,7 +1764,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { else { $mod.Name | Should Be $moduleName - } + } PowerShellGet\Uninstall-Module -Name $moduleName -RequiredVersion $PrereleaseModuleMiddleVersion -AllowPrerelease $installedModules = Get-InstalledModule -Name $moduleName -AllVersions -ErrorAction SilentlyContinue @@ -1774,7 +1774,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { It GetInstalledModulePipeToUninstallModuleOneVersion { $moduleName = "TestPackage" - + PowerShellGet\Install-Module -Name $moduleName -RequiredVersion $PrereleaseModuleMiddleVersion -AllowPrerelease -Repository $TestRepositoryName -Force $mod = Get-InstalledModule -Name $moduleName $mod | Should Not Be $null @@ -1783,7 +1783,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { $mod.Version | Should Match $PrereleaseModuleMiddleVersion $mod.AdditionalMetadata | Should Not Be $null $mod.AdditionalMetadata.IsPrerelease | Should Match "true" - + $modules = Get-InstalledModule -Name $moduleName -AllVersions if($PSVersionTable.PSVersion -gt '5.0.0') @@ -1793,10 +1793,10 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { else { $mod.Name | Should Be $moduleName - } + } Get-InstalledModule -Name $moduleName | Uninstall-Module - + $installedModules = Get-InstalledModule -Name $moduleName -AllVersions -ErrorAction SilentlyContinue $installedModules | Should Be $null @@ -1804,7 +1804,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { It GetInstalledModulePipeToUninstallModuleMultipleVersions { $moduleName = "TestPackage" - + PowerShellGet\Install-Module -Name $moduleName -RequiredVersion "1.0.0" -Repository $TestRepositoryName -Force $mod = Get-InstalledModule -Name $moduleName -RequiredVersion "1.0.0" $mod | Should Not Be $null @@ -1831,7 +1831,7 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { $mod2.Version | Should Match $PrereleaseModuleLatestPrereleaseVersion $mod2.AdditionalMetadata | Should Not Be $null $mod2.AdditionalMetadata.IsPrerelease | Should Match "true" - + $modules2 = Get-InstalledModule -Name $moduleName -AllVersions if($PSVersionTable.PSVersion -gt '5.0.0') @@ -1841,10 +1841,10 @@ Describe "--- Uninstall-Module ---" -Tags 'Module','BVT','InnerLoop' { else { $mod2.Name | Should Be $moduleName - } + } Get-InstalledModule -Name $moduleName -AllVersions | Uninstall-Module - + $installedModules = Get-InstalledModule -Name $moduleName -AllVersions -ErrorAction SilentlyContinue $installedModules | Should Be $null @@ -1866,12 +1866,12 @@ Describe "--- New-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { } Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { - + BeforeAll { # Create temp module to be published $script:TempScriptsPath="$env:LocalAppData\temp\PSGet_$(Get-Random)" $null = New-Item -Path $script:TempScriptsPath -ItemType Directory -Force - + $script:PublishScriptName = 'Fabrikam-TestScript' $script:PublishScriptVersion = '1.0' $script:PublishScriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "$script:PublishScriptName.ps1" @@ -1882,7 +1882,7 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { } BeforeEach { - + $null = New-ScriptFileInfo -Path $script:PublishScriptFilePath ` -Version $script:PublishScriptVersion ` -Author Manikyam.Bavandla@microsoft.com ` @@ -1899,7 +1899,7 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { Test-ScriptWorkflow" } - AfterEach { + AfterEach { RemoveItem $script:PublishScriptFilePath RemoveItem "$script:TempScriptsPath\*.ps1" } @@ -1915,15 +1915,15 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $scriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "Get-ProcessScript.ps1" Set-Content -Path $scriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha+001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -1949,15 +1949,15 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $scriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "Get-ProcessScript.ps1" Set-Content -Path $scriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha-beta.01 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -1965,7 +1965,7 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { "@ $ScriptBlock = { - Test-ScriptFileInfo -Path $scriptFilePath + Test-ScriptFileInfo -Path $scriptFilePath } $expectedErrorMessage = $LocalizedData.InvalidCharactersInPrereleaseString -f 'alpha-beta.01' @@ -1983,15 +1983,15 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $scriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "Get-ProcessScript.ps1" Set-Content -Path $scriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha.1 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -2017,15 +2017,15 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $scriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "Get-ProcessScript.ps1" Set-Content -Path $scriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-error.0.0.0.1 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -2051,15 +2051,15 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $scriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "Get-ProcessScript.ps1" Set-Content -Path $scriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2-alpha001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -2074,7 +2074,7 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $expectedFullyQualifiedErrorId = "IncorrectVersionPartsCountForPrereleaseStringUsage,Test-ScriptFileInfo" $scriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId } - + # Purpose: Test a script file info with an Prerelease string when the version has too many parts. # # Action: Test-ScriptFileInfo [path] -Version 3.2.1.0.5-alpha001 @@ -2085,21 +2085,21 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $scriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "Get-ProcessScript.ps1" Set-Content -Path $scriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1.1-alpha001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. #> "@ - + $ScriptBlock = { Test-ScriptFileInfo -Path $scriptFilePath } @@ -2119,28 +2119,28 @@ Describe "--- Test-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $scriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "Get-ProcessScript.ps1" Set-Content -Path $scriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. #> "@ - + $testScriptInfo = Test-ScriptFileInfo -Path $scriptFilePath $testScriptInfo.Version | Should -Match "3.2.1-alpha001" } } Describe "--- Update-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { - + BeforeAll { Get-InstalledScript -Name Fabrikam-ServerScript -ErrorAction SilentlyContinue | Uninstall-Script -Force } @@ -2150,7 +2150,7 @@ Describe "--- Update-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { { Reset-PATHVariableForScriptsInstallLocation -Scope AllUsers } - + if($script:AddedCurrentUserInstallPath) { Reset-PATHVariableForScriptsInstallLocation -Scope CurrentUser @@ -2227,7 +2227,7 @@ Describe "--- Update-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { $expectedFullyQualifiedErrorId = "IncorrectVersionPartsCountForPrereleaseStringUsage,Test-ScriptFileInfo" $scriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId } - + It "UpdateScriptFileWithPrereleaseStringAndLongVersion" { $Version = "3.2.1.1-alpha001" @@ -2262,19 +2262,19 @@ Describe "--- Update-ScriptFileInfo ---" -Tags 'Script','BVT','InnerLoop' { } Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { - # Not executing these tests on Linux as + # Not executing these tests on Linux as # the total execution time is exceeding allowed 50 min in TravisCI daily builds. if($IsLinux) { return } - + BeforeAll { # Create file-based repository from scratch $script:PSGalleryRepoPath = Join-Path -Path $script:TempPath -ChildPath 'PSGallery Repo With Spaces' RemoveItem $script:PSGalleryRepoPath $null = New-Item -Path $script:PSGalleryRepoPath -ItemType Directory -Force - + # Backup existing repositories config file $script:moduleSourcesFilePath= Join-Path $script:PSGetLocalAppDataPath "PSRepositories.xml" $script:moduleSourcesBackupFilePath = Join-Path $script:PSGetLocalAppDataPath "PSRepositories.xml_$(get-random)_backup" @@ -2282,25 +2282,25 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { { Rename-Item $script:moduleSourcesFilePath $script:moduleSourcesBackupFilePath -Force } - + # Set file-based repo as default PSGallery repo Set-PSGallerySourceLocation -Location $script:PSGalleryRepoPath ` -PublishLocation $script:PSGalleryRepoPath ` -ScriptSourceLocation $script:PSGalleryRepoPath ` -ScriptPublishLocation $script:PSGalleryRepoPath - + $modSource = Get-PSRepository -Name "PSGallery" $modSource.SourceLocation | Should Be $script:PSGalleryRepoPath $modSource.PublishLocation | Should Be $script:PSGalleryRepoPath - + $script:ApiKey="TestPSGalleryApiKey" - + # Create temp module to be published $script:TempScriptsPath = Join-Path -Path $script:TempPath -ChildPath "PSGet_$(Get-Random)" $null = New-Item -Path $script:TempScriptsPath -ItemType Directory -Force $script:TempScriptsLiteralPath = Join-Path -Path $script:TempScriptsPath -ChildPath 'Lite[ral]Path' $null = New-Item -Path $script:TempScriptsLiteralPath -ItemType Directory -Force - + $script:PublishScriptName = 'Fabrikam-TestScript' $script:PublishScriptVersion = '1.0' $script:PublishScriptFilePath = Join-Path -Path $script:TempScriptsPath -ChildPath "$script:PublishScriptName.ps1" @@ -2315,16 +2315,16 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { { RemoveItem $script:moduleSourcesFilePath } - + # Import the PowerShellGet provider to reload the repositories. $null = Import-PackageProvider -Name PowerShellGet -Force - + RemoveItem $script:PSGalleryRepoPath RemoveItem $script:TempScriptsPath } BeforeEach { - + $null = New-ScriptFileInfo -Path $script:PublishScriptFilePath ` -Version $script:PublishScriptVersion ` -Author Manikyam.Bavandla@microsoft.com ` @@ -2342,13 +2342,13 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { } AfterEach { - RemoveItem "$script:PSGalleryRepoPath\*" + RemoveItem "$script:PSGalleryRepoPath\*" RemoveItem $script:PublishScriptFilePath } - + It "PublishScriptSameVersionHigherPrerelease" { - + # Publish first version $version = "1.0.0-alpha001" Update-ScriptFileInfo -Path $script:PublishScriptFilePath -Version $version @@ -2377,7 +2377,7 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { } It "PublishScriptSameVersionLowerPrereleaseWithForce" { - + # Publish first version $version = "1.0.0-beta002" Update-ScriptFileInfo -Path $script:PublishScriptFilePath -Version $version @@ -2406,9 +2406,9 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { $psgetItemInfo.AdditionalMetadata | Should Not Be $null $psgetItemInfo.AdditionalMetadata.IsPrerelease | Should Match "true" } - + It "PublishScriptSameVersionLowerPrereleaseWithoutForce" { - + # Publish first version $version = "1.0.0-beta002" Update-ScriptFileInfo -Path $script:PublishScriptFilePath -Version $version @@ -2430,9 +2430,9 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { } $scriptBlock | Should -Throw -ErrorId "ScriptPrereleaseStringShouldBeGreaterThanGalleryPrereleaseString,Publish-Script" } - + It "PublishScriptSameVersionSamePrerelease" { - + # Publish first version $version = "1.0.0-alpha001" Update-ScriptFileInfo -Path $script:PublishScriptFilePath -Version $version @@ -2454,7 +2454,7 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { } It "PublishScriptSameVersionNoPrerelease" { - + # Publish first version $version = "1.0.0-alpha001" Update-ScriptFileInfo -Path $script:PublishScriptFilePath -Version $version @@ -2484,7 +2484,7 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { } It "PublishScriptWithForceNewPrereleaseAfterStableVersion" { - + # Publish stable version $version = "1.0.0" Update-ScriptFileInfo -Path $script:PublishScriptFilePath -Version $version @@ -2511,9 +2511,9 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { $psgetItemInfo.AdditionalMetadata | Should Not Be $null $psgetItemInfo.AdditionalMetadata.IsPrerelease | Should Match "true" } - + It "PublishScriptWithoutForceNewPrereleaseAfterStableVersion" { - + # Publish stable version $version = "1.0.0" Update-ScriptFileInfo -Path $script:PublishScriptFilePath -Version $version @@ -2539,15 +2539,15 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { It "PublishScriptWithInvalidPrereleaseString" { Set-Content -Path $script:PublishScriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha+001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -2567,15 +2567,15 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { It "PublishScriptWithInvalidPrereleaseString2" { Set-Content -Path $script:PublishScriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha-beta.01 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -2586,7 +2586,7 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { $expectedFullyQualifiedErrorId = "InvalidCharactersInPrereleaseString,Test-ScriptFileInfo" $ScriptBlock = { - Publish-Script -Path $script:PublishScriptFilePath + Publish-Script -Path $script:PublishScriptFilePath } $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId @@ -2595,21 +2595,21 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { It "PublishScriptWithInvalidPrereleaseString3" { Set-Content -Path $script:PublishScriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha.1 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. #> "@ - + $expectedErrorMessage = $LocalizedData.InvalidCharactersInPrereleaseString -f 'alpha.1' $expectedFullyQualifiedErrorId = "InvalidCharactersInPrereleaseString,Test-ScriptFileInfo" @@ -2623,15 +2623,15 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { It "PublishScriptWithInvalidPrereleaseString4" { Set-Content -Path $script:PublishScriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-error.0.0.0.1 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -2651,21 +2651,21 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { It "PublishScriptWithPrereleaseStringAndShortVersion" { Set-Content -Path $script:PublishScriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2-alpha001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. #> "@ - + $expectedErrorMessage = $LocalizedData.IncorrectVersionPartsCountForPrereleaseStringUsage -f '3.2' $expectedFullyQualifiedErrorId = "IncorrectVersionPartsCountForPrereleaseStringUsage,Test-ScriptFileInfo" @@ -2675,25 +2675,25 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { $ScriptBlock | Should -Throw $expectedErrorMessage -ErrorId $expectedFullyQualifiedErrorId } - + It "PublishScriptWithPrereleaseStringAndLongVersion" { Set-Content -Path $script:PublishScriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1.1-alpha001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. #> "@ - + $expectedErrorMessage = $LocalizedData.IncorrectVersionPartsCountForPrereleaseStringUsage -f '3.2.1.1' $expectedFullyQualifiedErrorId = "IncorrectVersionPartsCountForPrereleaseStringUsage,Test-ScriptFileInfo" @@ -2707,15 +2707,15 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { It "PublishScriptWithValidPrereleaseAndVersion" { Set-Content -Path $script:PublishScriptFilePath -Value @" <#PSScriptInfo - .DESCRIPTION + .DESCRIPTION Performs a collection of admin tasks (Update, Virus Scan, Clean-up, Repair & Defrag) that might speed-up a computers performance. - .VERSION + .VERSION 3.2.1-alpha001 - .GUID + .GUID 35eb535b-7e54-4412-a58b-8a0c588c0b30 - .AUTHOR + .AUTHOR Rebecca Roenitz @RebRo - .TAGS + .TAGS ManualScriptInfo .RELEASENOTES Release notes for this script file. @@ -2732,7 +2732,7 @@ Describe "--- Publish-Script ---" -Tags 'Script','P1','OuterLoop' { } Describe "--- Find-Script ---" -Tags 'Script','P1','OuterLoop' { - + # Find-Script Tests #------------------- It FindScriptReturnsLatestStableVersion { @@ -2752,7 +2752,7 @@ Describe "--- Find-Script ---" -Tags 'Script','P1','OuterLoop' { $psgetScriptInfo.AdditionalMetadata.IsPrerelease | Should Match "true" $psgetScriptInfo.Version | Should Match '-' } - + It FindScriptAllowPrereleaseAllVersions { $results = Find-Script -Name $PrereleaseTestScript -Repository $TestRepositoryName -AllowPrerelease -AllVersions @@ -2760,7 +2760,7 @@ Describe "--- Find-Script ---" -Tags 'Script','P1','OuterLoop' { $results | Where-Object { ($_.AdditionalMetadata.IsPrerelease -eq $true) -and ($_.Version -match '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 $results | Where-Object { ($_.AdditionalMetadata.IsPrerelease -eq $false) -and ($_.Version -notmatch '-') } | Measure-Object | ForEach-Object { $_.Count } | Should BeGreaterThan 0 } - + It FindScriptAllVersionsShouldReturnOnlyStableVersions { $results = Find-Script -Name $PrereleaseTestScript -Repository $TestRepositoryName -AllVersions @@ -2790,7 +2790,7 @@ Describe "--- Find-Script ---" -Tags 'Script','P1','OuterLoop' { } Describe "--- Install-Script ---" -Tags 'Script','P1','OuterLoop' { - + BeforeAll { Get-InstalledScript -Name "TestScript" -ErrorAction SilentlyContinue | Uninstall-Script -Force } @@ -2798,12 +2798,12 @@ Describe "--- Install-Script ---" -Tags 'Script','P1','OuterLoop' { AfterEach { PSGetTestUtils\RemoveItem -path $(Join-Path $script:ProgramFilesScriptsPath "TestScript.ps1") PSGetTestUtils\RemoveItem -path $(Join-Path $script:MyDocumentsScriptsPath "TestScript.ps1") - } + } - # Piping tests + # Piping tests #-------------- - + It "PipeFindToInstallScriptByNameAllowPrerelease" { Find-Script -Name $PrereleaseTestScript -AllowPrerelease -Repository $TestRepositoryName | Install-Script $res = Get-InstalledScript -Name $PrereleaseTestScript @@ -2815,7 +2815,7 @@ Describe "--- Install-Script ---" -Tags 'Script','P1','OuterLoop' { $res.AdditionalMetadata | Should Not Be $null $res.AdditionalMetadata.IsPrerelease | Should Match "true" } - + It "PipeFindToInstallScriptSpecificPrereleaseVersionByNameWithAllowPrerelease" { Find-Script -Name $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -AllowPrerelease -Repository $TestRepositoryName | Install-Script $res = Get-InstalledScript -Name $PrereleaseTestScript @@ -2827,17 +2827,17 @@ Describe "--- Install-Script ---" -Tags 'Script','P1','OuterLoop' { $res.AdditionalMetadata | Should Not Be $null $res.AdditionalMetadata.IsPrerelease | Should Match "true" } - + It "PipeFindToInstallScriptSpecificPrereleaseVersionByNameWithoutAllowPrerelease" { $script = { Find-Script -Name $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -Repository $TestRepositoryName | Install-Script } $script | Should Throw } - - # Regular Install Tests + + # Regular Install Tests #----------------------- - + It "InstallPrereleaseScriptByName" { Install-Script -Name $PrereleaseTestScript -AllowPrerelease -Repository $TestRepositoryName $res = Get-InstalledScript -Name $PrereleaseTestScript @@ -2846,7 +2846,7 @@ Describe "--- Install-Script ---" -Tags 'Script','P1','OuterLoop' { $res.Name | Should Be $PrereleaseTestScript $res.Version | Should Match $PrereleaseScriptLatestPrereleaseVersion } - + It "InstallSpecificPrereleaseScriptVersionByNameWithAllowPrerelease" { Install-Script -Name $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -AllowPrerelease -Repository $TestRepositoryName $res = Get-InstalledScript -Name $PrereleaseTestScript @@ -2855,7 +2855,7 @@ Describe "--- Install-Script ---" -Tags 'Script','P1','OuterLoop' { $res.Name | Should Be $PrereleaseTestScript $res.Version | Should Match $PrereleaseScriptMiddleVersion } - + It "InstallSpecificPrereleaseScriptVersionByNameWithoutAllowPrerelease" { $script = { Install-Script -Name $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -Repository $TestRepositoryName @@ -2874,9 +2874,9 @@ Describe "--- Save-Script ---" -Tags 'Script','BVT','InnerLoop' { AfterEach { PSGetTestUtils\RemoveItem -path $(Join-Path $script:ProgramFilesScriptsPath "TestScript.ps1") PSGetTestUtils\RemoveItem -path $(Join-Path $script:MyDocumentsScriptsPath "TestScript.ps1") - } + } - # Piping tests + # Piping tests #-------------- It "PipeFindToSaveScriptByNameAllowPrerelease" { Find-Script -Name $PrereleaseTestScript -AllowPrerelease -Repository $TestRepositoryName | Save-Script -LiteralPath $script:MyDocumentsScriptsPath @@ -2890,7 +2890,7 @@ Describe "--- Save-Script ---" -Tags 'Script','BVT','InnerLoop' { $savedVersion | Should Be $PrereleaseScriptLatestPrereleaseVersion } - + It "PipeFindToSaveScriptSpecificPrereleaseVersionByNameWithAllowPrerelease" { Find-Script -Name $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -AllowPrerelease -Repository $TestRepositoryName | Save-Script -LiteralPath $script:MyDocumentsScriptsPath @@ -2910,8 +2910,8 @@ Describe "--- Save-Script ---" -Tags 'Script','BVT','InnerLoop' { } $script | Should Throw } - - # Regular Save Tests + + # Regular Save Tests #----------------------- It "SavePrereleaseScriptByName" { Save-Script -Name $PrereleaseTestScript -AllowPrerelease -Repository $TestRepositoryName -LiteralPath $script:MyDocumentsScriptsPath @@ -2925,10 +2925,10 @@ Describe "--- Save-Script ---" -Tags 'Script','BVT','InnerLoop' { $savedVersion | Should Be $PrereleaseScriptLatestPrereleaseVersion } - + It "SaveSpecificPrereleaseScriptVersionByNameWithAllowPrerelease" { Save-Script -Name $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -AllowPrerelease -Repository $TestRepositoryName -LiteralPath $script:MyDocumentsScriptsPath - + $scriptPath = Join-Path -Path $script:MyDocumentsScriptsPath -ChildPath $PrereleaseTestScript Test-Path -Path "$scriptPath.ps1" -PathType Leaf | Should Be $true @@ -2938,7 +2938,7 @@ Describe "--- Save-Script ---" -Tags 'Script','BVT','InnerLoop' { $savedVersion | Should Be $PrereleaseScriptMiddleVersion } - + It "SaveSpecificPrereleaseScriptVersionByNameWithoutAllowPrerelease" { $script = { Save-Script -Name $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -Repository $TestRepositoryName -LiteralPath $script:MyDocumentsScriptsPath @@ -2948,7 +2948,7 @@ Describe "--- Save-Script ---" -Tags 'Script','BVT','InnerLoop' { } Describe "--- Update-Script ---" -Tags 'Script','BVT','InnerLoop' { - + BeforeAll { PSGetTestUtils\RemoveItem -path $(Join-Path $script:ProgramFilesScriptsPath "TestScript.ps1") PSGetTestUtils\RemoveItem -path $(Join-Path $script:MyDocumentsScriptsPath "TestScript.ps1") @@ -2957,7 +2957,7 @@ Describe "--- Update-Script ---" -Tags 'Script','BVT','InnerLoop' { AfterEach { PSGetTestUtils\RemoveItem -path $(Join-Path $script:ProgramFilesScriptsPath "TestScript.ps1") PSGetTestUtils\RemoveItem -path $(Join-Path $script:MyDocumentsScriptsPath "TestScript.ps1") - } + } # Updated to latest release version by default: When release version is installed (ex. 1.0.0 --> 3.0.0) It "UpdateScriptFromReleaseToReleaseVersionByDefault" { @@ -2988,7 +2988,7 @@ Describe "--- Update-Script ---" -Tags 'Script','BVT','InnerLoop' { $res.AdditionalMetadata | Should Not Be $null $res.AdditionalMetadata.IsPrerelease | Should Match "false" } - + # (In place update): prerelease to release, same root version. (ex. 2.0.0-alpha005 --> 3.0.0) It "UpdateScriptSameVersionPrereleaseToReleaseInPlaceUpdate" { Install-Script $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -AllowPrerelease -Repository $TestRepositoryName @@ -3004,7 +3004,7 @@ Describe "--- Update-Script ---" -Tags 'Script','BVT','InnerLoop' { $res.AdditionalMetadata.IsPrerelease | Should Match "false" } - # (In place update): prerelease to prerelease, same root version. (ex. 2.0.0-alpha005 --> 2.0.0-beta1234) + # (In place update): prerelease to prerelease, same root version. (ex. 2.0.0-alpha005 --> 2.0.0-beta1234) It "UpdateScriptSameVersionPrereleaseToPrereleaseInPlaceUpdate" { Install-Script $PrereleaseTestScript -RequiredVersion $PrereleaseScriptMiddleVersion -AllowPrerelease -Repository $TestRepositoryName Update-Script $PrereleaseTestScript -RequiredVersion "2.0.0-beta1234" -AllowPrerelease @@ -3059,10 +3059,9 @@ Describe "--- Uninstall-Script ---" -Tags 'Script','BVT','InnerLoop' { AfterEach { PSGetTestUtils\RemoveItem -path $(Join-Path $script:ProgramFilesScriptsPath "TestScript.ps1") PSGetTestUtils\RemoveItem -path $(Join-Path $script:MyDocumentsScriptsPath "TestScript.ps1") - } + } It UninstallPrereleaseScript { - $scriptName = "TestScript" PowerShellGet\Install-Script -Name $scriptName -RequiredVersion "1.0.0" -Repository $TestRepositoryName @@ -3091,7 +3090,7 @@ Describe "--- Uninstall-Script ---" -Tags 'Script','BVT','InnerLoop' { $mod2.Version | Should Match $PrereleaseScriptLatestPrereleaseVersion $mod2.AdditionalMetadata | Should Not Be $null $mod2.AdditionalMetadata.IsPrerelease | Should Match "true" - + $scripts2 = PowerShellGet\Get-InstalledScript -Name $scriptName -AllowPrerelease if($PSVersionTable.PSVersion -gt '5.0.0') @@ -3101,7 +3100,7 @@ Describe "--- Uninstall-Script ---" -Tags 'Script','BVT','InnerLoop' { else { $mod2.Name | Should Be $scriptName - } + } PowerShellGet\Uninstall-Script -Name $scriptName @@ -3109,4 +3108,4 @@ Describe "--- Uninstall-Script ---" -Tags 'Script','BVT','InnerLoop' { $installedScripts | Should Be $null } -} \ No newline at end of file +} diff --git a/src/PowerShellGet/PSGet.Resource.psd1 b/src/PowerShellGet/PSGet.Resource.psd1 index d0381a4b..122b3235 100644 --- a/src/PowerShellGet/PSGet.Resource.psd1 +++ b/src/PowerShellGet/PSGet.Resource.psd1 @@ -21,7 +21,7 @@ ConvertFrom-StringData @' VersionRangeAndRequiredVersionCannotBeSpecifiedTogether=You cannot use the parameters RequiredVersion and either MinimumVersion or MaximumVersion in the same command. Specify only one of these parameters in your command. RequiredVersionAllowedOnlyWithSingleModuleName=The RequiredVersion parameter is allowed only when a single module name is specified as the value of the Name parameter, without any wildcard characters. MinimumVersionIsGreaterThanMaximumVersion=The specified MinimumVersion '{0}' is greater than the specified MaximumVersion '{1}'. - AllowPrereleaseRequiredToUsePrereleaseStringInVersion=The '-AllowPrerelease' parameter must be specified when using the Prerelease string in MinimumVersion, MaximumVersion, or RequiredVersion. + AllowPrereleaseRequiredToUsePrereleaseStringInVersion=The '-AllowPrerelease' parameter must be specified when using the Prerelease string in MinimumVersion, MaximumVersion, or RequiredVersion. InstallModuleAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to install modules in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or install '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). InstallScriptAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to install scripts in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or install '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). AdministratorRightsNeededOrSpecifyCurrentUserScope=Administrator rights are required to install or update. Log on to the computer with an account that has Administrator rights, and then try again, or install by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). @@ -39,8 +39,8 @@ ConvertFrom-StringData @' InvalidAuthenticodeSignature=The module '{0}' cannot be installed or updated because the Authenticode signature for the file '{1}' is not valid. ModuleNotInstalledOnThisMachine=Module '{0}' was not updated because no valid module was found in the module directory. Verify that the module is located in the folder specified by $env:PSModulePath. ScriptNotInstalledOnThisMachine=Script '{0}' was not updated because no valid script was found in the script directories '{1}' and '{2}'. - AdminPrivilegesRequiredForUpdate=Module '{0}' (installed at'{1}') cannot be updated because Administrator rights are required to change that directory. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). - AdminPrivilegesRequiredForScriptUpdate=Script '{0}' (installed at'{1}') cannot be updated because Administrator rights are required to change that script. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). + AdminPrivilegesRequiredForUpdate=Module '{0}' (installed at '{1}') cannot be updated because Administrator rights are required to change that directory. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). + AdminPrivilegesRequiredForScriptUpdate=Script '{0}' (installed at '{1}') cannot be updated because Administrator rights are required to change that script. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). ModuleNotInstalledUsingPowerShellGet=Module '{0}' was not installed by using Install-Module, so it cannot be updated. ScriptNotInstalledUsingPowerShellGet=Script '{0}' was not installed by using Install-Script, so it cannot be updated. DownloadingModuleFromGallery=Downloading module '{0}' with version '{1}' from the repository '{2}'. @@ -65,7 +65,7 @@ ConvertFrom-StringData @' ModuleVersionInUse=The version '{0}' of module '{1}' is currently in use. Retry the operation after closing the applications. ModuleNotAvailableLocally=The specified module '{0}' was not published because no module with that name was found in any module directory. InvalidModulePathToPublish=The specified module with path '{0}' was not published because no valid module was found with that path. - ModuleWithRequiredVersionNotAvailableLocally= The specified module '{0}' with version '{1}' was not published because no module with that name and version was found in any module directory. + ModuleWithRequiredVersionNotAvailableLocally= The specified module '{0}' with version '{1}' was not published because no module with that name and version was found in any module directory. AmbiguousModuleName=Modules with the name '{0}' are available under multiple paths. Add the -RequiredVersion parameter or the -Path parameter to specify the module to publish. AmbiguousModulePath=Multiple versions are available under the specified module path '{0}'. Specify the full path to the module to be published. PublishModuleLocation=Module '{0}' was found in '{1}'. @@ -79,7 +79,7 @@ ConvertFrom-StringData @' CouldNotInstallNuGetExe=NuGet.exe version '{0}' or newer, or dotnet command version '{1}' or newer is required to interact with NuGet-based repositories. Please ensure that NuGet.exe or dotnet command is available under one of the paths specified in PATH environment variable value. CouldNotUpgradeNuGetExe=NuGet.exe version '{0}' or newer, or dotnet command version '{1}' or newer is required to interact with NuGet-based repositories. Please ensure that required version of NuGet.exe or dotnet command is available under one of the paths specified in PATH environment variable value. CouldNotFindDotnetCommand=For publish operations, dotnet command version '{0}' or newer is required to interact with the NuGet-based repositories. Please ensure that dotnet command version '{0}' or newer is installed and available under one of the paths specified in PATH environment variable value. You can also install the dotnet command by following the instructions specified at '{1}'. - CouldNotInstallNuGetBinaries2=PowerShellGet requires NuGet.exe (or dotnet command) and NuGet provider version '{0}' or newer to interact with the NuGet-based repositories. Please ensure that '{0}' or newer version of NuGet provider is installed and NuGet.exe (or dotnet command) is available under one of the paths specified in PATH environment variable value. + CouldNotInstallNuGetBinaries2=PowerShellGet requires NuGet.exe (or dotnet command) and NuGet provider version '{0}' or newer to interact with the NuGet-based repositories. Please ensure that '{0}' or newer version of NuGet provider is installed and NuGet.exe (or dotnet command) is available under one of the paths specified in PATH environment variable value. InstallNugetBinariesUpgradeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe and minimum version '{1}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{2}' or '{3}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. NuGet.exe must be available in '{4}' or '{5}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://go.microsoft.com/fwlink/?linkid=875534. Do you want PowerShellGet to upgrade NuGet.exe to the latest version and install the NuGet provider now? InstallNuGetBinariesShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe and minimum version '{1}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{3}' or '{3}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. NuGet.exe must be available in '{4}' or '{5}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://go.microsoft.com/fwlink/?linkid=875534. Do you want PowerShellGet to install both the latest NuGet.exe and NuGet provider now? InstallNugetExeUpgradeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in '{1}' or '{2}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://aka.ms/installing-powershellget . Do you want PowerShellGet to upgrade to the latest version of NuGet.exe now? @@ -91,7 +91,7 @@ ConvertFrom-StringData @' InstallNuGetExeShouldContinueCaption=NuGet.exe is required to continue InstallNuGetProviderShouldContinueCaption=NuGet provider is required to continue DownloadingNugetExe=Installing NuGet.exe. - DownloadingNugetProvider=Installing NuGet provider. + DownloadingNugetProvider=Installing NuGet provider. ModuleNotFound=Module '{0}' was not found. NoMatchFound=No match was found for the specified search criteria and module names '{0}'. NoMatchFoundForScriptName=No match was found for the specified search criteria and script names '{0}'. @@ -113,7 +113,7 @@ ConvertFrom-StringData @' PSGalleryPublishLocationIsMissing=The specified repository '{0}' does not have a valid PublishLocation. Retry after setting the PublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. PSRepositoryScriptPublishLocationIsMissing=The specified repository '{0}' does not have a valid ScriptPublishLocation. Retry after setting the ScriptPublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. ScriptSourceLocationIsMissing=The specified repository '{0}' does not have a valid ScriptSourceLocation. Retry after setting the ScriptSourceLocation for repository '{0}' to a valid NuGet endpoint for scripts using the Set-PSRepository cmdlet. - PublishModuleSupportsOnlyNuGetBasedPublishLocations=Publish-Module only supports the NuGet-based publish locations. The PublishLocation '{0}' of the repository '{1}' is not a NuGet-based publish location. Retry after setting the PublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. + PublishModuleSupportsOnlyNuGetBasedPublishLocations=Publish-Module only supports the NuGet-based publish locations. The PublishLocation '{0}' of the repository '{1}' is not a NuGet-based publish location. Retry after setting the PublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. PublishScriptSupportsOnlyNuGetBasedPublishLocations=Publish-Script only supports the NuGet-based publish locations. The ScriptPublishLocation '{0}' of the repository '{1}' is not a NuGet-based publish location. Retry after setting the ScriptPublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. DynamicParameterHelpMessage=The dynamic parameter '{0}' is required for Find-Module and Install-Module when using the PackageManagement provider '{1}' and source location '{2}'. Please enter your value for the '{3}' dynamic parameter: ProviderApiDebugMessage=In PowerShellGet Provider - '{0}'. @@ -123,14 +123,14 @@ ConvertFrom-StringData @' SpecifiedSourceName=Using the specified source names : '{0}'. SpecifiedLocationAndOGP=The specified Location is '{0}' and PackageManagementProvider is '{1}'. NoSourceNameIsSpecified=The -Repository parameter was not specified. PowerShellGet will use all of the registered repositories. - GettingPackageManagementProviderObject=Getting the provider object for the PackageManagement Provider '{0}'. + GettingPackageManagementProviderObject=Getting the provider object for the PackageManagement Provider '{0}'. InvalidInputObjectValue=Invalid value is specified for InputObject parameter. SpecifiedInstallationScope=The installation scope is specified to be '{0}'. SourceLocationValueForPSGalleryCannotBeChanged=The SourceLocation value for the PSGallery repository can not be changed. PublishLocationValueForPSGalleryCannotBeChanged=The PublishLocation value for the PSGallery repository can not be changed. SpecifiedProviderName=The specified PackageManagement provider name '{0}'. ProviderNameNotSpecified=User did not specify the PackageManagement provider name, trying with the provider name '{0}'. - SpecifiedProviderNotAvailable=The specified PackageManagement provider '{0}' is not available. + SpecifiedProviderNotAvailable=The specified PackageManagement provider '{0}' is not available. SpecifiedProviderDoesnotSupportPSModules=The specified PackageManagement Provider '{0}' does not support PowerShell Modules. PackageManagement Providers must support the 'supports-powershell-modules' feature. PollingPackageManagementProvidersForLocation=Polling available PackageManagement Providers to find one that can support the specified source location '{0}'. PollingSingleProviderForLocation=Resolving the source location '{0}' with PackageManagement Provider '{1}'. @@ -140,14 +140,14 @@ ConvertFrom-StringData @' NotSupportedPowerShellGetFormatVersion=The specified module '{0}' with PowerShellGetFormatVersion '{1}' is not supported by the current version of PowerShellGet. Get the latest version of the PowerShellGet module to install this module, '{2}'. NotSupportedPowerShellGetFormatVersionScripts=The specified script '{0}' with PowerShellGetFormatVersion '{1}' is not supported by the current version of PowerShellGet. Get the latest version of the PowerShellGet module to install this script, '{2}'. PathNotFound=Cannot find the path '{0}' because it does not exist. - ModuleIsNotTrusted=Untrusted module '{0}'. + ModuleIsNotTrusted=Untrusted module '{0}'. ScriptIsNotTrusted=Untrusted script '{0}'. - SkippedModuleDependency=Because dependent module '{0}' was skipped in the module dependencies list, users might not know how to install it. - MissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current module '{1}', ensure that its dependent module '{2}' is installed. - ExternallyManagedModuleDependencyIsInstalled=The externally managed, dependent module '{0}' is already installed on this computer. - ScriptMissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current script '{1}', ensure that its dependent module '{2}' is installed. + SkippedModuleDependency=Because dependent module '{0}' was skipped in the module dependencies list, users might not know how to install it. + MissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current module '{1}', ensure that its dependent module '{2}' is installed. + ExternallyManagedModuleDependencyIsInstalled=The externally managed, dependent module '{0}' is already installed on this computer. + ScriptMissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current script '{1}', ensure that its dependent module '{2}' is installed. ScriptMissingExternallyManagedScriptDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current script '{1}', ensure that its dependent script '{2}' is installed. - ScriptExternallyManagedScriptDependencyIsInstalled=The externally managed, dependent script '{0}' is already installed on this computer. + ScriptExternallyManagedScriptDependencyIsInstalled=The externally managed, dependent script '{0}' is already installed on this computer. UnableToResolveModuleDependency=PowerShellGet cannot resolve the module dependency '{0}' of the module '{1}' on the repository '{2}'. Verify that the dependent module '{3}' is available in the repository '{4}'. If this dependent module '{5}' is managed externally, add it to the ExternalModuleDependencies entry in the PSData section of the module manifest. FindingModuleDependencies=Finding module dependencies for version '{1}' of the module '{0}' from repository '{2}'. InstallingDependencyModule=Installing the dependency module '{0}' with version '{1}' for the module '{2}'. @@ -201,7 +201,7 @@ ConvertFrom-StringData @' FailedToPublishScript=Failed to publish script '{0}': '{1}'. PublishedScriptSuccessfully=Successfully published script '{0}' to the publish location '{1}'. Please allow few minutes for '{2}' to show up in the search results. UnableToResolveScriptDependency=PowerShellGet cannot resolve the {0} dependency '{1}' of the script '{2}' on the repository '{3}'. Verify that the dependent {0} '{1}' is available in the repository '{3}'. If this dependent {0} '{1}' is managed externally, add it to the '{4}' entry in the script metadata. - InvalidVersion=Cannot convert value '{0}' to type 'System.Version'. + InvalidVersion=Cannot convert value '{0}' to type 'System.Version'. InvalidGuid=Cannot convert value '{0}' to type 'System.Guid'. InvalidParameterValue=The specified value '{0}' for the parameter '{1}' is invalid. Ensure that it does not contain '<#' or '#>'. MissingPSScriptInfo=PSScriptInfo is not specified in the script file '{0}'. You can use the Update-ScriptFileInfo with -Force or New-ScriptFileInfo cmdlet to add the PSScriptInfo to the script file. @@ -219,18 +219,18 @@ ConvertFrom-StringData @' ScriptPATHPromptCaption=PATH Environment Variable Change ScriptPATHPromptQuery=Your system has not been configured with a default script installation path yet, which means you can only run a script by specifying the full path to the script file. This action places the script into the folder '{0}', and adds that folder to your PATH environment variable. Do you want to add the script installation path '{0}' to the PATH environment variable? AddedScopePathToProcessSpecificPATHVariable=Added scripts installation location '{0}' for '{1}' scope to process specific PATH environment variable. - AddedScopePathToPATHVariable=Added scripts installation location '{0}' for '{1}' scope to PATH environment variable. + AddedScopePathToPATHVariable=Added scripts installation location '{0}' for '{1}' scope to PATH environment variable. FilePathInFileListNotWithinModuleBase=Path '{0}' defined in FileList is not within module base '{1}'. Provide the correct FileList parameters and then try again. ManifestFileReadWritePermissionDenied=The current user does not have read-write permissions for the file:'{0}'. Check the file permissions and then try again. MissingTheRequiredPathOrPassThruParameter=The Path or PassThru parameter is required for creating the script file info. A new script file will be created with the script file info when the Path parameter is specified. Script file info will be returned if the PassThru parameter is specified. Try again after specifying the required parameter. DescriptionParameterIsMissingForAddingTheScriptFileInfo=Description parameter is missing for adding the metadata to the script file. Try again after specifying the description. UnableToAddPSScriptInfo=Unable to add PSScriptInfo to the script file '{0}'. You can use the New-ScriptFileInfo cmdlet to add the metadata to the existing script file. - RegisterVSTSFeedAsNuGetPackageSource=Publishing to a VSTS package management feed '{0}' requires it to be registered as a NuGet package source. Retry after adding this source '{0}' as NuGet package source by following the instructions specified at '{1}' + RegisterVSTSFeedAsNuGetPackageSource=Publishing to a VSTS package management feed '{0}' requires it to be registered as a NuGet package source. Retry after adding this source '{0}' as NuGet package source by following the instructions specified at '{1}' InvalidModuleAuthenticodeSignature=The module '{0}' cannot be installed or updated because the authenticode signature of the file '{1}' is not valid. InvalidCatalogSignature=The module '{0}' cannot be installed because the catalog signature in '{1}' does not match the hash generated from the module. AuthenticodeIssuerMismatch=Authenticode issuer '{0}' of the new module '{1}' with version '{2}' from root certificate authority '{3}' is not matching with the authenticode issuer '{4}' of the previously-installed module '{5}' with version '{6}' from root certificate authority '{7}'. If you still want to install or update, use -SkipPublisherCheck parameter. ModuleCommandAlreadyAvailable=The following commands are already available on this system:'{0}'. This module '{1}' may override the existing commands. If you still want to install this module '{1}', use -AllowClobber parameter. - CatalogFileFound=Found the catalog file '{0}' in the module '{1}' contents. + CatalogFileFound=Found the catalog file '{0}' in the module '{1}' contents. CatalogFileNotFoundInAvailableModule=Catalog file '{0}' is not found in the contents of the previously-installed module '{1}' with the same name. CatalogFileNotFoundInNewModule=Catalog file '{0}' is not found in the contents of the module '{1}' being installed. ValidAuthenticodeSignature=Valid authenticode signature found in the catalog file '{0}' for the module '{1}'. @@ -241,7 +241,7 @@ ConvertFrom-StringData @' SkippingPublisherCheck=Skipping the Publisher check for the version '{0}' of module '{1}'. SourceModuleDetailsForPublisherValidation=For publisher validation, using the previously-installed module '{0}' with version '{1}' under '{2}' with publisher name '{3}' from root certificate authority '{4}'. Is this module signed by Microsoft: '{5}'. NewModuleVersionDetailsForPublisherValidation=For publisher validation, current module '{0}' with version '{1}' with publisher name '{2}' from root certificate authority '{3}'. Is this module signed by Microsoft: '{4}'. - PublishersMatch=Publisher '{0}' of the new module '{1}' with version '{2}' matches with the publisher '{3}' of the previously-installed module '{4}' with version '{5}'. Both versions are signed with a Microsoft root certificate. + PublishersMatch=Publisher '{0}' of the new module '{1}' with version '{2}' matches with the publisher '{3}' of the previously-installed module '{4}' with version '{5}'. Both versions are signed with a Microsoft root certificate. PublishersMismatch=A Microsoft-signed module named '{0}' with version '{1}' that was previously installed conflicts with the new module '{2}' from publisher '{3}' with version '{4}'. Installing the new module may result in system instability. If you still want to install or update, use -SkipPublisherCheck parameter. ModuleIsNotCatalogSigned=The version '{0}' of the module '{1}' being installed is not catalog signed. Ensure that the version '{0}' of the module '{1}' has the catalog file '{2}' and signed with the same publisher '{3}' as the previously-installed module '{1}' with version '{4}' under the directory '{5}'. If you still want to install or update, use -SkipPublisherCheck parameter. SentEnvironmentVariableChangeMessage=Successfully broadcasted the Environment variable changes. @@ -258,7 +258,7 @@ ConvertFrom-StringData @' RequiredScriptVersion=REQUIREDSCRIPTS: Required version of script '{0}' is '{1}'. RequiredScriptVersoinFormat=, :, :[], :[,], :[,] FailedToParseRequiredScripts=Cannot parse REQUIREDSCRIPTS '{0}'. Acceptable formats are: '{1}'. - FailedToParseRequiredScriptsVersion=Version format error: {0}, '{1}'. Acceptable formats are: '{2}'. + FailedToParseRequiredScriptsVersion=Version format error: {0}, '{1}'. Acceptable formats are: '{2}'. PublishersMismatchAsWarning=Module '{0}' version '{1}' published by '{2}' will be superceded by version '{3}' published by '{4}'. If you do not trust the new publisher, uninstall the module. UnableToDownloadThePackage=The PackageManagement provider '{0}' is unable to download the package '{1}' version '{2}' to '{3}' path. ValidatingTheModule=Validating the '{0}' module contents under '{1}' path. @@ -268,4 +268,4 @@ ConvertFrom-StringData @' ValidateModuleCommandAlreadyAvailable=Checking for possible command collisions for the module '{0}' commands. UnauthorizedAccessError=Access to the path '{0}' is denied. ###PSLOC -'@ \ No newline at end of file +'@ diff --git a/src/PowerShellGet/private/functions/Get-InstallationScope.ps1 b/src/PowerShellGet/private/functions/Get-InstallationScope.ps1 new file mode 100644 index 00000000..7be34995 --- /dev/null +++ b/src/PowerShellGet/private/functions/Get-InstallationScope.ps1 @@ -0,0 +1,27 @@ + +# Determine scope. We prefer CurrentUser scope even if the older module is installed for AllUsers, unless: +# old module is installed for all users, we are elevated, AND using Windows PowerShell +# This is to mirror newer behavior of Install-Module. +function Get-InstallationScope() +{ + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [string]$PreviousInstallLocation, + + [Parameter(Mandatory=$true)] + [string]$CurrentUserPath + ) + + if ( -not $PreviousInstallLocation.ToString().StartsWith($currentUserPath, [System.StringComparison]::OrdinalIgnoreCase) -and + -not $script:IsCoreCLR -and + (Test-RunningAsElevated)) { + $Scope = "AllUsers" + } + else { + $Scope = "CurrentUser" + } + + Write-Debug "Get-InstallationScope: $PreviousInstallLocation $($script:IsCoreCLR) $(Test-RunningAsElevated) : $Scope" + return $Scope +} diff --git a/src/PowerShellGet/public/psgetfunctions/Update-Module.ps1 b/src/PowerShellGet/public/psgetfunctions/Update-Module.ps1 index 68447084..8a24deb4 100644 --- a/src/PowerShellGet/public/psgetfunctions/Update-Module.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Update-Module.ps1 @@ -81,51 +81,33 @@ function Update-Module { $PSGetItemInfos = @() - if ($Name) { - foreach ($moduleName in $Name) { - $GetPackageParameters['Name'] = $moduleName - $installedPackages = PackageManagement\Get-Package @GetPackageParameters - - if (-not $installedPackages -and -not (Test-WildcardPattern -Name $moduleName)) { - $availableModules = Get-Module -ListAvailable $moduleName -Verbose:$false | Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore - - if (-not $availableModules) { - $message = $LocalizedData.ModuleNotInstalledOnThisMachine -f ($moduleName) - Write-Error -Message $message -ErrorId 'ModuleNotInstalledOnThisMachine' -Category InvalidOperation -TargetObject $moduleName - } - else { - $message = $LocalizedData.ModuleNotInstalledUsingPowerShellGet -f ($moduleName) - Write-Error -Message $message -ErrorId 'ModuleNotInstalledUsingInstallModuleCmdlet' -Category InvalidOperation -TargetObject $moduleName - } - - continue - } + if (-not $Name) { + $Name = @('*') + } + + foreach ($moduleName in $Name) { + $GetPackageParameters['Name'] = $moduleName + $installedPackages = PackageManagement\Get-Package @GetPackageParameters - $installedPackages | - Microsoft.PowerShell.Core\ForEach-Object {New-PSGetItemInfo -SoftwareIdentity $_ -Type $script:PSArtifactTypeModule} | - Microsoft.PowerShell.Core\ForEach-Object { - if (-not (Test-RunningAsElevated) -and $_.InstalledLocation.StartsWith($script:programFilesModulesPath, [System.StringComparison]::OrdinalIgnoreCase)) { - if (-not (Test-WildcardPattern -Name $moduleName)) { - $message = $LocalizedData.AdminPrivilegesRequiredForUpdate -f ($_.Name, $_.InstalledLocation) - Write-Error -Message $message -ErrorId "AdminPrivilegesAreRequiredForUpdate" -Category InvalidOperation -TargetObject $moduleName - } - continue - } - - $PSGetItemInfos += $_ + if (-not $installedPackages -and -not (Test-WildcardPattern -Name $moduleName)) { + $availableModules = Get-Module -ListAvailable $moduleName -Verbose:$false | Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore + + if (-not $availableModules) { + $message = $LocalizedData.ModuleNotInstalledOnThisMachine -f ($moduleName) + Write-Error -Message $message -ErrorId 'ModuleNotInstalledOnThisMachine' -Category InvalidOperation -TargetObject $moduleName + } + else { + $message = $LocalizedData.ModuleNotInstalledUsingPowerShellGet -f ($moduleName) + Write-Error -Message $message -ErrorId 'ModuleNotInstalledUsingInstallModuleCmdlet' -Category InvalidOperation -TargetObject $moduleName } - } - } - else { - $PSGetItemInfos = PackageManagement\Get-Package @GetPackageParameters | - Microsoft.PowerShell.Core\ForEach-Object {New-PSGetItemInfo -SoftwareIdentity $_ -Type $script:PSArtifactTypeModule} | - Microsoft.PowerShell.Core\Where-Object { - (Test-RunningAsElevated) -or - $_.InstalledLocation.StartsWith($script:MyDocumentsModulesPath, [System.StringComparison]::OrdinalIgnoreCase) + continue } - } + $installedPackages | + Microsoft.PowerShell.Core\ForEach-Object { New-PSGetItemInfo -SoftwareIdentity $_ -Type $script:PSArtifactTypeModule} | + Microsoft.PowerShell.Core\ForEach-Object { $PSGetItemInfos += $_ } + } $PSBoundParameters["Provider"] = $script:PSModuleProviderName $PSBoundParameters[$script:PSArtifactType] = $script:PSArtifactTypeModule @@ -153,12 +135,7 @@ function Update-Module { $PSBoundParameters["PackageManagementProvider"] = $providerName $PSBoundParameters["InstallUpdate"] = $true - if ($psgetItemInfo.InstalledLocation.ToString().StartsWith($script:MyDocumentsModulesPath, [System.StringComparison]::OrdinalIgnoreCase)) { - $PSBoundParameters["Scope"] = "CurrentUser" - } - else { - $PSBoundParameters['Scope'] = 'AllUsers' - } + $PSBoundParameters["Scope"] = Get-InstallationScope -PreviousInstallLocation $psgetItemInfo.InstalledLocation -CurrentUserPath $script:MyDocumentsModulesPath $sid = PackageManagement\Install-Package @PSBoundParameters } diff --git a/src/PowerShellGet/public/psgetfunctions/Update-Script.ps1 b/src/PowerShellGet/public/psgetfunctions/Update-Script.ps1 index ceb2460f..75cf9db5 100644 --- a/src/PowerShellGet/public/psgetfunctions/Update-Script.ps1 +++ b/src/PowerShellGet/public/psgetfunctions/Update-Script.ps1 @@ -70,6 +70,10 @@ function Update-Script { return } + if(-not $Name) { + $Name = @('*') + } + if ($Name) { foreach ($scriptName in $Name) { $availableScriptPaths = Get-AvailableScriptFilePath -Name $scriptName -Verbose:$false @@ -81,19 +85,11 @@ function Update-Script { } foreach ($scriptFilePath in $availableScriptPaths) { + # Check if this script got installed with PowerShellGet $installedScriptFilePath = Get-InstalledScriptFilePath -Name ([System.IO.Path]::GetFileNameWithoutExtension($scriptFilePath)) | Microsoft.PowerShell.Core\Where-Object {$_ -eq $scriptFilePath } - # Check if this script got installed with PowerShellGet and user has required permissions if ($installedScriptFilePath) { - if (-not (Test-RunningAsElevated) -and $installedScriptFilePath.StartsWith($script:ProgramFilesScriptsPath, [System.StringComparison]::OrdinalIgnoreCase)) { - if (-not (Test-WildcardPattern -Name $scriptName)) { - $message = $LocalizedData.AdminPrivilegesRequiredForScriptUpdate -f ($scriptName, $installedScriptFilePath) - Write-Error -Message $message -ErrorId "AdminPrivilegesAreRequiredForUpdate" -Category InvalidOperation -TargetObject $scriptName - } - continue - } - $scriptFilePathsToUpdate += $installedScriptFilePath } else { @@ -106,19 +102,6 @@ function Update-Script { } } } - else { - $isRunningAsElevated = Test-RunningAsElevated - $installedScriptFilePaths = Get-InstalledScriptFilePath - - if ($isRunningAsElevated) { - $scriptFilePathsToUpdate = $installedScriptFilePaths - } - else { - # Update the scripts installed under - $scriptFilePathsToUpdate = $installedScriptFilePaths | Microsoft.PowerShell.Core\Where-Object { - $_.StartsWith($script:MyDocumentsScriptsPath, [System.StringComparison]::OrdinalIgnoreCase)} - } - } $PSBoundParameters["Provider"] = $script:PSModuleProviderName $PSBoundParameters[$script:PSArtifactType] = $script:PSArtifactTypeScript @@ -131,12 +114,10 @@ function Update-Script { $installedScriptInfoFileName = "$($scriptName)_$script:InstalledScriptInfoFileName" if ($scriptFilePath.ToString().StartsWith($script:MyDocumentsScriptsPath, [System.StringComparison]::OrdinalIgnoreCase)) { - $PSBoundParameters["Scope"] = "CurrentUser" $installedScriptInfoFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsInstalledScriptInfosPath ` -ChildPath $installedScriptInfoFileName } elseif ($scriptFilePath.ToString().StartsWith($script:ProgramFilesScriptsPath, [System.StringComparison]::OrdinalIgnoreCase)) { - $PSBoundParameters["Scope"] = "AllUsers" $installedScriptInfoFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:ProgramFilesInstalledScriptInfosPath ` -ChildPath $installedScriptInfoFileName @@ -182,6 +163,7 @@ function Update-Script { } $null = $PSBoundParameters.Remove("AllowPrerelease") + $PSBoundParameters["Scope"] = Get-InstallationScope -PreviousInstallLocation $scriptFilePath -CurrentUserPath $script:MyDocumentsScriptsPath $sid = PackageManagement\Install-Package @PSBoundParameters } } From b9fba013c9ec5ef691a6c02247ddfe044df4217f Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Tue, 26 Feb 2019 10:04:24 -0800 Subject: [PATCH 21/21] Update version and changelog (#430) --- CHANGELOG.md | 78 +++++++--------------------- src/PowerShellGet/PowerShellGet.psd1 | 23 +++++++- 2 files changed, 42 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38eb9d11..5cc86553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,63 +1,25 @@ # Changelog -## Unreleased - -- Made the list entries in the CHANGELOG.md to use dash `-` throughout to - be consequent (before there was a mix of dashes and asterisk). -- Update the AppVeyor CI test pipeline with a new job to run tests for - the DSC resources. - The new job uses the test framework used for the DSC Resource Kit, - the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) repository. -- Update .gitignore to ignore the [DscResource.Tests](https://github.com/PowerShell/DscResource.Tests) - test framework. When running unit test locally it is cloned into the - local repository folder. -- Added module PowerShellGet.LocalizationHelper containing localization - helper functions for DSC resources, and unit tests for the helper - functions. -- Moved helper functions for the DSC resource `PSModule` to the module - PowerShellGet.ResourceHelper. Added improved localization support, and - code formatting against workspace VS Code settings. - - Cleaned up the code against the [DscResources style guideline](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md). - - Renamed helper functions in the PowerShellGet.ResourceHelper module - to use Verb-Noun. - - Refactored the error handling logic to use the localization helper - functions, and also so that the error handling could be tested. -- Changes to PSModule. - - Added improved localization support. - - Changed type on the parameters that had `[Switch]` to correctly use - `Systen.Boolean` to match the schema.mof. - - Code formatting against workspace VS Code settings. - - Added unit tests. - - Added integration tests - - It is now possible to install a module and passing in `AllowClobber` - when the modules package source is trusted (it already worked in - other scenarios). - - Rephrased some of the localization strings. - - Cleaned up the code against the [DscResources style guideline](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md). - Suppressed some of the Script Analyzer rules that are not complaint - with the the Visual Studio Code workspace settings in this repository. - - Refactored the error handling logic to use the localization helper - functions, and also so that the error handling could be tested. - - Refactored the Get-TargetResource to return the correct hash table - when the current state is absent. - - Added new examples. -- Changed the AppVeyor CI build pipeline so it adds the DSC resources - and dependent helper modules (the `Modules` folder) to the AppVeyor - artifact. -- Added the `.MetaTestOptIn.json` file to opt-in for a lot of common test - in the DscResource.Tests test framework that tests the DSC resources. -- The examples under the folder `DSC/Examples` will be [published to PowerShell Gallery](https://github.com/PowerShell/DscResource.Tests#publish-examples-to-powershell-gallery) - so that they show up in the gallery part of Azure State Configuration. - The examples are published under the account 'dscresourcekit' which is - owned by the PowerShell DSC Team (DSC Resource Kit). - - In the folder `DSC/Examples` a `.gitattributes` was added to make sure - the examples is always checkout out using CRLF. There is an issue - using `Test-ScriptFileInfo` when files is checkout out using only LF - which is the default in AppVeyor. - - In the file `appveyor.yml` the PowerShell Gallery API key was added - for the account 'dscresourcekit', which can only be decrypted using - the PowerShell AppVeyor account. -- Added DSC resource PSRepository. +## 2.1.0 + +Breaking Change + +- Default installation scope for Update-Module and Update-Script has changed to match Install-Module and Install-Script. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times. + For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser. (#421) + +Bug Fixes + +- Update-ModuleManifest no longer clears FunctionsToExport, AliasesToExport, nor NestModules (#415 & #425) (Thanks @pougetat and @tnieto88!) +- Update-Module no longer changes repository URL (#407) +- Update-ModuleManifest no longer preprends 'PSGet_' to module name (#403) (Thanks @ThePoShWolf) +- Update-ModuleManifest now throws error and fails to update when provided invalid entries (#398) (Thanks @pougetat!) +- Ignore files no longer being included when uploading modules (#396) + +New Features + +- New DSC resource, PSRepository (#426) (Thanks @johlju!) +- Piping of PS respositories (#420) +- utf8 support for .nuspec (#419) ## 2.0.4 diff --git a/src/PowerShellGet/PowerShellGet.psd1 b/src/PowerShellGet/PowerShellGet.psd1 index 4c1d6139..4c82b748 100644 --- a/src/PowerShellGet/PowerShellGet.psd1 +++ b/src/PowerShellGet/PowerShellGet.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PSModule.psm1' -ModuleVersion = '2.0.4' +ModuleVersion = '2.1.0' GUID = '1d73a601-4a6c-43c5-ba3f-619b18bbb404' Author = 'Microsoft Corporation' CompanyName = 'Microsoft Corporation' @@ -54,6 +54,27 @@ PrivateData = @{ ProjectUri = 'https://go.microsoft.com/fwlink/?LinkId=828955' LicenseUri = 'https://go.microsoft.com/fwlink/?LinkId=829061' ReleaseNotes = @' +## 2.1.0 + +Breaking Change + +- Default installation scope for Update-Module and Update-Script has changed to match Install-Module and Install-Script. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times. + For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser. (#421) + +Bug Fixes + +- Update-ModuleManifest no longer clears FunctionsToExport, AliasesToExport, nor NestModules (#415 & #425) (Thanks @pougetat and @tnieto88!) +- Update-Module no longer changes repository URL (#407) +- Update-ModuleManifest no longer preprends 'PSGet_' to module name (#403) (Thanks @ThePoShWolf) +- Update-ModuleManifest now throws error and fails to update when provided invalid entries (#398) (Thanks @pougetat!) +- Ignore files no longer being included when uploading modules (#396) + +New Features + +- New DSC resource, PSRepository (#426) (Thanks @johlju!) +- Piping of PS respositories (#420) +- utf8 support for .nuspec (#419) + ## 2.0.4 Bug Fix