diff --git a/Tests/Engine/ModuleDependencyHandler.tests.ps1 b/Tests/Engine/ModuleDependencyHandler.tests.ps1 index 00de8a9ad..a76624e4b 100644 --- a/Tests/Engine/ModuleDependencyHandler.tests.ps1 +++ b/Tests/Engine/ModuleDependencyHandler.tests.ps1 @@ -8,7 +8,7 @@ Describe "Resolve DSC Resource Dependency" { $skipTest = $true return } - $SavedPSModulePath = $env:PSModulePath + $savedPSModulePath = $env:PSModulePath $violationFileName = 'MissingDSCResource.ps1' $violationFilePath = Join-Path $directory $violationFileName $testRootDirectory = Split-Path -Parent $directory @@ -27,7 +27,7 @@ Describe "Resolve DSC Resource Dependency" { } AfterAll { if ( $skipTest ) { return } - $env:PSModulePath = $SavedPSModulePath + $env:PSModulePath = $savedPSModulePath } Context "Module handler class" { @@ -35,7 +35,11 @@ Describe "Resolve DSC Resource Dependency" { if ( $skipTest ) { return } $moduleHandlerType = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.ModuleDependencyHandler] $oldEnvVars = Get-Item Env:\* | Sort-Object -Property Key - $oldPSModulePath = $env:PSModulePath + $savedPSModulePath = $env:PSModulePath + } + AfterAll { + if ( $skipTest ) { return } + $env:PSModulePath = $savedPSModulePath } It "Sets defaults correctly" -skip:$skipTest { $rsp = [runspacefactory]::CreateRunspace() @@ -54,7 +58,7 @@ Describe "Resolve DSC Resource Dependency" { $expectedPssaAppDataPath = Join-Path $depHandler.LocalAppDataPath "PSScriptAnalyzer" $depHandler.PSSAAppDataPath | Should -Be $expectedPssaAppDataPath - $expectedPSModulePath = $oldPSModulePath + [System.IO.Path]::PathSeparator + $depHandler.TempModulePath + $expectedPSModulePath = $savedPSModulePath + [System.IO.Path]::PathSeparator + $depHandler.TempModulePath $env:PSModulePath | Should -Be $expectedPSModulePath $depHandler.Dispose() @@ -174,7 +178,7 @@ Describe "Resolve DSC Resource Dependency" { # Save the current environment variables $oldLocalAppDataPath = $env:LOCALAPPDATA $oldTempPath = $env:TEMP - $oldPSModulePath = $env:PSModulePath + $savedPSModulePath = $env:PSModulePath # set the environment variables $tempPath = Join-Path $oldTempPath ([guid]::NewGUID()).ToString() @@ -184,8 +188,8 @@ Describe "Resolve DSC Resource Dependency" { $env:TEMP = $newTempPath # create the temporary directories - New-Item -Type Directory -Path $newLocalAppDataPath - New-Item -Type Directory -Path $newTempPath + New-Item -Type Directory -Path $newLocalAppDataPath -force + New-Item -Type Directory -Path $newTempPath -force # create and dispose module dependency handler object # to setup the temporary module @@ -203,7 +207,8 @@ Describe "Resolve DSC Resource Dependency" { } AfterAll { - $env:PSModulePath = $oldPSModulePath + if ( $skipTest ) { return } + $env:PSModulePath = $savedPSModulePath } It "has a single parse error" -skip:$skipTest { @@ -217,7 +222,7 @@ Describe "Resolve DSC Resource Dependency" { It "Keeps PSModulePath unchanged before and after invocation" -skip:$skipTest { $dr = Invoke-ScriptAnalyzer -Path $violationFilePath -ErrorVariable parseErrors -ErrorAction SilentlyContinue - $env:PSModulePath | Should -Be $oldPSModulePath + $env:PSModulePath | Should -Be $savedPSModulePath } if (!$skipTest) diff --git a/Tests/Rules/DscExamplesPresent.tests.ps1 b/Tests/Rules/DscExamplesPresent.tests.ps1 index 3524a39e7..7436d2b77 100644 --- a/Tests/Rules/DscExamplesPresent.tests.ps1 +++ b/Tests/Rules/DscExamplesPresent.tests.ps1 @@ -23,8 +23,8 @@ if ($PSVersionTable.PSVersion -ge [Version]'5.0.0') { } Context "When examples present" { - New-Item -Path $examplesPath -ItemType Directory - New-Item -Path "$examplesPath\FileResource_Example.psm1" -ItemType File + New-Item -Path $examplesPath -ItemType Directory -force + New-Item -Path "$examplesPath\FileResource_Example.psm1" -ItemType File -force $noViolations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $classResourcePath | Where-Object {$_.RuleName -eq $ruleName} @@ -57,8 +57,8 @@ Describe "DscExamplesPresent rule in regular (non-class) based resource" { } Context "When examples present" { - New-Item -Path $examplesPath -ItemType Directory - New-Item -Path "$examplesPath\MSFT_WaitForAll_Example.psm1" -ItemType File + New-Item -Path $examplesPath -ItemType Directory -force + New-Item -Path "$examplesPath\MSFT_WaitForAll_Example.psm1" -ItemType File -force $noViolations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $resourcePath | Where-Object {$_.RuleName -eq $ruleName} diff --git a/Tests/Rules/DscTestsPresent.tests.ps1 b/Tests/Rules/DscTestsPresent.tests.ps1 index f4faedf5e..d90a70f3f 100644 --- a/Tests/Rules/DscTestsPresent.tests.ps1 +++ b/Tests/Rules/DscTestsPresent.tests.ps1 @@ -23,8 +23,8 @@ if ($PSVersionTable.PSVersion -ge [Version]'5.0.0') { } Context "When tests present" { - New-Item -Path $testsPath -ItemType Directory - New-Item -Path "$testsPath\FileResource_Test.psm1" -ItemType File + New-Item -Path $testsPath -ItemType Directory -force + New-Item -Path "$testsPath\FileResource_Test.psm1" -ItemType File -force $noViolations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $classResourcePath | Where-Object {$_.RuleName -eq $ruleName} @@ -57,8 +57,8 @@ Describe "DscTestsPresent rule in regular (non-class) based resource" { } Context "When tests present" { - New-Item -Path $testsPath -ItemType Directory - New-Item -Path "$testsPath\MSFT_WaitForAll_Test.psm1" -ItemType File + New-Item -Path $testsPath -ItemType Directory -force + New-Item -Path "$testsPath\MSFT_WaitForAll_Test.psm1" -ItemType File -force $noViolations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $resourcePath | Where-Object {$_.RuleName -eq $ruleName} diff --git a/Tests/Rules/UseIdenticalMandatoryParametersForDSC.tests.ps1 b/Tests/Rules/UseIdenticalMandatoryParametersForDSC.tests.ps1 index f1a6d3fed..6d49eb4b3 100644 --- a/Tests/Rules/UseIdenticalMandatoryParametersForDSC.tests.ps1 +++ b/Tests/Rules/UseIdenticalMandatoryParametersForDSC.tests.ps1 @@ -63,7 +63,7 @@ Describe "UseIdenticalMandatoryParametersForDSC" { } "@ # and under it a directory called dscresources\something - New-Item -ItemType Directory -Path $noParentClassDir + New-Item -ItemType Directory -Path $noParentClassDir -force $noparentClassFilepath = Join-Path -Path $noParentClassDir -ChildPath 'ClassWithNoParent.psm1' $noparentClassMofFilepath = Join-Path -Path $noParentClassDir -ChildPath 'ClassWithNoParent.schema.mof' @@ -102,7 +102,7 @@ class ClassWithNoParent } "@ # and under it a directory called dscresources\something - New-Item -ItemType Directory -Path $noParentClassDir + New-Item -ItemType Directory -Path $noParentClassDir -force $noparentClassFilepath = Join-Path -Path $noParentClassDir -ChildPath 'MSFT_ClassWithNoParent.psm1' $noparentClassMofFilepath = Join-Path -Path $noParentClassDir -ChildPath 'MSFT_ClassWithNoParent.schema.mof' diff --git a/build.psm1 b/build.psm1 index 4749713ab..4a1290a8d 100644 --- a/build.psm1 +++ b/build.psm1 @@ -3,7 +3,18 @@ # Build module for PowerShell ScriptAnalyzer $projectRoot = $PSScriptRoot -$destinationDir = Join-Path -Path $projectRoot -ChildPath (Join-Path -Path "out" -ChildPath "PSScriptAnalyzer") +$analyzerName = "PSScriptAnalyzer" + +function Get-AnalyzerVersion +{ + $csprojPath = [io.path]::Combine($projectRoot,"Engine","Engine.csproj") + $xml = [xml](Get-Content "${csprojPath}") + $xml.SelectSingleNode(".//VersionPrefix")."#text" +} + +$analyzerVersion = Get-AnalyzerVersion +# location where analyzer goes +$script:destinationDir = [io.path]::Combine($projectRoot,"out","${analyzerName}", $analyzerVersion) function Publish-File { @@ -40,7 +51,7 @@ function Get-UserModulePath function Uninstall-ScriptAnalyzer { [CmdletBinding(SupportsShouldProcess)] - param ( $ModulePath = $(Join-Path -Path (Get-UserModulePath) -ChildPath PSScriptAnalyzer) ) + param ( $ModulePath = $(Join-Path -Path (Get-UserModulePath) -ChildPath ${analyzerName}) ) END { if ( $PSCmdlet.ShouldProcess("$modulePath") ) { Remove-Item -Recurse -Path "$ModulePath" -Force @@ -52,10 +63,10 @@ function Uninstall-ScriptAnalyzer function Install-ScriptAnalyzer { [CmdletBinding(SupportsShouldProcess)] - param ( $ModulePath = $(Join-Path -Path (Get-UserModulePath) -ChildPath PSScriptAnalyzer) ) + param ( $ModulePath = $(Join-Path -Path (Get-UserModulePath) -ChildPath ${analyzerName}) ) END { if ( $PSCmdlet.ShouldProcess("$modulePath") ) { - Copy-Item -Recurse -Path "$destinationDir" -Destination "$ModulePath\." -Force + Copy-Item -Recurse -Path "$script:destinationDir" -Destination "$ModulePath\." -Force } } } @@ -64,7 +75,7 @@ function Install-ScriptAnalyzer function Uninstall-ScriptAnalyzer { [CmdletBinding(SupportsShouldProcess)] - param ( $ModulePath = $(Join-Path -Path (Get-UserModulePath) -ChildPath PSScriptAnalyzer) ) + param ( $ModulePath = $(Join-Path -Path (Get-UserModulePath) -ChildPath ${analyzerName}) ) END { if ((Test-Path $ModulePath) -and (Get-Item $ModulePath).PSIsContainer ) { @@ -79,9 +90,9 @@ function Remove-Build [CmdletBinding(SupportsShouldProcess=$true)] param () END { - if ( $PSCmdlet.ShouldProcess("${destinationDir}")) { - if ( Test-Path ${destinationDir} ) { - Remove-Item -Force -Recurse ${destinationDir} + if ( $PSCmdlet.ShouldProcess("${script:destinationDir}")) { + if ( Test-Path ${script:destinationDir} ) { + Remove-Item -Force -Recurse ${script:destinationDir} } } } @@ -92,7 +103,7 @@ function Start-DocumentationBuild { $docsPath = Join-Path $projectRoot docs $markdownDocsPath = Join-Path $docsPath markdown - $outputDocsPath = Join-Path $destinationDir en-US + $outputDocsPath = Join-Path $script:destinationDir en-US $platyPS = Get-Module -ListAvailable platyPS if ($null -eq $platyPS -or ($platyPS | Sort-Object Version -Descending | Select-Object -First 1).Version -lt [version]0.12) { @@ -118,12 +129,12 @@ function Copy-CompatibilityProfiles } $profileDir = [System.IO.Path]::Combine($PSScriptRoot, 'PSCompatibilityCollector', 'profiles') - $destinationDir = [System.IO.Path]::Combine($PSScriptRoot, 'out', 'PSScriptAnalyzer', "compatibility_profiles") - if ( -not (Test-Path $destinationDir) ) { - $null = New-Item -Type Directory $destinationDir + $targetProfileDir = [io.path]::Combine($script:destinationDir,"compatibility_profiles") + if ( -not (Test-Path $targetProfileDir) ) { + $null = New-Item -Type Directory $targetProfileDir } - Copy-Item -Force $profileDir/* $destinationDir + Copy-Item -Force $profileDir/* $targetProfileDir } # build script analyzer (and optionally build everything with -All) @@ -162,9 +173,6 @@ function Start-ScriptAnalyzerBuild Start-DocumentationBuild } - # Destination for the composed module when built - $destinationDir = "$projectRoot\out\PSScriptAnalyzer" - if ( $All ) { # Build all the versions of the analyzer @@ -205,24 +213,23 @@ function Start-ScriptAnalyzerBuild "$projectRoot\Engine\ScriptAnalyzer.format.ps1xml", "$projectRoot\Engine\ScriptAnalyzer.types.ps1xml" ) - $destinationDir = "$projectRoot\out\PSScriptAnalyzer" switch ($PSVersion) { 3 { - $destinationDirBinaries = "$destinationDir\PSv3" + $destinationDirBinaries = "$script:destinationDir\PSv3" } 4 { - $destinationDirBinaries = "$destinationDir\PSv4" + $destinationDirBinaries = "$script:destinationDir\PSv4" } 5 { - $destinationDirBinaries = "$destinationDir" + $destinationDirBinaries = "$script:destinationDir" } 6 { - $destinationDirBinaries = "$destinationDir\coreclr" + $destinationDirBinaries = "$script:destinationDir\coreclr" } default { @@ -252,7 +259,7 @@ function Start-ScriptAnalyzerBuild Pop-Location } - Publish-File $itemsToCopyCommon $destinationDir + Publish-File $itemsToCopyCommon $script:destinationDir $itemsToCopyBinaries = @( "$projectRoot\Engine\bin\${config}\${Framework}\Microsoft.Windows.PowerShell.ScriptAnalyzer.dll", @@ -262,7 +269,7 @@ function Start-ScriptAnalyzerBuild Publish-File $itemsToCopyBinaries $destinationDirBinaries $settingsFiles = Get-Childitem "$projectRoot\Engine\Settings" | ForEach-Object -MemberName FullName - Publish-File $settingsFiles (Join-Path -Path $destinationDir -ChildPath Settings) + Publish-File $settingsFiles (Join-Path -Path $script:destinationDir -ChildPath Settings) if ($framework -eq 'net452') { Copy-Item -path "$projectRoot\Rules\bin\${config}\${framework}\Newtonsoft.Json.dll" -Destination $destinationDirBinaries @@ -280,10 +287,38 @@ function Test-ScriptAnalyzer param ( [Parameter()][switch]$InProcess, [switch]$ShowAll ) END { - $testModulePath = Join-Path "${projectRoot}" -ChildPath out + # versions 3 and 4 don't understand versioned module paths, so we need to rename the directory of the version to + # the module name, and then set the ModulePath to that + # + # the layout of the build location is + # .../out + # /PSScriptAnalyzer + # /1.18.0 + # / + # and ".../out" is added to env:PSModulePath + # on v3 and v4, it will be + # .../out + # /PSScriptAnalyzer + # /PSScriptAnalyzer + # / + # and ".../out/PSScriptAnalyzer" is added to env:PSModulePath + # + # + $major = $PSVersionTable.PSVersion.Major + if ( $major -lt 5 ) { + # get the directory name of the destination, we need to change it + $versionDirectoryRoot = Split-Path $script:destinationDir + $testModulePath = Join-Path $versionDirectoryRoot $analyzerName + } + else { + $testModulePath = Join-Path "${projectRoot}" -ChildPath out + } $testResultsFile = "'$(Join-Path ${projectRoot} -childPath TestResults.xml)'" $testScripts = "'${projectRoot}\Tests\Engine','${projectRoot}\Tests\Rules','${projectRoot}\Tests\Documentation'" try { + if ( $major -lt 5 ) { + Rename-Item $script:destinationDir ${testModulePath} + } $savedModulePath = $env:PSModulePath $env:PSModulePath = "${testModulePath}{0}${env:PSModulePath}" -f [System.IO.Path]::PathSeparator if ($ShowAll) @@ -304,6 +339,9 @@ function Test-ScriptAnalyzer } finally { $env:PSModulePath = $savedModulePath + if ( $major -lt 5 ) { + Rename-Item ${testModulePath} ${script:destinationDir} + } } } } diff --git a/tools/appveyor.psm1 b/tools/appveyor.psm1 index 2acbec9bc..65dd71304 100644 --- a/tools/appveyor.psm1 +++ b/tools/appveyor.psm1 @@ -51,10 +51,23 @@ function Invoke-AppveyorTest { Write-Verbose -Verbose ("Running tests on PowerShell version " + $PSVersionTable.PSVersion) Write-Verbose -Verbose "Language set to '${env:LANG}'" - # Copy the generated modules into the out directory - $modulePath = $env:PSModulePath.Split([System.IO.Path]::PathSeparator) | Where-Object { Test-Path $_} | Select-Object -First 1 - Copy-Item "${CheckoutPath}\out\PSScriptAnalyzer" "$modulePath\" -Recurse -Force - Copy-Item "${CheckoutPath}\PSCompatibilityCollector\out\PSCompatibilityCollector" "$modulePath\" -Recurse -Force + # set up env:PSModulePath to the build location, don't copy it to the "normal place" + $analyzerVersion = ([xml](Get-Content "${CheckoutPath}\Engine\Engine.csproj")).SelectSingleNode(".//VersionPrefix")."#text".Trim() + $majorVersion = ([System.Version]$analyzerVersion).Major + $psMajorVersion = $PSVersionTable.PSVersion.Major + + if ( $psMajorVersion -lt 5 ) { + $versionModuleDir = "${CheckoutPath}\out\PSScriptAnalyzer\${analyzerVersion}" + $renameTarget = "${CheckoutPath}\out\PSScriptAnalyzer\PSScriptAnalyzer" + Rename-Item "${versionModuleDir}" "${renameTarget}" + $moduleDir = "${CheckoutPath}\out\PSScriptAnalyzer" + } + else{ + $moduleDir = "${CheckoutPath}\out" + } + + $env:PSModulePath = "${moduleDir}","${env:PSModulePath}" -join [System.IO.Path]::PathSeparator + Write-Verbose -Verbose "module path: ${env:PSModulePath}" # Set up testing assets $testResultsPath = Join-Path ${CheckoutPath} TestResults.xml