diff --git a/.vsts-ci/linux.yml b/.vsts-ci/linux.yml index 3e6288406a..874b7ae0b6 100644 --- a/.vsts-ci/linux.yml +++ b/.vsts-ci/linux.yml @@ -81,7 +81,7 @@ jobs: - powershell: | Import-Module ./tools/ci.psm1 - Invoke-Bootstrap-Stage + Invoke-BootstrapStage displayName: Bootstrap condition: succeeded() diff --git a/.vsts-ci/mac.yml b/.vsts-ci/mac.yml index ec73ac4ba3..084900cec0 100644 --- a/.vsts-ci/mac.yml +++ b/.vsts-ci/mac.yml @@ -35,43 +35,64 @@ resources: - repo: self clean: true jobs: -- job: macOS_CI +- template: templates/ci-build.yml + parameters: + pool: Hosted macOS + jobName: mac_build + displayName: macOS Build +- template: templates/nix-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: CI + parentJobs: + - mac_build + +- template: templates/nix-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: CI + parentJobs: + - mac_build + +- template: templates/nix-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: Others + parentJobs: + - mac_build + +- template: templates/nix-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: Others + parentJobs: + - mac_build + +- job: verify_xunit + displayName: Verify xUnit Results pool: - name: Hosted macOS - - timeoutInMinutes: 120 - + name: 'Hosted macOS' + dependsOn: + - mac_build steps: - - powershell: | - Get-ChildItem -Path env: - displayName: Capture environment - condition: succeededOrFailed() + - task: DownloadBuildArtifacts@0 + displayName: 'Download build artifacts' + inputs: + downloadType: specific + itemPattern: | + xunit/**/* + downloadPath: '$(System.ArtifactsDirectory)' - - powershell: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" - displayName: Set Build Name for Non-PR - condition: ne(variables['Build.Reason'], 'PullRequest') - - - template: ../tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml - - - powershell: | - Import-Module ./tools/ci.psm1 - Invoke-Bootstrap-Stage - displayName: Bootstrap - condition: succeeded() - - - powershell: | - Import-Module ./tools/ci.psm1 - $env:NugetKey = '$(NUGET_KEY)' - Invoke-LinuxTests - displayName: Build and Test - condition: succeeded() - - # Uploads any packages as an artifact - - powershell: | - Get-ChildItem -Path ${env:BUILD_ARTIFACTSTAGINGDIRECTORY} -Include *.nupkg, *.pkg, *.tar.gz, TestPackage.zip -Recurse | Select-Object -ExpandProperty FullName | ForEach-Object { - Write-Host "##vso[artifact.upload containerfolder=artifacts;artifactname=artifacts]$_" - } - displayName: Publish Artifacts - condition: succeededOrFailed() + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture artifacts directory' continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + $xUnitTestResultsFile = "$(System.ArtifactsDirectory)\xunit\xUnitTestResults.xml" + + Test-XUnitTestResults -TestResultsFile $xUnitTestResultsFile + displayName: Test + condition: succeeded() diff --git a/.vsts-ci/templates/windows-build.yml b/.vsts-ci/templates/ci-build.yml similarity index 93% rename from .vsts-ci/templates/windows-build.yml rename to .vsts-ci/templates/ci-build.yml index 0cb07dd5d6..8eb6fc9fb2 100644 --- a/.vsts-ci/templates/windows-build.yml +++ b/.vsts-ci/templates/ci-build.yml @@ -1,13 +1,14 @@ parameters: pool: 'Hosted VS2017' jobName: 'win_build' + displayName: Windows Build jobs: - job: ${{ parameters.jobName }} pool: name: ${{ parameters.pool }} - displayName: Windows Build + displayName: ${{ parameters.displayName }} steps: - powershell: | @@ -24,7 +25,7 @@ jobs: - powershell: | [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 Import-Module .\tools\ci.psm1 - Invoke-CIInstall + Invoke-CIInstall -SkipUser displayName: Bootstrap condition: succeededOrFailed() diff --git a/.vsts-ci/templates/nix-test.yml b/.vsts-ci/templates/nix-test.yml new file mode 100644 index 0000000000..b93e336a28 --- /dev/null +++ b/.vsts-ci/templates/nix-test.yml @@ -0,0 +1,72 @@ +parameters: + pool: 'Hosted macOS' + parentJobs: [] + purpose: '' + tagSet: 'CI' + name: 'mac' + +jobs: +- job: ${{ parameters.name }}_test_${{ parameters.purpose }}_${{ parameters.tagSet }} + dependsOn: + ${{ parameters.parentJobs }} + pool: + name: ${{ parameters.pool }} + + displayName: ${{ parameters.name }} Test - ${{ parameters.purpose }} - ${{ parameters.tagSet }} + + steps: + - pwsh: | + Get-ChildItem -Path env: + displayName: Capture environment + condition: succeededOrFailed() + + - task: DownloadBuildArtifacts@0 + displayName: 'Download build artifacts' + inputs: + downloadType: specific + itemPattern: | + build/**/* + downloadPath: '$(System.ArtifactsDirectory)' + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture artifacts directory' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + condition: succeededOrFailed() + + - task: ExtractFiles@1 + displayName: 'Extract build zip' + inputs: + archiveFilePatterns: '$(System.ArtifactsDirectory)/build/build.zip' + destinationFolder: '$(System.ArtifactsDirectory)/bins' + + - bash: | + find "$(System.ArtifactsDirectory)/bins" -type d -exec chmod +rwx {} \; + find "$(System.ArtifactsDirectory)/bins" -type f -exec chmod +rw {} \; + displayName: 'Fix permissions' + continueOnError: true + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\bins\*" -Recurse -ErrorAction SilentlyContinue + displayName: 'Capture extracted build zip' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + $options = (Get-PSOptions) + $rootPath = '$(System.ArtifactsDirectory)\bins' + $originalRootPath = Split-Path -path $options.Output + $path = Join-Path -path $rootPath -ChildPath (split-path -leaf -path $originalRootPath) + $pwshPath = Join-Path -path $path -ChildPath 'pwsh' + chmod a+x $pwshPath + $options.Output = $pwshPath + Set-PSOptions $options + Invoke-CITest -Purpose '${{ parameters.purpose }}' -TagSet '${{ parameters.tagSet }}' + displayName: Test + condition: succeeded() diff --git a/.vsts-ci/templates/windows-test.yml b/.vsts-ci/templates/windows-test.yml index 3844418e40..fefa39b98d 100644 --- a/.vsts-ci/templates/windows-test.yml +++ b/.vsts-ci/templates/windows-test.yml @@ -14,7 +14,7 @@ jobs: displayName: Windows Test - ${{ parameters.purpose }} - ${{ parameters.tagSet }} steps: - - powershell: | + - pwsh: | Get-ChildItem -Path env: displayName: Capture environment condition: succeededOrFailed() @@ -27,19 +27,19 @@ jobs: build/**/* downloadPath: '$(System.ArtifactsDirectory)' - - powershell: | - dir "$(System.ArtifactsDirectory)\*" -Recurse + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse displayName: 'Capture artifacts directory' continueOnError: true + # must be run frow Windows PowerShell - powershell: | - [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 Import-Module .\tools\ci.psm1 Invoke-CIInstall displayName: Bootstrap condition: succeededOrFailed() - - powershell: | + - pwsh: | Import-Module .\tools\ci.psm1 Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' $options = (Get-PSOptions) diff --git a/.vsts-ci/windows.yml b/.vsts-ci/windows.yml index d2d94b9823..055138da1b 100644 --- a/.vsts-ci/windows.yml +++ b/.vsts-ci/windows.yml @@ -36,7 +36,7 @@ resources: clean: true jobs: -- template: templates/windows-build.yml +- template: templates/ci-build.yml - template: templates/windows-test.yml parameters: diff --git a/build.psm1 b/build.psm1 index 723c7fb42a..658252cf61 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1636,9 +1636,6 @@ function Start-PSBootstrap { # Install dependencies # ignore exitcode, because they may be already installed Start-NativeExecution { brew install $Deps } -IgnoreExitcode - - # Install patched version of curl - Start-NativeExecution { brew install curl --with-openssl --with-gssapi } -IgnoreExitcode } elseif ($Environment.IsAlpine) { $Deps += 'libunwind', 'libcurl', 'bash', 'cmake', 'clang', 'build-base', 'git', 'curl' diff --git a/tools/WindowsCI.psm1 b/tools/WindowsCI.psm1 new file mode 100644 index 0000000000..7c0c02265e --- /dev/null +++ b/tools/WindowsCI.psm1 @@ -0,0 +1,77 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +if($PSVersionTable.PSEdition -ne 'Desktop') +{ + throw 'Must be run from Windows PowerShell' +} + +function New-LocalUser +{ + <# + .SYNOPSIS + Creates a local user with the specified username and password + .DESCRIPTION + .EXAMPLE + .PARAMETER + username Username of the user which will be created + .PARAMETER + password Password of the user which will be created + .OUTPUTS + .NOTES + #> + param( + [Parameter(Mandatory=$true)] + [string] $username, + + [Parameter(Mandatory=$true)] + [string] $password + + ) + $LocalComputer = [ADSI] "WinNT://$env:computername"; + $user = $LocalComputer.Create('user', $username); + $user.SetPassword($password) | out-null; + $user.SetInfo() | out-null; +} + +<# + Converts SID to NT Account Name +#> +function ConvertTo-NtAccount +{ + param( + [Parameter(Mandatory=$true)] + [string] $sid + ) + (new-object System.Security.Principal.SecurityIdentifier($sid)).translate([System.Security.Principal.NTAccount]).Value +} + +<# + Add a user to a local security group + Requires Windows PowerShell +#> +function Add-UserToGroup +{ + param( + [Parameter(Mandatory=$true)] + [string] $username, + + [Parameter(Mandatory=$true, ParameterSetName = "SID")] + [string] $groupSid, + + [Parameter(Mandatory=$true, ParameterSetName = "Name")] + [string] $group + ) + + $userAD = [ADSI] "WinNT://$env:computername/${username},user" + + if($PsCmdlet.ParameterSetName -eq "SID") + { + $ntAccount=ConvertTo-NtAccount $groupSid + $group =$ntAccount.Split("\\")[1] + } + + $groupAD = [ADSI] "WinNT://$env:computername/${group},group" + + $groupAD.Add($userAD.AdsPath); +} diff --git a/tools/ci.psm1 b/tools/ci.psm1 index a74dbf1269..f235b670d7 100644 --- a/tools/ci.psm1 +++ b/tools/ci.psm1 @@ -15,75 +15,12 @@ if(Test-Path $dotNetPath) # import build into the global scope so it can be used by packaging Import-Module (Join-Path $repoRoot 'build.psm1') -Scope Global -Import-Module (Join-Path $repoRoot 'tools\packaging') +Import-Module (Join-Path $repoRoot 'tools\packaging') -scope Global -function New-LocalUser +# import the windows specific functcion only in Windows PowerShell or on Windows +if($PSVersionTable.PSEdition -eq 'Desktop' -or $isWindows) { - <# - .SYNOPSIS - Creates a local user with the specified username and password - .DESCRIPTION - .EXAMPLE - .PARAMETER - username Username of the user which will be created - .PARAMETER - password Password of the user which will be created - .OUTPUTS - .NOTES - #> - param( - [Parameter(Mandatory=$true)] - [string] $username, - - [Parameter(Mandatory=$true)] - [string] $password - - ) - $LocalComputer = [ADSI] "WinNT://$env:computername"; - $user = $LocalComputer.Create('user', $username); - $user.SetPassword($password) | out-null; - $user.SetInfo() | out-null; -} - -<# - Converts SID to NT Account Name -#> -function ConvertTo-NtAccount -{ - param( - [Parameter(Mandatory=$true)] - [string] $sid - ) - (new-object System.Security.Principal.SecurityIdentifier($sid)).translate([System.Security.Principal.NTAccount]).Value -} - -<# - Add a user to a local security group -#> -function Add-UserToGroup -{ - param( - [Parameter(Mandatory=$true)] - [string] $username, - - [Parameter(Mandatory=$true, ParameterSetName = "SID")] - [string] $groupSid, - - [Parameter(Mandatory=$true, ParameterSetName = "Name")] - [string] $group - ) - - $userAD = [ADSI] "WinNT://$env:computername/${username},user" - - if($PsCmdlet.ParameterSetName -eq "SID") - { - $ntAccount=ConvertTo-NtAccount $groupSid - $group =$ntAccount.Split("\\")[1] - } - - $groupAD = [ADSI] "WinNT://$env:computername/${group},group" - - $groupAD.Add($userAD.AdsPath); + Import-Module (Join-Path $PSScriptRoot 'WindowsCI.psm1') -scope Global } # tests if we should run a daily build @@ -209,6 +146,10 @@ function Invoke-CIBuild # Implements the CI 'install' step function Invoke-CIInstall { + param( + [switch] + $SkipUser + ) # Make sure we have all the tags Sync-PSTags -AddRemoteIfMissing $releaseTag = Get-ReleaseTag @@ -221,7 +162,7 @@ function Invoke-CIInstall } } - if ($env:TF_BUILD) + if ($env:TF_BUILD -and !$SkipUser.IsPresent) { # Generate new credential for CI (only) remoting tests. Write-Log -Message "Creating account for remoting tests in CI." @@ -269,12 +210,18 @@ function Invoke-CIxUnit $SkipFailing ) $env:CoreOutput = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions)) - if(!(Test-Path "$env:CoreOutput\pwsh.exe")) + $path = "$env:CoreOutput\pwsh.exe" + if($IsMacOS -or $IsLinux) + { + $path = "$env:CoreOutput\pwsh" + } + + if(!(Test-Path $path)) { throw "CoreCLR pwsh.exe was not built" } - $xUnitTestResultsFile = "$pwd\xUnitTestResults.xml" + $xUnitTestResultsFile = Join-Path -Path $pwd -childpath "xUnitTestResults.xml" Start-PSxUnit -xUnitTestResultsFile $xUnitTestResultsFile Push-Artifact -Path $xUnitTestResultsFile -name xunit @@ -296,16 +243,6 @@ function Invoke-CITest [ValidateSet('CI', 'Others')] [string] $TagSet ) - # CoreCLR - - $env:CoreOutput = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions)) - Write-Host -Foreground Green 'Run CoreCLR tests' - $testResultsNonAdminFile = "$pwd\TestsResultsNonAdmin-$TagSet.xml" - $testResultsAdminFile = "$pwd\TestsResultsAdmin-$TagSet.xml" - if(!(Test-Path "$env:CoreOutput\pwsh.exe")) - { - throw "CoreCLR pwsh.exe was not built" - } # Pester doesn't allow Invoke-Pester -TagAll@('CI', 'RequireAdminOnWindows') currently # https://github.com/pester/Pester/issues/608 @@ -324,6 +261,22 @@ function Invoke-CITest } } + if($IsLinux -or $IsMacOS) + { + return Invoke-LinuxTestsCore -Purpose $Purpose -ExcludeTag $ExcludeTag -TagSet $TagSet + } + + # CoreCLR + + $env:CoreOutput = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions)) + Write-Host -Foreground Green 'Run CoreCLR tests' + $testResultsNonAdminFile = "$pwd\TestsResultsNonAdmin-$TagSet.xml" + $testResultsAdminFile = "$pwd\TestsResultsAdmin-$TagSet.xml" + if(!(Test-Path "$env:CoreOutput\pwsh.exe")) + { + throw "CoreCLR pwsh.exe was not built" + } + # Get the experimental feature names and the tests associated with them $ExperimentalFeatureTests = Get-ExperimentalFeatureTests @@ -336,6 +289,7 @@ function Invoke-CITest Tag = @() ExcludeTag = $ExcludeTag + 'RequireAdminOnWindows' } + Start-PSPester @arguments -Title "Pester Unelevated - $TagSet" # Fail the build, if tests failed Test-PSPesterResults -TestResultsFile $testResultsNonAdminFile @@ -357,6 +311,7 @@ function Invoke-CITest # If a non-empty string or array is specified for the feature name, we only run those test files. $arguments['Path'] = $testFiles } + Start-PSPester @arguments -Title "Pester Experimental Unelevated - $featureName" # Fail the build, if tests failed @@ -372,6 +327,7 @@ function Invoke-CITest Tag = @('RequireAdminOnWindows') ExcludeTag = $ExcludeTag } + Start-PSPester @arguments -Title "Pester Elevated - $TagSet" # Fail the build, if tests failed @@ -499,6 +455,12 @@ function Invoke-CIFinish param( [string] $NuGetKey ) + + if($IsLinux -or $IsMacOS) + { + return New-LinuxPackage -NugetKey $NugetKey + } + try { $releaseTag = Get-ReleaseTag @@ -613,7 +575,7 @@ function Invoke-CIFinish } # Bootstrap script for Linux and macOS -function Invoke-Bootstrap-Stage +function Invoke-BootstrapStage { $createPackages = Test-DailyBuild Write-Log -Message "Executing ci.psm1 Bootstrap Stage" @@ -622,42 +584,33 @@ function Invoke-Bootstrap-Stage Start-PSBootstrap -Package:$createPackages } -# Build and test script for Linux and macOS: -function Invoke-LinuxTests +# Run pester tests for Linux and macOS +function Invoke-LinuxTestsCore { - $releaseTag = Get-ReleaseTag - Write-Log -Message "Executing ci.psm1 build and test on a Linux based operating system." - $originalProgressPreference = $ProgressPreference - $ProgressPreference = 'SilentlyContinue' - try { - # We use CrossGen build to run tests only if it's the daily build. - Start-PSBuild -CrossGen -PSModuleRestore -CI -ReleaseTag $releaseTag -Configuration 'Release' - } - finally - { - $ProgressPreference = $originalProgressPreference - } + [CmdletBinding()] + param( + [ValidateSet('UnelevatedPesterTests', 'ElevatedPesterTests', 'All')] + [string] $Purpose = 'All', + [string[]] $ExcludeTag = @('Slow', 'Feature', 'Scenario'), + [string] $TagSet = 'CI' + ) $output = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions)) $testResultsNoSudo = "$pwd/TestResultsNoSudo.xml" $testResultsSudo = "$pwd/TestResultsSudo.xml" - $excludeTag = @('RequireSudoOnUnix') + $testExcludeTag = $ExcludeTag + 'RequireSudoOnUnix' $noSudoPesterParam = @{ 'BinDir' = $output 'PassThru' = $true 'Terse' = $true 'Tag' = @() - 'ExcludeTag' = $excludeTag + 'ExcludeTag' = $testExcludeTag 'OutputFile' = $testResultsNoSudo } # create packages if it is a full build $isFullBuild = Test-DailyBuild - $createPackages = $isFullBuild - if ($isFullBuild) { - $noSudoPesterParam['Tag'] = @('CI','Feature','Scenario') - } else { - $noSudoPesterParam['Tag'] = @('CI') + if (!$isFullBuild) { $noSudoPesterParam['ThrowOnFailure'] = $true } if ($hasRunFailingTestTag) { @@ -668,60 +621,68 @@ function Invoke-LinuxTests $ExperimentalFeatureTests = Get-ExperimentalFeatureTests # Running tests which do not require sudo. - $pesterPassThruNoSudoObject = Start-PSPester @noSudoPesterParam -Title 'Pester No Sudo' + if($Purpose -eq 'UnelevatedPesterTests' -or $Purpose -eq 'All') + { + $pesterPassThruNoSudoObject = Start-PSPester @noSudoPesterParam -Title "Pester No Sudo - $TagSet" - # Running tests that do not require sudo, with specified experimental features enabled - $noSudoResultsWithExpFeatures = @() - foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) { - $featureName = $entry.Key - $testFiles = $entry.Value + # Running tests that do not require sudo, with specified experimental features enabled + $noSudoResultsWithExpFeatures = @() + foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) { + $featureName = $entry.Key + $testFiles = $entry.Value - $expFeatureTestResultFile = "$pwd\TestResultsNoSudo.$featureName.xml" - $noSudoPesterParam['OutputFile'] = $expFeatureTestResultFile - $noSudoPesterParam['ExperimentalFeatureName'] = $featureName - if ($testFiles.Count -eq 0) { - # If an empty array is specified for the feature name, we run all tests with the feature enabled. - # This allows us to prevent regressions to a critical engine experimental feature. - $noSudoPesterParam.Remove('Path') + $expFeatureTestResultFile = "$pwd\TestResultsNoSudo.$featureName.xml" + $noSudoPesterParam['OutputFile'] = $expFeatureTestResultFile + $noSudoPesterParam['ExperimentalFeatureName'] = $featureName + if ($testFiles.Count -eq 0) { + # If an empty array is specified for the feature name, we run all tests with the feature enabled. + # This allows us to prevent regressions to a critical engine experimental feature. + $noSudoPesterParam.Remove('Path') + } + else + { + # If a non-empty string or array is specified for the feature name, we only run those test files. + $noSudoPesterParam['Path'] = $testFiles + } + $passThruResult = Start-PSPester @noSudoPesterParam -Title "Pester Experimental No Sudo - $featureName - $TagSet" + $noSudoResultsWithExpFeatures += $passThruResult } - else - { - # If a non-empty string or array is specified for the feature name, we only run those test files. - $noSudoPesterParam['Path'] = $testFiles - } - $passThruResult = Start-PSPester @noSudoPesterParam -Title "Pester Experimental No Sudo - $featureName" - $noSudoResultsWithExpFeatures += $passThruResult } - # Running tests, which require sudo. - $sudoPesterParam = $noSudoPesterParam.Clone() - $sudoPesterParam.Remove('Path') - $sudoPesterParam['Tag'] = @('RequireSudoOnUnix') - $sudoPesterParam['ExcludeTag'] = @() - $sudoPesterParam['Sudo'] = $true - $sudoPesterParam['OutputFile'] = $testResultsSudo - $pesterPassThruSudoObject = Start-PSPester @sudoPesterParam -Title 'Pester Sudo' - # Running tests that require sudo, with specified experimental features enabled - $sudoResultsWithExpFeatures = @() - foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) { - $featureName = $entry.Key - $testFiles = $entry.Value - $expFeatureTestResultFile = "$pwd\TestResultsSudo.$featureName.xml" - $sudoPesterParam['OutputFile'] = $expFeatureTestResultFile - $sudoPesterParam['ExperimentalFeatureName'] = $featureName - if ($testFiles.Count -eq 0) - { - # If an empty array is specified for the feature name, we run all tests with the feature enabled. - # This allows us to prevent regressions to a critical engine experimental feature. - $sudoPesterParam.Remove('Path') + # Running tests, which require sudo. + if($Purpose -eq 'ElevatedPesterTests' -or $Purpose -eq 'All') + { + $sudoPesterParam = $noSudoPesterParam.Clone() + $sudoPesterParam.Remove('Path') + $sudoPesterParam['Tag'] = @('RequireSudoOnUnix') + $sudoPesterParam['ExcludeTag'] = $ExcludeTag + $sudoPesterParam['Sudo'] = $true + $sudoPesterParam['OutputFile'] = $testResultsSudo + $pesterPassThruSudoObject = Start-PSPester @sudoPesterParam -Title "Pester Sudo - $TagSet" + + # Running tests that require sudo, with specified experimental features enabled + $sudoResultsWithExpFeatures = @() + foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) { + $featureName = $entry.Key + $testFiles = $entry.Value + + $expFeatureTestResultFile = "$pwd\TestResultsSudo.$featureName.xml" + $sudoPesterParam['OutputFile'] = $expFeatureTestResultFile + $sudoPesterParam['ExperimentalFeatureName'] = $featureName + if ($testFiles.Count -eq 0) + { + # If an empty array is specified for the feature name, we run all tests with the feature enabled. + # This allows us to prevent regressions to a critical engine experimental feature. + $sudoPesterParam.Remove('Path') + } + else + { + # If a non-empty string or array is specified for the feature name, we only run those test files. + $sudoPesterParam['Path'] = $testFiles + } + $passThruResult = Start-PSPester @sudoPesterParam -Title "Pester Experimental Sudo - $featureName - $TagSet" + $sudoResultsWithExpFeatures += $passThruResult } - else - { - # If a non-empty string or array is specified for the feature name, we only run those test files. - $sudoPesterParam['Path'] = $testFiles - } - $passThruResult = Start-PSPester @sudoPesterParam -Title "Pester Experimental Sudo - $featureName" - $sudoResultsWithExpFeatures += $passThruResult } # Determine whether the build passed @@ -737,6 +698,33 @@ function Invoke-LinuxTests $resultError = $_ $result = "FAIL" } +} + +# Build and test script for Linux and macOS: +function Invoke-LinuxTests +{ + param( + [switch] + $SkipBuild + ) + + if(!$SkipBuild.IsPresent) + { + $releaseTag = Get-ReleaseTag + Write-Log -Message "Executing ci.psm1 build and test on a Linux based operating system." + $originalProgressPreference = $ProgressPreference + $ProgressPreference = 'SilentlyContinue' + try { + # We use CrossGen build to run tests only if it's the daily build. + Start-PSBuild -CrossGen -PSModuleRestore -CI -ReleaseTag $releaseTag -Configuration 'Release' + } + finally + { + $ProgressPreference = $originalProgressPreference + } + } + + Invoke-LinuxTestsCore try { $xUnitTestResultsFile = "$pwd/xUnitTestResults.xml" @@ -751,64 +739,11 @@ function Invoke-LinuxTests } } + $createPackages = $isFullBuild + if ($createPackages) { - $packageParams = @{} - $packageParams += @{ReleaseTag=$releaseTag} - - # Only build packages for PowerShell/PowerShell repository - # branches, not pull requests - $packages = @(Start-PSPackage @packageParams -SkipReleaseChecks) - foreach($package in $packages) - { - if (Test-Path $package) - { - Write-Log "Package found: $package" - } - else - { - Write-Error -Message "Package NOT found: $package" - } - - # Publish the packages to the nuget feed if: - # 1 - It's a Daily build (already checked, for not a PR) - # 2 - We have the info to publish (NUGET_KEY and NUGET_URL) - # 3 - it's a nupkg file - if($isFullBuild -and $NugetKey -and $env:NUGET_URL -and [system.io.path]::GetExtension($package) -ieq '.nupkg') - { - Write-Log "pushing $package to $env:NUGET_URL" - Start-NativeExecution -sb {dotnet nuget push $package --api-key $NugetKey --source "$env:NUGET_URL/api/v2/package"} -IgnoreExitcode - } - - if($isFullBuild) - { - if ($package -isnot [System.IO.FileInfo]) - { - $packageObj = Get-Item $package - Write-Error -Message "The PACKAGE is not a FileInfo object" - } - else - { - $packageObj = $package - } - - Write-Log -message "Artifacts directory: ${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" - Copy-Item $packageObj.FullName -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force - } - } - - if ($IsLinux) - { - # Create and package Raspbian .tgz - Start-PSBuild -PSModuleRestore -Clean -Runtime linux-arm -Configuration 'Release' - $armPackage = Start-PSPackage @packageParams -Type tar-arm -SkipReleaseChecks - Copy-Item $armPackage -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force - } - - if ($isFullBuild) - { - New-TestPackage -Destination "${env:SYSTEM_ARTIFACTSDIRECTORY}" - } + New-LinuxPackage -NugetKey $env:NugetKey } # If the tests did not pass, throw the reason why @@ -822,3 +757,70 @@ function Invoke-LinuxTests Write-Verbose "Tests did not fail! Nice job!" } } + +function New-LinuxPackage +{ + param( + [string] + $NugetKey + ) + + $isFullBuild = Test-DailyBuild + $releaseTag = Get-ReleaseTag + $packageParams = @{} + $packageParams += @{ReleaseTag=$releaseTag} + + # Only build packages for PowerShell/PowerShell repository + # branches, not pull requests + $packages = @(Start-PSPackage @packageParams -SkipReleaseChecks) + foreach($package in $packages) + { + if (Test-Path $package) + { + Write-Log "Package found: $package" + } + else + { + Write-Error -Message "Package NOT found: $package" + } + + # Publish the packages to the nuget feed if: + # 1 - It's a Daily build (already checked, for not a PR) + # 2 - We have the info to publish (NUGET_KEY and NUGET_URL) + # 3 - it's a nupkg file + if($isFullBuild -and $NugetKey -and $env:NUGET_URL -and [system.io.path]::GetExtension($package) -ieq '.nupkg') + { + Write-Log "pushing $package to $env:NUGET_URL" + Start-NativeExecution -sb {dotnet nuget push $package --api-key $NugetKey --source "$env:NUGET_URL/api/v2/package"} -IgnoreExitcode + } + + if($isFullBuild) + { + if ($package -isnot [System.IO.FileInfo]) + { + $packageObj = Get-Item $package + Write-Error -Message "The PACKAGE is not a FileInfo object" + } + else + { + $packageObj = $package + } + + Write-Log -message "Artifacts directory: ${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" + Copy-Item $packageObj.FullName -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force + } + } + + if ($IsLinux) + { + # Create and package Raspbian .tgz + Start-PSBuild -PSModuleRestore -Clean -Runtime linux-arm -Configuration 'Release' + $armPackage = Start-PSPackage @packageParams -Type tar-arm -SkipReleaseChecks + Copy-Item $armPackage -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force + } + + if ($isFullBuild) + { + New-TestPackage -Destination "${env:SYSTEM_ARTIFACTSDIRECTORY}" + } +}