diff --git a/.vsts-ci/linux.yml b/.vsts-ci/linux.yml index 66e2e2c7c57..3e6288406a2 100644 --- a/.vsts-ci/linux.yml +++ b/.vsts-ci/linux.yml @@ -80,24 +80,16 @@ jobs: - template: ../tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml - powershell: | - tools/travis.ps1 -Stage Bootstrap + Import-Module ./tools/ci.psm1 + Invoke-Bootstrap-Stage displayName: Bootstrap condition: succeeded() - powershell: | - $ErrorActionPreference = 'continue' - tools/travis.ps1 -NugetKey $(NUGET_KEY) - displayName: Build and test - condition: succeeded() - - - powershell: | - tools/travis.ps1 -Stage Failure - displayName: After Failure - condition: failed() - - - powershell: | - tools/travis.ps1 -Stage Success - displayName: After Success + Import-Module ./tools/ci.psm1 + $env:NugetKey = '$(NUGET_KEY)' + Invoke-LinuxTests + displayName: Build and Test condition: succeeded() # Uploads any packages as an artifact diff --git a/.vsts-ci/mac.yml b/.vsts-ci/mac.yml index 842464a98ff..ec73ac4ba3e 100644 --- a/.vsts-ci/mac.yml +++ b/.vsts-ci/mac.yml @@ -55,24 +55,16 @@ jobs: - template: ../tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml - powershell: | - tools/travis.ps1 -Stage Bootstrap + Import-Module ./tools/ci.psm1 + Invoke-Bootstrap-Stage displayName: Bootstrap condition: succeeded() - powershell: | - $ErrorActionPreference = 'continue' - tools/travis.ps1 -NugetKey $(NUGET_KEY) - displayName: Build and test - condition: succeeded() - - - powershell: | - tools/travis.ps1 -Stage Failure - displayName: After Failure - condition: failed() - - - powershell: | - tools/travis.ps1 -Stage Success - displayName: After Success + Import-Module ./tools/ci.psm1 + $env:NugetKey = '$(NUGET_KEY)' + Invoke-LinuxTests + displayName: Build and Test condition: succeeded() # Uploads any packages as an artifact diff --git a/tools/ci.psm1 b/tools/ci.psm1 index f268663f25a..12f070cb954 100644 --- a/tools/ci.psm1 +++ b/tools/ci.psm1 @@ -1,17 +1,19 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -$ErrorActionPreference = 'Stop' + +$ErrorActionPreference = 'continue' $repoRoot = Join-Path $PSScriptRoot '..' $script:administratorsGroupSID = "S-1-5-32-544" $script:usersGroupSID = "S-1-5-32-545" +# set .NET path $dotNetPath = "$env:USERPROFILE\Appdata\Local\Microsoft\dotnet" if(Test-Path $dotNetPath) { $env:PATH = $dotNetPath + ';' + $env:PATH } -#import build into the global scope so it can be used by packaging +# 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') @@ -37,7 +39,6 @@ function New-LocalUser [string] $password ) - $LocalComputer = [ADSI] "WinNT://$env:computername"; $user = $LocalComputer.Create('user', $username); $user.SetPassword($password) | out-null; @@ -91,27 +92,25 @@ function Add-UserToGroup Function Test-DailyBuild { $trueString = 'True' - # PS_DAILY_BUILD says that we have previously determined that this is a daily build - # APPVEYOR_SCHEDULED_BUILD is True means that we are in an AppVeyor Scheduled build - # APPVEYOR_REPO_TAG_NAME means we are building a tag in AppVeyor - # BUILD_REASON is Schedule means we are in a VSTS Scheduled build if(($env:PS_DAILY_BUILD -eq $trueString) -or $env:BUILD_REASON -eq 'Schedule') { return $true } - # if [Feature] is in the commit message, + # if [feature] is in the commit message, # Run Daily tests $commitMessage = Get-CommitMessage - Write-Verbose "commitMessage: $commitMessage" -verbose + Write-log -message "commitMessage: $commitMessage" if($commitMessage -match '\[feature\]' -or $env:FORCE_FEATURE -eq 'True') { Set-BuildVariable -Name PS_DAILY_BUILD -Value $trueString return $true } - - return $false + else + { + return $false + } } # Returns the commit message for the current build @@ -144,10 +143,10 @@ Function Set-BuildVariable if($env:TF_BUILD) { - #In VSTS + # In VSTS Write-Host "##vso[task.setvariable variable=$Name;]$Value" # The variable will not show up until the next task. - # Setting in the current session for the same behavior as AppVeyor + # Setting in the current session for the same behavior as the CI Set-Item env:/$name -Value $Value } else @@ -156,7 +155,7 @@ Function Set-BuildVariable } } -# Emulates running all of AppVeyor but locally +# Emulates running all of CI but locally function Invoke-AppVeyorFull { param( @@ -174,13 +173,14 @@ function Invoke-AppVeyorFull Invoke-AppveyorFinish } -# Implements the AppVeyor 'build_script' step +# Implements the CI 'build_script' step function Invoke-AppVeyorBuild { $releaseTag = Get-ReleaseTag # check to be sure our test tags are correct $result = Get-PesterTag - if ( $result.Result -ne "Pass" ) { + if ( $result.Result -ne "Pass" ) + { $result.Warnings throw "Tags must be CI, Feature, Scenario, or Slow" } @@ -193,25 +193,25 @@ function Invoke-AppVeyorBuild Start-PSBuild -CrossGen -PSModuleRestore -Configuration 'Release' -CI -ReleaseTag $releaseTag } -# Implements the AppVeyor 'install' step +# Implements the CI 'install' step function Invoke-AppVeyorInstall { # Make sure we have all the tags Sync-PSTags -AddRemoteIfMissing $releaseTag = Get-ReleaseTag - if(Test-DailyBuild){ - if ($env:BUILD_REASON -eq 'Schedule') { + if(Test-DailyBuild) + { + if ($env:BUILD_REASON -eq 'Schedule') + { Write-Host "##vso[build.updatebuildnumber]Daily-$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhss"))" } } if ($env:TF_BUILD) { - # - # Generate new credential for appveyor (only) remoting tests. - # - Write-Verbose "Creating account for remoting tests in AppVeyor." + # Generate new credential for CI (only) remoting tests. + Write-Log -Message "Creating account for remoting tests in CI." # Password $randomObj = [System.Random]::new() @@ -225,18 +225,19 @@ function Invoke-AppVeyorInstall # Provide credentials globally for remote tests. $ss = ConvertTo-SecureString -String $password -AsPlainText -Force - $appveyorRemoteCredential = [PSCredential]::new("$env:COMPUTERNAME\$userName", $ss) - $appveyorRemoteCredential | Export-Clixml -Path "$env:TEMP\AppVeyorRemoteCred.xml" -Force + $ciRemoteCredential = [PSCredential]::new("$env:COMPUTERNAME\$userName", $ss) + $ciRemoteCredential | Export-Clixml -Path "$env:TEMP\AppVeyorRemoteCred.xml" -Force # Check that LocalAccountTokenFilterPolicy policy is set, since it is needed for remoting # using above local admin account. - Write-Verbose "Checking for LocalAccountTokenFilterPolicy in AppVeyor." + Write-Log -Message "Checking for LocalAccountTokenFilterPolicy in the CI." $haveLocalAccountTokenFilterPolicy = $false try { $haveLocalAccountTokenFilterPolicy = ((Get-ItemPropertyValue -Path HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Name LocalAccountTokenFilterPolicy) -eq 1) } - catch { } + # ignore if anything is caught: + catch {} if (!$haveLocalAccountTokenFilterPolicy) { Write-Verbose "Setting the LocalAccountTokenFilterPolicy for remoting tests" @@ -251,7 +252,7 @@ function Invoke-AppVeyorInstall # A wrapper to ensure that we upload test results # and that if we are not able to that it does not fail # the CI build -function Update-AppVeyorTestResults +function Update-TestResults { param( [string] $resultsFile @@ -262,7 +263,7 @@ function Update-AppVeyorTestResults } } -# Implement AppVeyor 'Test_script' +# Implement CI 'Test_script' function Invoke-AppVeyorTest { [CmdletBinding()] @@ -270,7 +271,6 @@ function Invoke-AppVeyorTest [ValidateSet('UnelevatedPesterTests', 'ElevatedPesterTests_xUnit_Packaging')] [string] $Purpose ) - # # CoreCLR $env:CoreOutput = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions)) @@ -286,11 +286,13 @@ function Invoke-AppVeyorTest # Pester doesn't allow Invoke-Pester -TagAll@('CI', 'RequireAdminOnWindows') currently # https://github.com/pester/Pester/issues/608 # To work-around it, we exlude all categories, but 'CI' from the list - if (Test-DailyBuild) { + if (Test-DailyBuild) + { $ExcludeTag = @() Write-Host -Foreground Green 'Running all CoreCLR tests..' } - else { + else + { $ExcludeTag = @('Slow', 'Feature', 'Scenario') Write-Host -Foreground Green 'Running "CI" CoreCLR tests..' } @@ -309,12 +311,13 @@ function Invoke-AppVeyorTest } Start-PSPester @arguments -Title 'Pester Unelevated' Write-Host -Foreground Green 'Upload CoreCLR Non-Admin test results' - Update-AppVeyorTestResults -resultsFile $testResultsNonAdminFile + Update-TestResults -resultsFile $testResultsNonAdminFile # Fail the build, if tests failed Test-PSPesterResults -TestResultsFile $testResultsNonAdminFile # Run tests with specified experimental features enabled - foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) { + foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) + { $featureName = $entry.Key $testFiles = $entry.Value @@ -332,7 +335,7 @@ function Invoke-AppVeyorTest Start-PSPester @arguments -Title "Pester Experimental Unelevated - $featureName" Write-Host -ForegroundColor Green "Upload CoreCLR Non-Admin test results for experimental feature '$featureName'" - Update-AppVeyorTestResults -resultsFile $expFeatureTestResultFile + Update-TestResults -resultsFile $expFeatureTestResultFile # Fail the build, if tests failed Test-PSPesterResults -TestResultsFile $expFeatureTestResultFile } @@ -348,36 +351,40 @@ function Invoke-AppVeyorTest } Start-PSPester @arguments -Title 'Pester Elevated' Write-Host -Foreground Green 'Upload CoreCLR Admin test results' - Update-AppVeyorTestResults -resultsFile $testResultsAdminFile + Update-TestResults -resultsFile $testResultsAdminFile Start-PSxUnit -ParallelTestResultsFile $ParallelXUnitTestResultsFile Write-Host -ForegroundColor Green 'Uploading PSxUnit test results' - Update-AppVeyorTestResults -resultsFile $ParallelXUnitTestResultsFile + Update-TestResults -resultsFile $ParallelXUnitTestResultsFile # Fail the build, if tests failed Test-PSPesterResults -TestResultsFile $testResultsAdminFile Test-XUnitTestResults -TestResultsFile $ParallelXUnitTestResultsFile # Run tests with specified experimental features enabled - foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) { + foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) + { $featureName = $entry.Key $testFiles = $entry.Value $expFeatureTestResultFile = "$pwd\TestsResultsAdmin.$featureName.xml" $arguments['OutputFile'] = $expFeatureTestResultFile $arguments['ExperimentalFeatureName'] = $featureName - if ($testFiles.Count -eq 0) { + 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. $arguments.Remove('Path') - } else { + } + else + { # 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 Elevated - $featureName" Write-Host -ForegroundColor Green "Upload CoreCLR Admin test results for experimental feature '$featureName'" - Update-AppVeyorTestResults -resultsFile $expFeatureTestResultFile + Update-TestResults -resultsFile $expFeatureTestResultFile # Fail the build, if tests failed Test-PSPesterResults -TestResultsFile $expFeatureTestResultFile } @@ -386,7 +393,7 @@ function Invoke-AppVeyorTest Set-BuildVariable -Name TestPassed -Value True } -#Implement AppVeyor 'after_test' phase +# Implement CI 'after_test' phase function Invoke-AppVeyorAfterTest { [CmdletBinding()] @@ -395,7 +402,8 @@ function Invoke-AppVeyorAfterTest if (Test-DailyBuild) { ## Publish code coverage build, tests and OpenCover module to artifacts, so webhook has the information. - ## Build webhook is called after 'after_test' phase, hence we need to do this here and not in AppveyorFinish. + Push-Artifact -Path $_ -Name 'CodeCoverage' + Push-Artifact $testPackageFullName -Name 'artifacts' $codeCoverageOutput = Split-Path -Parent (Get-PSOutput -Options (New-PSOptions -Configuration CodeCoverage)) $codeCoverageArtifacts = Compress-CoverageArtifacts -CodeCoverageOutput $codeCoverageOutput @@ -433,7 +441,7 @@ function Push-Artifact } if ($env:TF_BUILD) { - # In VSTS + # In Azure DevOps Write-Host "##vso[artifact.upload containerfolder=$artifactName;artifactname=$artifactName;]$Path" } } @@ -444,7 +452,6 @@ function Compress-CoverageArtifacts # Create archive for test content, OpenCover module and CodeCoverage build $artifacts = New-Object System.Collections.ArrayList - $zipTestContentPath = Join-Path $pwd 'tests.zip' Compress-TestContent -Destination $zipTestContentPath $null = $artifacts.Add($zipTestContentPath) @@ -467,30 +474,21 @@ function Get-ReleaseTag { $metaDataPath = Join-Path -Path $PSScriptRoot -ChildPath 'metadata.json' $metaData = Get-Content $metaDataPath | ConvertFrom-Json - $releaseTag = $metadata.PreviewReleaseTag - if($env:APPVEYOR_BUILD_NUMBER) + if($env:BUILD_BUILID) { - $releaseTag = $releaseTag.split('.')[0..2] -join '.' - $releaseTag = $releaseTag + '.' + $env:APPVEYOR_BUILD_NUMBER - } - elseif($env:BUILD_BUILID) - { - #In VSTS $releaseTag = $releaseTag.split('.')[0..2] -join '.' $releaseTag = $releaseTag + '.' + $env:BUILD_BUILID } - return $releaseTag } -# Implements AppVeyor 'on_finish' step +# Implements CI 'on_finish' step function Invoke-AppveyorFinish { param( [string] $NuGetKey ) - try { $releaseTag = Get-ReleaseTag @@ -513,6 +511,15 @@ function Invoke-AppveyorFinish $artifacts = New-Object System.Collections.ArrayList foreach ($package in $packages) { + if (Test-Path $package) + { + Write-Log "Package found: $package" + } + else + { + Write-Warning -Message "Package NOT found: $package" + } + if($package -is [string]) { $null = $artifacts.Add($package) @@ -565,7 +572,7 @@ function Invoke-AppveyorFinish $pushedAllArtifacts = $true $artifacts | ForEach-Object { - Write-Host "Pushing $_ as Appveyor artifact" + Write-Log -Message "Pushing $_ as CI artifact" if(Test-Path $_) { Push-Artifact -Path $_ -Name 'artifacts' @@ -587,9 +594,221 @@ function Invoke-AppveyorFinish throw "Some artifacts did not exist!" } } - catch { + catch + { Write-Host -Foreground Red $_ Write-Host -Foreground Red $_.ScriptStackTrace throw $_ } } + +# Bootstrap script for Linux and macOS +function Invoke-Bootstrap-Stage +{ + $createPackages = Test-DailyBuild + Write-Log -Message "Executing ci.psm1 Bootstrap Stage" + # Make sure we have all the tags + Sync-PSTags -AddRemoteIfMissing + Start-PSBootstrap -Package:$createPackages +} + +# Build and test script for Linux and macOS: +function Invoke-LinuxTests +{ + $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 + } + + $output = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions)) + $testResultsNoSudo = "$pwd/TestResultsNoSudo.xml" + $testResultsSudo = "$pwd/TestResultsSudo.xml" + $excludeTag = @('RequireSudoOnUnix') + + $noSudoPesterParam = @{ + 'BinDir' = $output + 'PassThru' = $true + 'Terse' = $true + 'Tag' = @() + 'ExcludeTag' = $excludeTag + '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') + $noSudoPesterParam['ThrowOnFailure'] = $true + } + if ($hasRunFailingTestTag) { + $noSudoPesterParam['IncludeFailingTest'] = $true + } + + # Get the experimental feature names and the tests associated with them + $ExperimentalFeatureTests = Get-ExperimentalFeatureTests + + # Running tests which do not require sudo. + $pesterPassThruNoSudoObject = Start-PSPester @noSudoPesterParam -Title 'Pester No Sudo' + + # 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') + } + 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') + } + 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 + try { + $allTestResultsWithNoExpFeature = @($pesterPassThruNoSudoObject, $pesterPassThruSudoObject) + $allTestResultsWithExpFeatures = $noSudoResultsWithExpFeatures + $sudoResultsWithExpFeatures + # This throws if there was an error: + $allTestResultsWithNoExpFeature | ForEach-Object { Test-PSPesterResults -ResultObject $_ } + $allTestResultsWithExpFeatures | ForEach-Object { Test-PSPesterResults -ResultObject $_ -CanHaveNoResult } + $result = "PASS" + } catch { + # The build failed, set the result: + $resultError = $_ + $result = "FAIL" + } + + try { + $ParallelXUnitTestResultsFile = "$pwd/ParallelXUnitTestResults.xml" + Start-PSxUnit -ParallelTestResultsFile $ParallelXUnitTestResultsFile + # If there are failures, Test-XUnitTestResults throws + Test-XUnitTestResults -TestResultsFile $ParallelXUnitTestResultsFile + } catch { + $result = "FAIL" + if (!$resultError) + { + $resultError = $_ + } + } + + 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($isDailyBuild -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($isDailyBuild) + { + 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 ($isDailyBuild) + { + New-TestPackage -Destination "${env:SYSTEM_ARTIFACTSDIRECTORY}" + } + } + + # If the tests did not pass, throw the reason why + if ( $result -eq "FAIL" ) + { + Write-Warning "Tests failed. See the issue below." + Throw $resultError + } + else + { + Write-Verbose "Tests did not fail! Nice job!" + } +} diff --git a/tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml b/tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml index 1b32bb07079..ae697e70e33 100644 --- a/tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml +++ b/tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml @@ -7,5 +7,5 @@ steps: { throw "nuget.config is not created" } - displayName: 'Add nuget.config for AzDevOps feed for PSGallery modules ' + displayName: 'Add nuget.config for Azure DevOps feed for PSGallery modules' condition: and(succeededOrFailed(), ne(variables['AzDevOpsFeed'], '')) diff --git a/tools/releaseTools.psm1 b/tools/releaseTools.psm1 index 2f9d0dcdc8c..42274c8f4f0 100644 --- a/tools/releaseTools.psm1 +++ b/tools/releaseTools.psm1 @@ -1,7 +1,7 @@ #requires -Version 6.0 - # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. + class CommitNode { [string] $Hash [string[]] $Parents @@ -43,6 +43,7 @@ $Script:community_login_map = @{ "info@powercode-consulting.se" = "powercode" } +# ignore dependency bumping bot (dependabot): $Script:attribution_ignore_list = @( 'dependabot[bot]@users.noreply.github.com' ) diff --git a/tools/travis.ps1 b/tools/travis.ps1 deleted file mode 100644 index 2b08cddf95a..00000000000 --- a/tools/travis.ps1 +++ /dev/null @@ -1,453 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -param( - [ValidateSet('Bootstrap','Build','Failure','Success')] - [String]$Stage = 'Build', - [String]$NugetKey -) - -Import-Module $PSScriptRoot/../build.psm1 -Force -Import-Module $PSScriptRoot/packaging -Force - -function Send-DailyWebHook -{ - param ( - [Parameter(Mandatory=$true,Position=0)][ValidateSet("Pass","Fail")]$result - ) - - # Only send web hook if the environment variable is present - # Varible should be set in Travis-CI.org settings - if ($env:WebHookUrl) - { - Write-Log "Sending DailyWebHook with result '$result'." - $webhook = $env:WebHookUrl - - $Body = @{ - 'text'= @" -Build Result: $result
-OS Type: $($PSVersionTable.OS)
-Build $env:TRAVIS_BUILD_NUMBER
-Job $env:TRAVIS_JOB_NUMBER -"@ - } - - $params = @{ - Headers = @{'accept'='application/json'} - Body = $Body | convertto-json - Method = 'Post' - URI = $webhook - } - - Invoke-RestMethod @params - } - else - { - Write-Log "Skipping DailyWebHook. WebHookUrl environment variable not present." - } -} - -function Get-ReleaseTag -{ - $metaDataPath = Join-Path -Path $PSScriptRoot -ChildPath 'metadata.json' - $metaData = Get-Content $metaDataPath | ConvertFrom-Json - - $releaseTag = $metadata.PreviewReleaseTag - $previewVersion = $releaseTag.Split('-') - $previewPrefix = $previewVersion[0] - $previewLabel = $previewVersion[1].replace('.','') - - if($isDailyBuild) - { - $previewLabel= "daily{0}" -f $previewLabel - } - - $preReleaseVersion = "$previewPrefix-$previewLabel.$env:BUILD_BUILDID" - - return $preReleaseVersion -} - -# This function retrieves the appropriate svg to be used when presenting -# the daily test run badge -# the location in azure is public readonly -function Get-DailyBadge -{ - param ( - [Parameter(Mandatory=$true,Position=0)][ValidateSet("Pass","Fail")]$result - ) - $PASS = "https://jimtru1979.blob.core.windows.net/badges/DailyBuild.Pass.svg" - $FAIL = "https://jimtru1979.blob.core.windows.net/badges/DailyBuild.Fail.svg" - - if ( $result -eq "Pass" ) { $BadgeUrl = $PASS } else { $BadgeUrl = $FAIL } - $response = Invoke-WebRequest -Uri $BadgeUrl - if ( $response.StatusCode -ne 200 ) { throw "Could not read badge '$BadgeUrl'" } - $response.Content -} - -# This function uses Azure REST api to update the daily test pass results -# it relies on writing a specific SVG into a constant location so the -# README.MD can report on the status of the daily test pass -# it also relies on two environment variables which need to be set in the -# Travis-CI config which is the account name and key for the azure blob location -# -# the best way to do this would be if travis-ci supported a webcall to get -# the status of cron_job builds, but it doesn't, so we have this -# also, since we can have a build on Linux which succeeds and one on macOS which -# doesn't we'll set the appropriate badge so the the README can pick it up -function Set-DailyBuildBadge -{ - [CmdletBinding(SupportsShouldProcess=$true)] - param ( [Parameter(Mandatory=$true,Position=0)]$content ) - $method = "PUT" - $headerDate = '2015-12-11' - - $storageAccountName = $Env:TestResultAccountName - $storageAccountKey = $Env:TestResultAccountKey - - # this is the url referenced in README.MD which displays the badge - $platform = if ( $IsLinux ) { "Linux" } else { "OSX" } - $Url = "https://jimtru1979.blob.core.windows.net/badges/DailyBuildStatus.${platform}.svg" - - $body = $content - $bytes = ([System.Text.Encoding]::UTF8.GetBytes($body)) - $contentLength = $bytes.length - - $now = [datetime]::UtcNow.ToString("R", [System.Globalization.CultureInfo]::InvariantCulture) - $headers = @{ - "x-ms-date" = $now - "cache-control" = "no-cache" - "x-ms-blob-type" = "BlockBlob" - "x-ms-version" = "$headerDate" - } - - $contentType = "image/svg+xml" - # more info: https://docs.microsoft.com/rest/api/storageservices/fileservices/put-blob - $sb = [text.stringbuilder]::new() - # can't use AppendLine because the `r`n causes the command to fail, it must be `n and only `n - $null = $sb.Append("$method`n") - $null = $sb.Append("`n") - $null = $sb.Append("`n") - $null = $sb.Append("$contentLength`n") - $null = $sb.Append("`n") - $null = $sb.Append("$contentType`n") - $null = $sb.Append("`n") - $null = $sb.Append("`n") - $null = $sb.Append("`n") - $null = $sb.Append("`n") - $null = $sb.Append("`n") - $null = $sb.Append("`n") - - $null = $sb.Append("x-ms-blob-type:" + $headers["x-ms-blob-type"] + "`n") - $null = $sb.Append("x-ms-date:" + $headers["x-ms-date"] + "`n") - $null = $sb.Append("x-ms-version:" + $headers["x-ms-version"] + "`n") - $null = $sb.Append("/" + $storageAccountName + ([System.Uri]::new($url).AbsolutePath)) - - $dataToMac = [System.Text.Encoding]::UTF8.GetBytes($sb.ToString()) - $accountKeyBytes = [System.Convert]::FromBase64String($storageAccountKey) - $hmac = [System.Security.Cryptography.HMACSHA256]::new($accountKeyBytes) - $signature = [System.Convert]::ToBase64String($hmac.ComputeHash($dataToMac)) - - $headers["Authorization"] = "SharedKey " + $storageAccountName + ":" + $signature - - if ( $PSCmdlet.ShouldProcess("$signaturestring")) - { - # if this fails, it will throw, you can't check the response for a success code - $response = Invoke-RestMethod -Uri $Url -Method $method -headers $headers -Body $body -ContentType "image/svg+xml" - } -} - -# https://docs.travis-ci.com/user/environment-variables/ -# TRAVIS_EVENT_TYPE: Indicates how the build was triggered. -# One of push, pull_request, api, cron. -$isPR = $env:TRAVIS_EVENT_TYPE -eq 'pull_request' - -$commitMessage = [string]::Empty - -# For PRs, Travis-ci strips out [ and ] so read the message directly from git -if($env:TRAVIS_EVENT_TYPE -eq 'pull_request' -or $env:BUILD_REASON) -{ - $commitId = $null - if ($env:TRAVIS_EVENT_TYPE) - { - # We are in Travis-CI - $commitId = $env:TRAVIS_PULL_REQUEST_SHA - - # If the current job is a pull request, the env variable 'TRAVIS_PULL_REQUEST_SHA' contains - # the commit SHA of the HEAD commit of the PR. - $commitMessage = git log --format=%B -n 1 $commitId - Write-Log -message "commitMessage: $commitMessage" - } - elseif($env:TF_BUILD) - { - if($env:BUILD_SOURCEVERSIONMESSAGE -match 'Merge\s*([0-9A-F]*)') - { - # We are in VSTS and have a commit ID in the Source Version Message - $commitId = $Matches[1] - $commitMessage = git log --format=%B -n 1 $commitId - } - else - { - Write-Log "Unknown BUILD_SOURCEVERSIONMESSAGE format '$env:BUILD_SOURCEVERSIONMESSAGE'" -Verbose - } - } -} -else -{ - $commitMessage = $env:TRAVIS_COMMIT_MESSAGE -} - -# Run a full build if the build was trigger via cron, api or the commit message contains `[Feature]` -# or the environment variable `FORCE_FEATURE` equals `True` -$hasFeatureTag = $commitMessage -match '\[feature\]' -or $env:FORCE_FEATURE -eq 'True' - -# Run a packaging if the commit message contains `[Package]` -# or the environment variable `FORCE_PACKAGE` equals `True` -$hasPackageTag = $commitMessage -match '\[package\]' -or $env:FORCE_PACKAGE -eq 'True' -$createPackages = -not $isPr -or $hasPackageTag -$hasRunFailingTestTag = $commitMessage -match '\[includeFailingTest\]' -$isDailyBuild = $env:TRAVIS_EVENT_TYPE -eq 'cron' -or $env:TRAVIS_EVENT_TYPE -eq 'api' -or $env:BUILD_REASON -eq 'Schedule' -# only update the build badge for the cron job -$cronBuild = $env:TRAVIS_EVENT_TYPE -eq 'cron' -or $env:BUILD_REASON -eq 'Schedule' -$isFullBuild = $isDailyBuild -or $hasFeatureTag - -if($Stage -eq 'Bootstrap') -{ - if($cronBuild -and $env:TF_BUILD) - { - Write-Host "##vso[build.updatebuildnumber]Daily-$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" - } - - Write-Host -Foreground Green "Executing travis.ps1 -BootStrap `$isPR='$isPr' - $commitMessage" - # Make sure we have all the tags - Sync-PSTags -AddRemoteIfMissing - Start-PSBootstrap -Package:$createPackages -} -elseif($Stage -eq 'Build') -{ - $releaseTag = Get-ReleaseTag - - Write-Host -Foreground Green "Executing travis.ps1 `$isPR='$isPr' `$isFullBuild='$isFullBuild' - $commitMessage" - - $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 - } - - $output = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions)) - - $testResultsNoSudo = "$pwd/TestResultsNoSudo.xml" - $testResultsSudo = "$pwd/TestResultsSudo.xml" - - $excludeTag = @('RequireSudoOnUnix') - - $noSudoPesterParam = @{ - 'BinDir' = $output - 'PassThru' = $true - 'Terse' = $true - 'Tag' = @() - 'ExcludeTag' = $excludeTag - 'OutputFile' = $testResultsNoSudo - } - - if ($isFullBuild) { - $noSudoPesterParam['Tag'] = @('CI','Feature','Scenario') - } else { - $noSudoPesterParam['Tag'] = @('CI') - $noSudoPesterParam['ThrowOnFailure'] = $true - } - - if ($hasRunFailingTestTag) { - $noSudoPesterParam['IncludeFailingTest'] = $true - } - - # Get the experimental feature names and the tests associated with them - $ExperimentalFeatureTests = Get-ExperimentalFeatureTests - - # Running tests which do not require sudo. - $pesterPassThruNoSudoObject = Start-PSPester @noSudoPesterParam -Title 'Pester No Sudo' - - # 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') - } 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') - } 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 - try { - $allTestResultsWithNoExpFeature = @($pesterPassThruNoSudoObject, $pesterPassThruSudoObject) - $allTestResultsWithExpFeatures = $noSudoResultsWithExpFeatures + $sudoResultsWithExpFeatures - # this throws if there was an error - $allTestResultsWithNoExpFeature | ForEach-Object { Test-PSPesterResults -ResultObject $_ } - $allTestResultsWithExpFeatures | ForEach-Object { Test-PSPesterResults -ResultObject $_ -CanHaveNoResult } - $result = "PASS" - } - catch { - $resultError = $_ - $result = "FAIL" - } - - try { - $ParallelXUnitTestResultsFile = "$pwd/ParallelXUnitTestResults.xml" - - Start-PSxUnit -ParallelTestResultsFile $ParallelXUnitTestResultsFile - # If there are failures, Test-XUnitTestResults throws - Test-XUnitTestResults -TestResultsFile $ParallelXUnitTestResultsFile - } - catch { - $result = "FAIL" - if (!$resultError) - { - $resultError = $_ - } - } - - if ($createPackages) { - - $packageParams = @{} - $packageParams += @{ReleaseTag=$releaseTag} - - # Only build packages for 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($isDailyBuild -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($isDailyBuild) - { - 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 ($isDailyBuild) - { - New-TestPackage -Destination "${env:SYSTEM_ARTIFACTSDIRECTORY}" - } - } - - # if the tests did not pass, throw the reason why - if ( $result -eq "FAIL" ) { - Throw $resultError - } -} -elseif($Stage -in 'Failure', 'Success') -{ - $result = 'PASS' - if($Stage -eq 'Failure') - { - $result = 'FAIL' - } - - if ($cronBuild) { - # update the badge if you've done a cron build, these are not fatal issues - try { - $svgData = Get-DailyBadge -result $result - if ( ! $svgData ) { - write-warning "Could not retrieve $result badge" - } - else { - Write-Log "Setting status badge to '$result'" - Set-DailyBuildBadge -content $svgData - } - } - catch { - Write-Warning "Could not update status badge: $_" - } - - try { - Send-DailyWebHook -result $result - } - catch { - Write-Warning "Could not send webhook: $_" - } - } - else { - Write-Log 'We only send bagde or webhook update for Cron builds' - } - -} diff --git a/tools/windows/Reset-PWSHSystemPath.ps1 b/tools/windows/Reset-PWSHSystemPath.ps1 index eb3b182d419..527f726bd66 100644 --- a/tools/windows/Reset-PWSHSystemPath.ps1 +++ b/tools/windows/Reset-PWSHSystemPath.ps1 @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + <# .SYNOPSIS Idempotently removes extra PowerShell Core paths from the machine, user and/or process environment scope with no reordering.