From 64ef6c178f3d0e69f35811a6b79c71da84e7551a Mon Sep 17 00:00:00 2001 From: gaelcolas Date: Mon, 3 Apr 2023 21:09:50 +0200 Subject: [PATCH 1/3] adding some fixes --- .build/tasks/DscResource.Test.build.ps1 | 266 +-------------- Sampler/Templates/Build/build.yaml.template | 4 +- .../chocobuild.yml.template | 4 +- build.yaml | 1 + tests/Unit/tasks/GuestConfig.build.Tests.ps1 | 307 ++++++++++-------- 5 files changed, 179 insertions(+), 403 deletions(-) diff --git a/.build/tasks/DscResource.Test.build.ps1 b/.build/tasks/DscResource.Test.build.ps1 index 07098ad3..db528f6c 100644 --- a/.build/tasks/DscResource.Test.build.ps1 +++ b/.build/tasks/DscResource.Test.build.ps1 @@ -44,268 +44,10 @@ param $BuildInfo = (property BuildInfo @{ }) ) -# Synopsis: Making sure the Module meets some quality standard (help, tests) -task Invoke_DscResource_Tests { - # Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables. - . Set-SamplerTaskVariable - $DscTestOutputFolder = Get-SamplerAbsolutePath -Path $DscTestOutputFolder -RelativeTo $OutputDirectory +# Synopsis: Deprecated HQRM task +task DscResource_Tests_Stop_On_Fail { + Write-Warning -Message "THIS TASK IS DEPRECATED! Please use Invoke_HQRM_Tests_Stop_On_Fail from the module DscResource.Test..." - "`tDSC Test Output Folder = '$DscTestOutputFolder'" - - $builtDscResourcesFolder = Get-SamplerAbsolutePath -Path 'DSCResources' -RelativeTo $builtModuleBase - - "`tBuilt DSC Resource Path = '$builtDscResourcesFolder'" - - if (-not (Test-Path -Path $DscTestOutputFolder)) - { - Write-Build -Color 'Yellow' -Text "Creating folder $DscTestOutputFolder" - - $null = New-Item -Path $DscTestOutputFolder -ItemType 'Directory' -Force -ErrorAction 'Stop' - } - - $DscTestScript = $DscTestScript.Where{ -not [System.String]::IsNullOrEmpty($_) } - $DscTestTag = $DscTestTag.Where{ -not [System.String]::IsNullOrEmpty($_) } - $DscTestExcludeTag = $DscTestExcludeTag.Where{ -not [System.String]::IsNullOrEmpty($_) } - - # Same parameters for both Pester 4 and Pester 5. - $defaultDscTestParams = @{ - PassThru = $true - } - - $isPester5 = (Get-Module -Name 'Pester').Version -ge '5.0.0' - - if ($isPester5) - { - $defaultDscTestParams['Output'] = 'Detailed' - } - else - { - $defaultDscTestParams['OutputFile'] = $DscTestOutputFullPath - $defaultDscTestParams['OutputFormat'] = 'NUnitXML' - } - - Import-Module -Name 'DscResource.Test' -ErrorAction 'Stop' - Import-Module -Name 'Pester' -MinimumVersion 4.0 -ErrorAction 'Stop' - - $dscTestCmd = Get-Command -Name Invoke-DscResourceTest - - <# - This will build the DscTest* variables (e.g. PesterScript, or - PesterOutputFormat) in this scope that are used in the rest of the code. - It will use values for the variables in the following order: - - 1. Skip creating the variable if a variable is already available because - it was already set in a passed parameter (Pester*). - 2. Use the value from a property in the build.yaml under the key 'Pester:'. - 3. Use the default value set previously in the variable $defaultPesterParams. - #> - foreach ($paramName in $dscTestCmd.Parameters.Keys) - { - if (($paramName -eq 'ExcludeTagFilter' -or $paramName -eq 'TagFilter') -and -not $isPester5) - { - $paramName = $paramName -replace 'Filter' - } - - $taskParamName = "DscTest$paramName" - - $DscTestBuildConfig = $BuildInfo.DscTest - - if (-not (Get-Variable -Name $taskParamName -ValueOnly -ErrorAction 'SilentlyContinue') -and ($DscTestBuildConfig)) - { - $paramValue = $DscTestBuildConfig.($paramName) - - # The Variable is set to '' so we should try to use the Config'd one if exists - if ($paramValue) - { - Write-Build -Color 'DarkGray' -Text "Using $taskParamName from Build Config" - - Set-Variable -Name $taskParamName -Value $paramValue - } # or use a default if available - elseif ($defaultDscTestParams.ContainsKey($paramName)) - { - Write-Build -Color 'DarkGray' -Text "Using $taskParamName from Defaults" - - Set-Variable -Name $taskParamName -Value $defaultDscTestParams.($paramName) - } - } - else - { - Write-Build -Color 'DarkGray' -Text "Using $taskParamName from Build Invocation Parameters" - } - } - - "`tTest Scripts = $($DscTestScript -join ', ')" - "`tTags = $($DscTestTag -join ', ')" - "`tExclude Tags = $($DscTestExcludeTag -join ', ')" - - $os = Get-OperatingSystemShortName - - $psVersion = 'PSv.{0}' -f $PSVersionTable.PSVersion - $DscTestOutputFileFileName = "DscTest_{0}_v{1}.{2}.{3}.xml" -f $ProjectName, $ModuleVersion, $os, $psVersion - $DscTestOutputFullPath = Join-Path -Path $DscTestOutputFolder -ChildPath "$($DscTestOutputFormat)_$DscTestOutputFileFileName" - - $dscTestParams = @{ - PassThru = $true - } - - if ($isPester5) - { - $dscTestParams['Output'] = $DscTestOutput - } - else - { - $dscTestParams['OutputFormat'] = $DscTestOutputFormat - $dscTestParams['OutputFile'] = $DscTestOutputFullPath - } - - if ($DscTestModule) - { - $dscTestParams.Add('Module', $DscTestModule) - } - elseif ($DscTestFullyQualifiedModule) - { - $dscTestParams.Add('FullyQualifiedModule', $DscTestFullyQualifiedModule) - } - else - { - $dscTestParams.Add('ProjectPath', $ProjectPath) - } - - if ($DscTestExcludeTag.Count -gt 0) - { - $dscTestParams.Add('ExcludeTag', $DscTestExcludeTag) - } - - if ($DscTestTag.Count -gt 0) - { - $dscTestParams.Add('Tag', $DscTestTag) - } - - # Test folders is specified, override invoke-DscResourceTest internal default - if ($DscTestScript.Count -gt 0) - { - $dscTestParams.Add('Path', @()) - - Write-Build -Color 'DarkGray' -Text " Adding DscTestScript to params" - - foreach ($testFolder in $DscTestScript) - { - if (-not (Split-Path -IsAbsolute $testFolder)) - { - $testFolder = Join-Path -Path $ProjectPath -ChildPath $testFolder - } - - Write-Build -Color 'DarkGray' -Text " ... $testFolder" - - <# - The Absolute path to this folder exists, adding to the list of - DscTest scripts to run. - #> - if (Test-Path -Path $testFolder) - { - if ($isPester5) - { - $dscTestParams.Path += $testFolder - } - else - { - $dscTestParams.Script += $testFolder - } - } - } - } - - # Add all DscTest* variables in current scope into the $dscTestParams hashtable. - foreach ($paramName in $DscTestCmd.Parameters.keys) - { - $paramValueFromScope = (Get-Variable -Name "DscTest$paramName" -ValueOnly -ErrorAction 'SilentlyContinue') - - if (-not $dscTestParams.ContainsKey($paramName) -and $paramValueFromScope) - { - $dscTestParams.Add($paramName, $paramValueFromScope) - } - } - - Write-Verbose -Message ($dscTestParams | ConvertTo-Json) - - $script:testResults = Invoke-DscResourceTest @dscTestParams - - $DscTestResultObjectCliXml = Join-Path -Path $DscTestOutputFolder -ChildPath "DscTestObject_$DscTestOutputFileFileName" - - $null = $script:testResults | Export-CliXml -Path $DscTestResultObjectCliXml -Force -} - -# Synopsis: This task ensures the build job fails if the test aren't successful. -task Fail_Build_If_DscResource_Tests_Failed { - "Asserting that no test failed" - "" - - # Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables. - . Set-SamplerTaskVariable - - $DscTestOutputFolder = Get-SamplerAbsolutePath -Path $DscTestOutputFolder -RelativeTo $OutputDirectory - - $os = Get-OperatingSystemShortName - - $builtDscResourcesFolder = Get-SamplerAbsolutePath -Path 'DSCResources' -RelativeTo $builtModuleBase - - "`tBuilt DSC Resource Path = '$builtDscResourcesFolder'" - - $psVersion = 'PSv.{0}' -f $PSVersionTable.PSVersion - $DscTestOutputFileFileName = "DscTest_{0}_v{1}.{2}.{3}.xml" -f $ProjectName, $ModuleVersion, $os, $psVersion - $DscTestResultObjectClixml = Join-Path -Path $DscTestOutputFolder -ChildPath "DscTestObject_$DscTestOutputFileFileName" - - "`tDscTest Output Object = $DscTestResultObjectClixml" - - if (-not (Test-Path -Path $DscTestResultObjectClixml)) - { - throw "No command were tested. $DscTestResultObjectClixml not found" - } - else - { - $DscTestObject = Import-Clixml -Path $DscTestResultObjectClixml -ErrorAction 'Stop' - - Assert-Build -Condition ($DscTestObject.FailedCount -eq 0) -Message ('Failed {0} tests. Aborting Build' -f $DscTestObject.FailedCount) - } + throw "THIS TASK IS DEPRECATED! Please use Invoke_HQRM_Tests_Stop_On_Fail from the module DscResource.Test..." } - -# Synopsis: Uploading Unit Test results to AppVeyor -task Upload_DscResourceTest_Results_To_AppVeyor -If { (property BuildSystem 'unknown') -eq 'AppVeyor' } { - # Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables. - . Set-SamplerTaskVariable - - $DscTestOutputFolder = Get-SamplerAbsolutePath -Path $DscTestOutputFolder -RelativeTo $OutputDirectory - - if (-not (Test-Path -Path $DscTestOutputFolder)) - { - Write-Build -Color 'Yellow' -Text "Creating folder $DscTestOutputFolder" - - $null = New-Item -Path $DscTestOutputFolder -ItemType Directory -Force -ErrorAction 'Stop' - } - - $os = Get-OperatingSystemShortName - - $builtDscResourcesFolder = Get-SamplerAbsolutePath -Path 'DSCResources' -RelativeTo $builtModuleBase - - "`tBuilt DSC Resource Path = '$builtDscResourcesFolder'" - - $psVersion = 'PSv.{0}' -f $PSVersionTable.PSVersion - $DscTestOutputFileFileName = "DscResource.Test_{0}_v{1}.{2}.{3}.xml" -f $ProjectName, $ModuleVersion, $os, $psVersion - - $DscTestOutputFullPath = Join-Path -Path $DscTestOutputFolder -ChildPath "$($DscTestOutputFormat)_$DscTestOutputFileFileName" - - $testResultFile = Get-Item -Path $DscTestOutputFullPath -ErrorAction 'Ignore' - - if ($testResultFile) - { - Write-Build -Color 'Green' -Text " Uploading test results $testResultFile to Appveyor" - - $testResultFile | Add-TestResultToAppveyor - - Write-Build -Color 'Green' -Text " Upload Complete" - } -} - -# Synopsis: Meta task that runs Quality Tests, and fails if they're not successful -task DscResource_Tests_Stop_On_Fail Invoke_DscResource_Tests, Upload_DscResourceTest_Results_To_AppVeyor, Fail_Build_If_DscResource_Tests_Failed diff --git a/Sampler/Templates/Build/build.yaml.template b/Sampler/Templates/Build/build.yaml.template index 5626c34e..ad5fa9fe 100644 --- a/Sampler/Templates/Build/build.yaml.template +++ b/Sampler/Templates/Build/build.yaml.template @@ -133,7 +133,7 @@ BuildWorkflow: if($PLASTER_PARAM_ModuleType -in @('dsccommunity')) { @" hqrmtest: - - DscResource_Tests_Stop_On_Fail + - Invoke_HQRM_Tests_Stop_On_Fail "@ } %> @@ -288,6 +288,8 @@ ModuleBuildTasks: @" Sampler.GitHubTasks: - '*.ib.tasks' + DscResource.Test: + - 'Task.*' "@ } %> diff --git a/Sampler/Templates/ChocolateyPipeline/chocobuild.yml.template b/Sampler/Templates/ChocolateyPipeline/chocobuild.yml.template index 8845e3dd..b2b7ab4a 100644 --- a/Sampler/Templates/ChocolateyPipeline/chocobuild.yml.template +++ b/Sampler/Templates/ChocolateyPipeline/chocobuild.yml.template @@ -22,7 +22,7 @@ BuildWorkflow: - build hqrmtest: - - DscResource_Tests_Stop_On_Fail + - Invoke_HQRM_Tests_Stop_On_Fail # Defining test task to be run when invoking `./build.ps1 -Tasks test` test: @@ -102,6 +102,8 @@ ModuleBuildTasks: - '*.build.Sampler.ib.tasks' Sampler.GitHubTasks: - '*.ib.tasks' + DscResource.Test: + - 'Task.*' # Invoke-Build Header to be used to 'decorate' the terminal output of the tasks. TaskHeader: | diff --git a/build.yaml b/build.yaml index c1aafe76..14e473bc 100644 --- a/build.yaml +++ b/build.yaml @@ -174,6 +174,7 @@ TaskHeader: | GitHubConfig: GitHubFilesToAdd: - 'CHANGELOG.md' + ReleaseAssets: GitHubConfigUserName: gaelcolas GitHubConfigUserEmail: gaelcolas@synedgy.com UpdateChangelogOnPrerelease: false diff --git a/tests/Unit/tasks/GuestConfig.build.Tests.ps1 b/tests/Unit/tasks/GuestConfig.build.Tests.ps1 index 7cc07134..10c8d849 100644 --- a/tests/Unit/tasks/GuestConfig.build.Tests.ps1 +++ b/tests/Unit/tasks/GuestConfig.build.Tests.ps1 @@ -1,160 +1,189 @@ -BeforeAll { - $script:moduleName = 'Sampler' +param +( + [Parameter()] + [System.String] + $ProjectName = (property ProjectName ''), + + [Parameter()] + [System.String] + $SourcePath = (property SourcePath ''), + + [Parameter()] + [System.String] + $GCPackagesPath = (property GCPackagesPath 'GCPackages'), + + [Parameter()] + [System.String] + $GCPackagesOutputPath = (property GCPackagesOutputPath 'GCPackages'), + + [Parameter()] + [System.String] + $GCPoliciesPath = (property GCPoliciesPath 'GCPolicies'), + + [Parameter()] + [System.String] + $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot "output")), + + [Parameter()] + [System.String] + $BuiltModuleSubdirectory = (property BuiltModuleSubdirectory ''), + + [Parameter()] + [System.String] + $BuildModuleOutput = (property BuildModuleOutput (Join-Path $OutputDirectory $BuiltModuleSubdirectory)), + + [Parameter()] + [System.String] + $ModuleVersion = (property ModuleVersion ''), + + [Parameter()] + [System.Collections.Hashtable] + $BuildInfo = (property BuildInfo @{ }) +) + +# SYNOPSIS: Building the Azure Policy Guest Configuration Packages +task build_guestconfiguration_packages { + # Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables. + . Set-SamplerTaskVariable -AsNewBuild + + if (-not (Split-Path -IsAbsolute $GCPackagesPath)) + { + $GCPackagesPath = Join-Path -Path $SourcePath -ChildPath $GCPackagesPath + } - # If the module is not found, run the build task 'noop'. - if (-not (Get-Module -Name $script:moduleName -ListAvailable)) + if (-not (Split-Path -IsAbsolute $GCPoliciesPath)) { - # Redirect all streams to $null, except the error stream (stream 2) - & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + $GCPoliciesPath = Join-Path -Path $SourcePath -ChildPath $GCPoliciesPath } - # Re-import the module using force to get any code changes between runs. - Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' -} + "`tBuild Module Output = $BuildModuleOutput" + "`tGC Packages Path = $GCPackagesPath" + "`tGC Policies Path = $GCPoliciesPath" + "`t------------------------------------------------`r`n" + + Get-ChildItem -Path $GCPackagesPath -Directory -ErrorAction SilentlyContinue | ForEach-Object -Process { + Write-Build Magenta "`r`n`tPackaging Guest Configuration Package '$($_.Name)'" + $GCPackageName = $_.Name + $ConfigurationFile = Join-Path -Path $_.FullName -ChildPath ('{0}.config.ps1' -f $GCPackageName) + $newPackageParamsFile = Join-Path -Path $_.FullName -ChildPath ('{0}.psd1' -f $GCPackageName) + $MOFFile = Join-Path -Path $_.FullName -ChildPath ('{0}.mof' -f $GCPackageName) + + if (-not (Test-Path -Path $ConfigurationFile) -and -not (Test-Path -Path $MOFFile)) + { + throw "The configuration '$ConfigurationFile' could not be found. Cannot compile MOF for '$GCPackageName' policy Package" + } -AfterAll { - Remove-Module -Name $script:moduleName -} + if (Test-Path -Path $MOFFile) + { + Write-Build Magenta "`t Creating GC Package from MOF file: '$MOFFile'" + } + else + { + Write-Build DarkGray "`t Creating GC Package from Configuration file: '$ConfigurationFile'" + try + { + $MOFFileAndErrors = &{ + . $ConfigurationFile + &$GCPackageName -OutputPath (Join-Path -Path $OutputDirectory -ChildPath 'MOFs') -ErrorAction SilentlyContinue + } 2>&1 + + $CompilationErrors = @() + $MOFFile = $MOFFileAndErrors.Foreach{ + if ($_ -isnot [System.Management.Automation.ErrorRecord]) + { + # If the MOF name is localhost.mof, mv to PackageName.mof + $_ + } + else + { + $CompilationErrors += $_ + } + } -Describe 'GuestConfig' { - It 'Should have exported the alias correct' { - $taskAlias = Get-Alias -Name 'GuestConfig.build.Sampler.ib.tasks' + $MOFFile = [string]($MOFFile[0]) # ensure it's a single string + Write-Build White "`t Compiled '$MOFFile'." - $taskAlias.Name | Should -Be 'GuestConfig.build.Sampler.ib.tasks' - $taskAlias.ReferencedCommand | Should -Be 'GuestConfig.build.ps1' - $taskAlias.Definition | Should -Match 'Sampler[\/|\\]\d+\.\d+\.\d+[\/|\\]tasks[\/|\\]GuestConfig\.build\.ps1' - } -} + if ((Split-Path -Leaf -Path $MOFFile -ErrorAction 'SilentlyContinue') -eq 'localhost.mof') + { + $destinationMof = Join-Path -Path (Join-Path -Path $OutputDirectory -ChildPath 'MOFs') -ChildPath ('{0}.mof' -f $GCPackageName) + Write-Build DarkGray "`t Renaming MOF to '$destinationMof'." + $null = Move-Item -Path $MOFFile -Destination $destinationMof -Force -ErrorAction Stop + $MOFFile = $destinationMof + } + } + catch + { + throw "Compilation error. $($_.Exception.Message)" + } + } -Describe 'build_guestconfiguration_packages' { - BeforeAll { - # Dot-source mocks - . $PSScriptRoot/../TestHelpers/MockSetSamplerTaskVariable + if (Test-Path -Path $newPackageParamsFile) + { + $newPackageExtraParams = Import-PowerShellDataFile -Path $newPackageParamsFile -ErrorAction 'Stop' + Write-Build DarkGray "`t Using extra parameters from '$newPackageParamsFile'." + } + else + { + $newPackageExtraParams = @{} + } - $taskAlias = Get-Alias -Name 'GuestConfig.build.Sampler.ib.tasks' + Write-Verbose -Message "Package Name '$GCPackageName' with Configuration '$MOFFile', OutputDirectory $OutputDirectory, GCPackagesOutputPath '$GCPackagesOutputPath'." + $GCPackageOutput = Get-SamplerAbsolutePath -Path $GCPackagesOutputPath -RelativeTo $OutputDirectory - $mockTaskParameters = @{ - SourcePath = Join-Path -Path $TestDrive -ChildPath 'MyModule/source' - ProjectName = 'MyModule' - OutputDirectory = Join-Path -Path $TestDrive -ChildPath 'output' + $NewGCPackageParams = @{ + Configuration = [string]$MOFFile + Name = $GCPackageName + Path = $GCPackageOutput + Force = $true + Version = $ModuleVersion + Type = 'AuditAndSet' } - } - Context 'When using MOF file' { - BeforeAll { - # Stub function to be able to mock the command. - function New-GuestConfigurationPackage {} - - Mock -CommandName Get-ChildItem -ParameterFilter { - $Path -match 'GCPackages' - } -MockWith { - return @{ - Name = 'GCPackage1' - FullName = $TestDrive | Join-Path -ChildPath 'GCPackage1' - } - } + foreach ($paramName in (Get-Command -Name 'New-GuestConfigurationPackage' -ErrorAction Stop).Parameters.Keys.Where({$_ -in $newPackageExtraParams.Keys})) + { + Write-Verbose -Message "`t Testing for parameter '$paramName'." + Write-Build DarkGray "`t`t Using configured parameter '$paramName' with value '$($newPackageExtraParams[$paramName])'." + # Override the Parameters from the $GCPackageName.psd1 + $NewGCPackageParams[$paramName] = $newPackageExtraParams[$paramName] + } - Mock -CommandName Test-Path -ParameterFilter { - $Path -match 'GCPackage1' - } -MockWith { - return $true + $ZippedGCPackage = (@(&{ + New-GuestConfigurationPackage @NewGCPackageParams + } 2>&1)).Where{ + if ($_ -isnot [System.Management.Automation.ErrorRecord]) + { + # Filter out the Error records from New-GuestConfigurationPackage + $true } - - Mock -CommandName Test-Path -ParameterFilter { - $Path -match '\.mof' - } -MockWith { - return $true + elseif ($_.Exception.Message -notmatch '^A second CIM class definition') + { + # Write non-terminating errors that are not "A second CIM class definition for .... was found..." + $false + Write-Error $_ -ErrorAction Continue } - - Mock -CommandName Import-PowerShellDataFile - Mock -CommandName New-GuestConfigurationPackage -MockWith { - return @{ - Path = $TestDrive | Join-Path -ChildPath 'GCPackage1' - } + else + { + $false } - - Mock -CommandName Move-Item } - It 'Should run the build task without throwing' { - { - Invoke-Build -Task 'build_guestconfiguration_packages' -File $taskAlias.Definition @mockTaskParameters - } | Should -Not -Throw + Write-Build DarkGray "`t Zips created, you may want to delete the unzipped folders under '$GCPackagesOutputPath'..." + + if ($ModuleVersion) + { + $GCPackageWithVersionZipName = ('{0}_{1}.zip' -f $GCPackageName,$ModuleVersion) + $GCPackageOutputPath = Get-SamplerAbsolutePath -Path $GCPackagesOutputPath -RelativeTo $OutputDirectory + $versionedGCPackageName = Join-Path -Path $GCPackageOutputPath -ChildPath $GCPackageWithVersionZipName + Write-Build DarkGray "`t Renaming Zip as '$versionedGCPackageName'." + $ZippedGCPackagePath = Move-Item -Path $ZippedGCPackage.Path -Destination $versionedGCPackageName -Force -PassThru + $ZippedGCPackage = @{ + Name = $ZippedGCPackage.Name + Path = $ZippedGCPackagePath.FullName + } } - } - <# - TODO: The below test does not work since the script block to compiles the - configuration sometimes throws the error: - Cannot process argument transformation on parameter 'ResourceModuleTuplesToImport'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "System.Tuple`3[System.String[],Microsoft.PowerShell.Commands.ModuleSpecification[],System.Version]". - #> - -# Context 'When using configuration file' { -# BeforeAll { -# # Stub function to be able to mock the command. -# function New-GuestConfigurationPackage {} - -# $mockGCPackagePath = $TestDrive | Join-Path -ChildPath 'GCPackage1' - -# New-Item -Path $mockGCPackagePath -ItemType Directory -Force | Out-Null - -# # Create the mock output folder so configuration can be compiled. -# New-Item -Path ($TestDrive | Join-Path -ChildPath 'output/MOFs') -ItemType Directory -Force | Out-Null - -# $mockConfigurationScriptFile = @' -# Configuration GCPackage1 -# { -# Node "localhost" -# { -# File DirectoryCopy -# { -# Ensure = "Present" -# Type = "Directory" -# Recurse = $true -# SourcePath = "\\PullServer\DemoSource" -# DestinationPath = "C:\Users\Public\Documents\DSCDemo\DemoDestination" -# } -# } -# } -# '@ - -# $mockConfigurationScriptFile | Out-File -FilePath ($mockGCPackagePath | Join-Path -ChildPath 'GCPackage1.config.ps1') -Encoding UTF8 -Force - -# Mock -CommandName Get-ChildItem -ParameterFilter { -# $Path -match 'GCPackages' -# } -MockWith { -# return @{ -# Name = 'GCPackage1' -# FullName = $mockGCPackagePath -# } -# } - -# Mock -CommandName Test-Path -ParameterFilter { -# $Path -match 'GCPackage1' -# } -MockWith { -# return $true -# } - -# Mock -CommandName Test-Path -ParameterFilter { -# $Path -match '\.mof' -# } -MockWith { -# return $false -# } - -# Mock -CommandName Import-PowerShellDataFile -# Mock -CommandName New-GuestConfigurationPackage -MockWith { -# return @{ -# Path = $TestDrive | Join-Path -ChildPath 'GCPackage1' -# } -# } - -# Mock -CommandName Move-Item -# } - -# It 'Should run the build task without throwing' { -# { -# Invoke-Build -Task 'build_guestconfiguration_packages' -File $taskAlias.Definition @mockTaskParameters -# } | Should -Not -Throw -# } -# } + Write-Build Green "`tZipped Guest Config Package: $($ZippedGCPackage.Path)" + } } + +task gcpack clean,build,build_guestconfiguration_packages From 87cb27997cc987b5bc063bbb89dd6b34c9c0797b Mon Sep 17 00:00:00 2001 From: gaelcolas Date: Sat, 22 Apr 2023 21:21:23 +0200 Subject: [PATCH 2/3] reverting changes to GC test file --- tests/Unit/tasks/GuestConfig.build.Tests.ps1 | 307 +++++++++---------- 1 file changed, 139 insertions(+), 168 deletions(-) diff --git a/tests/Unit/tasks/GuestConfig.build.Tests.ps1 b/tests/Unit/tasks/GuestConfig.build.Tests.ps1 index 10c8d849..7cc07134 100644 --- a/tests/Unit/tasks/GuestConfig.build.Tests.ps1 +++ b/tests/Unit/tasks/GuestConfig.build.Tests.ps1 @@ -1,189 +1,160 @@ -param -( - [Parameter()] - [System.String] - $ProjectName = (property ProjectName ''), - - [Parameter()] - [System.String] - $SourcePath = (property SourcePath ''), - - [Parameter()] - [System.String] - $GCPackagesPath = (property GCPackagesPath 'GCPackages'), - - [Parameter()] - [System.String] - $GCPackagesOutputPath = (property GCPackagesOutputPath 'GCPackages'), - - [Parameter()] - [System.String] - $GCPoliciesPath = (property GCPoliciesPath 'GCPolicies'), - - [Parameter()] - [System.String] - $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot "output")), - - [Parameter()] - [System.String] - $BuiltModuleSubdirectory = (property BuiltModuleSubdirectory ''), - - [Parameter()] - [System.String] - $BuildModuleOutput = (property BuildModuleOutput (Join-Path $OutputDirectory $BuiltModuleSubdirectory)), - - [Parameter()] - [System.String] - $ModuleVersion = (property ModuleVersion ''), - - [Parameter()] - [System.Collections.Hashtable] - $BuildInfo = (property BuildInfo @{ }) -) - -# SYNOPSIS: Building the Azure Policy Guest Configuration Packages -task build_guestconfiguration_packages { - # Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables. - . Set-SamplerTaskVariable -AsNewBuild - - if (-not (Split-Path -IsAbsolute $GCPackagesPath)) - { - $GCPackagesPath = Join-Path -Path $SourcePath -ChildPath $GCPackagesPath - } +BeforeAll { + $script:moduleName = 'Sampler' - if (-not (Split-Path -IsAbsolute $GCPoliciesPath)) + # If the module is not found, run the build task 'noop'. + if (-not (Get-Module -Name $script:moduleName -ListAvailable)) { - $GCPoliciesPath = Join-Path -Path $SourcePath -ChildPath $GCPoliciesPath + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null } - "`tBuild Module Output = $BuildModuleOutput" - "`tGC Packages Path = $GCPackagesPath" - "`tGC Policies Path = $GCPoliciesPath" - "`t------------------------------------------------`r`n" - - Get-ChildItem -Path $GCPackagesPath -Directory -ErrorAction SilentlyContinue | ForEach-Object -Process { - Write-Build Magenta "`r`n`tPackaging Guest Configuration Package '$($_.Name)'" - $GCPackageName = $_.Name - $ConfigurationFile = Join-Path -Path $_.FullName -ChildPath ('{0}.config.ps1' -f $GCPackageName) - $newPackageParamsFile = Join-Path -Path $_.FullName -ChildPath ('{0}.psd1' -f $GCPackageName) - $MOFFile = Join-Path -Path $_.FullName -ChildPath ('{0}.mof' -f $GCPackageName) - - if (-not (Test-Path -Path $ConfigurationFile) -and -not (Test-Path -Path $MOFFile)) - { - throw "The configuration '$ConfigurationFile' could not be found. Cannot compile MOF for '$GCPackageName' policy Package" - } + # Re-import the module using force to get any code changes between runs. + Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' +} - if (Test-Path -Path $MOFFile) - { - Write-Build Magenta "`t Creating GC Package from MOF file: '$MOFFile'" - } - else - { - Write-Build DarkGray "`t Creating GC Package from Configuration file: '$ConfigurationFile'" - try - { - $MOFFileAndErrors = &{ - . $ConfigurationFile - &$GCPackageName -OutputPath (Join-Path -Path $OutputDirectory -ChildPath 'MOFs') -ErrorAction SilentlyContinue - } 2>&1 - - $CompilationErrors = @() - $MOFFile = $MOFFileAndErrors.Foreach{ - if ($_ -isnot [System.Management.Automation.ErrorRecord]) - { - # If the MOF name is localhost.mof, mv to PackageName.mof - $_ - } - else - { - $CompilationErrors += $_ - } - } +AfterAll { + Remove-Module -Name $script:moduleName +} - $MOFFile = [string]($MOFFile[0]) # ensure it's a single string - Write-Build White "`t Compiled '$MOFFile'." +Describe 'GuestConfig' { + It 'Should have exported the alias correct' { + $taskAlias = Get-Alias -Name 'GuestConfig.build.Sampler.ib.tasks' - if ((Split-Path -Leaf -Path $MOFFile -ErrorAction 'SilentlyContinue') -eq 'localhost.mof') - { - $destinationMof = Join-Path -Path (Join-Path -Path $OutputDirectory -ChildPath 'MOFs') -ChildPath ('{0}.mof' -f $GCPackageName) - Write-Build DarkGray "`t Renaming MOF to '$destinationMof'." - $null = Move-Item -Path $MOFFile -Destination $destinationMof -Force -ErrorAction Stop - $MOFFile = $destinationMof - } - } - catch - { - throw "Compilation error. $($_.Exception.Message)" - } - } - - if (Test-Path -Path $newPackageParamsFile) - { - $newPackageExtraParams = Import-PowerShellDataFile -Path $newPackageParamsFile -ErrorAction 'Stop' - Write-Build DarkGray "`t Using extra parameters from '$newPackageParamsFile'." - } - else - { - $newPackageExtraParams = @{} - } + $taskAlias.Name | Should -Be 'GuestConfig.build.Sampler.ib.tasks' + $taskAlias.ReferencedCommand | Should -Be 'GuestConfig.build.ps1' + $taskAlias.Definition | Should -Match 'Sampler[\/|\\]\d+\.\d+\.\d+[\/|\\]tasks[\/|\\]GuestConfig\.build\.ps1' + } +} - Write-Verbose -Message "Package Name '$GCPackageName' with Configuration '$MOFFile', OutputDirectory $OutputDirectory, GCPackagesOutputPath '$GCPackagesOutputPath'." - $GCPackageOutput = Get-SamplerAbsolutePath -Path $GCPackagesOutputPath -RelativeTo $OutputDirectory +Describe 'build_guestconfiguration_packages' { + BeforeAll { + # Dot-source mocks + . $PSScriptRoot/../TestHelpers/MockSetSamplerTaskVariable - $NewGCPackageParams = @{ - Configuration = [string]$MOFFile - Name = $GCPackageName - Path = $GCPackageOutput - Force = $true - Version = $ModuleVersion - Type = 'AuditAndSet' - } + $taskAlias = Get-Alias -Name 'GuestConfig.build.Sampler.ib.tasks' - foreach ($paramName in (Get-Command -Name 'New-GuestConfigurationPackage' -ErrorAction Stop).Parameters.Keys.Where({$_ -in $newPackageExtraParams.Keys})) - { - Write-Verbose -Message "`t Testing for parameter '$paramName'." - Write-Build DarkGray "`t`t Using configured parameter '$paramName' with value '$($newPackageExtraParams[$paramName])'." - # Override the Parameters from the $GCPackageName.psd1 - $NewGCPackageParams[$paramName] = $newPackageExtraParams[$paramName] + $mockTaskParameters = @{ + SourcePath = Join-Path -Path $TestDrive -ChildPath 'MyModule/source' + ProjectName = 'MyModule' + OutputDirectory = Join-Path -Path $TestDrive -ChildPath 'output' } + } - $ZippedGCPackage = (@(&{ - New-GuestConfigurationPackage @NewGCPackageParams - } 2>&1)).Where{ - if ($_ -isnot [System.Management.Automation.ErrorRecord]) - { - # Filter out the Error records from New-GuestConfigurationPackage - $true + Context 'When using MOF file' { + BeforeAll { + # Stub function to be able to mock the command. + function New-GuestConfigurationPackage {} + + Mock -CommandName Get-ChildItem -ParameterFilter { + $Path -match 'GCPackages' + } -MockWith { + return @{ + Name = 'GCPackage1' + FullName = $TestDrive | Join-Path -ChildPath 'GCPackage1' + } } - elseif ($_.Exception.Message -notmatch '^A second CIM class definition') - { - # Write non-terminating errors that are not "A second CIM class definition for .... was found..." - $false - Write-Error $_ -ErrorAction Continue + + Mock -CommandName Test-Path -ParameterFilter { + $Path -match 'GCPackage1' + } -MockWith { + return $true } - else - { - $false + + Mock -CommandName Test-Path -ParameterFilter { + $Path -match '\.mof' + } -MockWith { + return $true } - } - Write-Build DarkGray "`t Zips created, you may want to delete the unzipped folders under '$GCPackagesOutputPath'..." - - if ($ModuleVersion) - { - $GCPackageWithVersionZipName = ('{0}_{1}.zip' -f $GCPackageName,$ModuleVersion) - $GCPackageOutputPath = Get-SamplerAbsolutePath -Path $GCPackagesOutputPath -RelativeTo $OutputDirectory - $versionedGCPackageName = Join-Path -Path $GCPackageOutputPath -ChildPath $GCPackageWithVersionZipName - Write-Build DarkGray "`t Renaming Zip as '$versionedGCPackageName'." - $ZippedGCPackagePath = Move-Item -Path $ZippedGCPackage.Path -Destination $versionedGCPackageName -Force -PassThru - $ZippedGCPackage = @{ - Name = $ZippedGCPackage.Name - Path = $ZippedGCPackagePath.FullName + Mock -CommandName Import-PowerShellDataFile + Mock -CommandName New-GuestConfigurationPackage -MockWith { + return @{ + Path = $TestDrive | Join-Path -ChildPath 'GCPackage1' + } } + + Mock -CommandName Move-Item } - Write-Build Green "`tZipped Guest Config Package: $($ZippedGCPackage.Path)" + It 'Should run the build task without throwing' { + { + Invoke-Build -Task 'build_guestconfiguration_packages' -File $taskAlias.Definition @mockTaskParameters + } | Should -Not -Throw + } } -} -task gcpack clean,build,build_guestconfiguration_packages + <# + TODO: The below test does not work since the script block to compiles the + configuration sometimes throws the error: + Cannot process argument transformation on parameter 'ResourceModuleTuplesToImport'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "System.Tuple`3[System.String[],Microsoft.PowerShell.Commands.ModuleSpecification[],System.Version]". + #> + +# Context 'When using configuration file' { +# BeforeAll { +# # Stub function to be able to mock the command. +# function New-GuestConfigurationPackage {} + +# $mockGCPackagePath = $TestDrive | Join-Path -ChildPath 'GCPackage1' + +# New-Item -Path $mockGCPackagePath -ItemType Directory -Force | Out-Null + +# # Create the mock output folder so configuration can be compiled. +# New-Item -Path ($TestDrive | Join-Path -ChildPath 'output/MOFs') -ItemType Directory -Force | Out-Null + +# $mockConfigurationScriptFile = @' +# Configuration GCPackage1 +# { +# Node "localhost" +# { +# File DirectoryCopy +# { +# Ensure = "Present" +# Type = "Directory" +# Recurse = $true +# SourcePath = "\\PullServer\DemoSource" +# DestinationPath = "C:\Users\Public\Documents\DSCDemo\DemoDestination" +# } +# } +# } +# '@ + +# $mockConfigurationScriptFile | Out-File -FilePath ($mockGCPackagePath | Join-Path -ChildPath 'GCPackage1.config.ps1') -Encoding UTF8 -Force + +# Mock -CommandName Get-ChildItem -ParameterFilter { +# $Path -match 'GCPackages' +# } -MockWith { +# return @{ +# Name = 'GCPackage1' +# FullName = $mockGCPackagePath +# } +# } + +# Mock -CommandName Test-Path -ParameterFilter { +# $Path -match 'GCPackage1' +# } -MockWith { +# return $true +# } + +# Mock -CommandName Test-Path -ParameterFilter { +# $Path -match '\.mof' +# } -MockWith { +# return $false +# } + +# Mock -CommandName Import-PowerShellDataFile +# Mock -CommandName New-GuestConfigurationPackage -MockWith { +# return @{ +# Path = $TestDrive | Join-Path -ChildPath 'GCPackage1' +# } +# } + +# Mock -CommandName Move-Item +# } + +# It 'Should run the build task without throwing' { +# { +# Invoke-Build -Task 'build_guestconfiguration_packages' -File $taskAlias.Definition @mockTaskParameters +# } | Should -Not -Throw +# } +# } +} From 52f223785c8677011f1222aa3c95cb2b1c944cfb Mon Sep 17 00:00:00 2001 From: gaelcolas Date: Tue, 25 Apr 2023 07:31:25 +0200 Subject: [PATCH 3/3] updating changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee577c3..be8470a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Deprecate the use of the task `DscResource_Tests_Stop_On_Fail` and warn the user to use `Invoke_HQRM_Tests_Stop_On_Fail` instead. +- Updated the choco and build templates to use `Invoke_HQRM_Tests_Stop_On_Fail` by default. + ## [0.116.5] - 2023-04-19 - Fix Azure Pipeline bug to resolve errors and delays during the build process. Shallow fetch has been disabled to ensure complete repository cloning. Fixes [#424](https://github.com/gaelcolas/Sampler/issues/424)