PowerShell/tools/appveyor.psm1

554 lines
18 KiB
PowerShell
Raw Normal View History

$ErrorActionPreference = 'Stop'
$repoRoot = Join-Path $PSScriptRoot '..'
$script:administratorsGroupSID = "S-1-5-32-544"
$script:usersGroupSID = "S-1-5-32-545"
$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-Module (Join-Path $repoRoot 'build.psm1') -Scope Global
Import-Module (Join-Path $repoRoot 'tools\packaging')
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
#>
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);
}
# tests if we should run a daily build
2017-01-16 22:31:14 +01:00
# returns true if the build is scheduled
# or is a pushed tag
Function Test-DailyBuild
{
2017-07-23 00:34:03 +02:00
$trueString = 'True'
if(($env:PS_DAILY_BUILD -eq $trueString) -or ($env:APPVEYOR_SCHEDULED_BUILD -eq $trueString) -or ($env:APPVEYOR_REPO_TAG_NAME))
{
return $true
}
# if [Feature] is in the commit message,
# Run Daily tests
if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match '\[feature\]')
{
2017-07-23 00:34:03 +02:00
Set-AppveyorBuildVariable -Name PS_DAILY_BUILD -Value $trueString
return $true
}
return $false
}
# Sets a build variable
Function Set-BuildVariable
{
param(
[Parameter(Mandatory=$true)]
[string]
$Name,
[Parameter(Mandatory=$true)]
[string]
$Value
)
if($env:AppVeyor)
{
Set-AppveyorBuildVariable @PSBoundParameters
}
2017-01-16 22:31:14 +01:00
else
{
Set-Item env:/$name -Value $Value
}
}
# Emulates running all of AppVeyor but locally
# should not be used on AppVeyor
function Invoke-AppVeyorFull
{
param(
[switch] $APPVEYOR_SCHEDULED_BUILD,
[switch] $CleanRepo
)
if($CleanRepo)
{
Clear-PSRepo
}
if($env:APPVEYOR)
{
throw "This function is to simulate appveyor, but not to be run from appveyor!"
}
if($APPVEYOR_SCHEDULED_BUILD)
{
$env:APPVEYOR_SCHEDULED_BUILD = 'True'
}
try {
Invoke-AppVeyorInstall
Invoke-AppVeyorBuild
Invoke-AppVeyorTest -ErrorAction Continue
Invoke-AppveyorFinish
}
finally {
if($APPVEYOR_SCHEDULED_BUILD -and $env:APPVEYOR_SCHEDULED_BUILD)
{
Remove-Item env:APPVEYOR_SCHEDULED_BUILD
}
}
}
# Implements the AppVeyor '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" ) {
$result.Warnings
throw "Tags must be CI, Feature, Scenario, or Slow"
}
Add OpenCover PS Module to collect code coverage (#2585) * Add OpenCover PS Module to collect code coverage OpenCover PS Module helps is collecting Code Coverage using the OpenCover toolset. The module helps in comparing two code coverage runs as well. * Change OpenCover.psd1 to ASCII * Fix an error in path for OpenCover Fixed an error on path for OpenCover. Also used ZipFile class instead of cmdlet as it might not be available on CI system. * Convert module to be Powershell v4 compliant Changed implementation from classes to PSObjects and implemented Expand-ZipArchive. * Added CodeCoverage as a configuration to project.json files Added CodeCoverage as the new configuration for all the project.json files. When Start-PSBuild is executed with configuration as CodeCoverage, we change the degubType to 'full' as required by OpenCover toolset. Also made changes to appveyor.psm1 to build a CodeCoverage package on daily builds and publish it as a zip. * Addressed code review comments Changed from Add-Member to use pscustomobject type accelator. Removed [gc]::collect. * Added explicit garbage collection * Addressed code review comments - Make sure that the build Start-PSPackage gets is not a code coverage build - Add debugType = full for FullCLR - Remove configurations from PackageManagement files as it is not needed. - Build CodeCoverage build first in AppVeyor. * Resolve merge conflict * Fix indentation * Fix newline at end of file * Added command discovery for locating OpenCover.console.exe
2016-11-11 20:12:07 +01:00
if(Test-DailyBuild)
{
Start-PSBuild -Configuration 'CodeCoverage' -PSModuleRestore -CI -ReleaseTag $releaseTag
}
Add OpenCover PS Module to collect code coverage (#2585) * Add OpenCover PS Module to collect code coverage OpenCover PS Module helps is collecting Code Coverage using the OpenCover toolset. The module helps in comparing two code coverage runs as well. * Change OpenCover.psd1 to ASCII * Fix an error in path for OpenCover Fixed an error on path for OpenCover. Also used ZipFile class instead of cmdlet as it might not be available on CI system. * Convert module to be Powershell v4 compliant Changed implementation from classes to PSObjects and implemented Expand-ZipArchive. * Added CodeCoverage as a configuration to project.json files Added CodeCoverage as the new configuration for all the project.json files. When Start-PSBuild is executed with configuration as CodeCoverage, we change the degubType to 'full' as required by OpenCover toolset. Also made changes to appveyor.psm1 to build a CodeCoverage package on daily builds and publish it as a zip. * Addressed code review comments Changed from Add-Member to use pscustomobject type accelator. Removed [gc]::collect. * Added explicit garbage collection * Addressed code review comments - Make sure that the build Start-PSPackage gets is not a code coverage build - Add debugType = full for FullCLR - Remove configurations from PackageManagement files as it is not needed. - Build CodeCoverage build first in AppVeyor. * Resolve merge conflict * Fix indentation * Fix newline at end of file * Added command discovery for locating OpenCover.console.exe
2016-11-11 20:12:07 +01:00
Start-PSBuild -CrossGen -PSModuleRestore -Configuration 'Release' -CI -ReleaseTag $releaseTag
}
# Implements the AppVeyor 'install' step
function Invoke-AppVeyorInstall
{
# Make sure we have all the tags
Sync-PSTags -AddRemoteIfMissing
$releaseTag = Get-ReleaseTag
if($env:APPVEYOR_BUILD_NUMBER)
{
Update-AppveyorBuild -Version $releaseTag
}
if(Test-DailyBuild){
$buildName = "[Daily]"
# Add daily to title if it's not already there
# It can be there already for rerun requests
if($env:APPVEYOR_PULL_REQUEST_TITLE -and $env:APPVEYOR_PULL_REQUEST_TITLE -notmatch '^\[Daily\]')
{
$buildName += $env:APPVEYOR_PULL_REQUEST_TITLE
}
elseif($env:APPVEYOR_PULL_REQUEST_TITLE)
{
$buildName = $env:APPVEYOR_PULL_REQUEST_TITLE
}
elseif($env:APPVEYOR_REPO_COMMIT_MESSAGE -notmatch '^\[Daily\].*$')
{
$buildName += $env:APPVEYOR_REPO_COMMIT_MESSAGE
}
else
{
$buildName = $env:APPVEYOR_REPO_COMMIT_MESSAGE
}
Update-AppveyorBuild -message $buildName
}
if ($env:APPVEYOR)
{
#
# Generate new credential for appveyor (only) remoting tests.
#
Write-Verbose "Creating account for remoting tests in AppVeyor."
# Password
$randomObj = [System.Random]::new()
$password = ""
1..(Get-Random -Minimum 15 -Maximum 126) | ForEach-Object { $password = $password + [char]$randomObj.next(45,126) }
# Account
$userName = 'appVeyorRemote'
New-LocalUser -username $userName -password $password
Add-UserToGroup -username $userName -groupSid $script:administratorsGroupSID
# 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
# Check that LocalAccountTokenFilterPolicy policy is set, since it is needed for remoting
# using above local admin account.
Write-Verbose "Checking for LocalAccountTokenFilterPolicy in AppVeyor."
$haveLocalAccountTokenFilterPolicy = $false
try
{
$haveLocalAccountTokenFilterPolicy = ((Get-ItemPropertyValue -Path HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Name LocalAccountTokenFilterPolicy) -eq 1)
}
catch { }
if (!$haveLocalAccountTokenFilterPolicy)
{
Write-Verbose "Setting the LocalAccountTokenFilterPolicy for remoting tests"
Set-ItemProperty -Path HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Name LocalAccountTokenFilterPolicy -Value 1
}
}
Set-BuildVariable -Name TestPassed -Value False
Start-PSBootstrap -Confirm:$false
}
# 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
{
param(
[string] $resultsFile
)
if($env:Appveyor)
{
2017-01-16 22:31:14 +01:00
$retryCount = 0
$pushedResults = $false
2017-01-16 22:31:14 +01:00
$pushedArtifacts = $false
while( (!$pushedResults -or !$pushedResults) -and $retryCount -lt 3)
{
if($retryCount -gt 0)
{
Write-Verbose "Retrying updating test artifacts..."
}
$retryCount++
$resolvedResultsPath = (Resolve-Path $resultsFile)
try {
(New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", $resolvedResultsPath)
$pushedResults = $true
}
catch {
Write-Warning "Pushing test result failed..."
}
try {
Push-AppveyorArtifact $resolvedResultsPath
$pushedArtifacts = $true
}
catch {
Write-Warning "Pushing test Artifact failed..."
}
}
if(!$pushedResults -or !$pushedResults)
{
Write-Warning "Failed to push all artifacts for $resultsFile"
}
}
2017-01-16 22:31:14 +01:00
else
{
Write-Warning "Not running in appveyor, skipping upload of test results: $resultsFile"
}
}
# Implement AppVeyor 'Test_script'
2017-01-16 22:31:14 +01:00
function Invoke-AppVeyorTest
{
[CmdletBinding()]
param()
#
# CoreCLR
2017-01-16 22:31:14 +01:00
$env:CoreOutput = Split-Path -Parent (Get-PSOutput -Options (Get-PSOptions))
Write-Host -Foreground Green 'Run CoreCLR tests'
$testResultsNonAdminFile = "$pwd\TestsResultsNonAdmin.xml"
$testResultsAdminFile = "$pwd\TestsResultsAdmin.xml"
$SequentialXUnitTestResultsFile = "$pwd\SequentialXUnitTestResults.xml"
$ParallelXUnitTestResultsFile = "$pwd\ParallelXUnitTestResults.xml"
if(!(Test-Path "$env:CoreOutput\pwsh.exe"))
{
throw "CoreCLR pwsh.exe was not built"
}
2017-01-16 22:31:14 +01:00
if(-not (Test-DailyBuild))
{
# 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
$ExcludeTag = @('Slow', 'Feature', 'Scenario')
Write-Host -Foreground Green 'Running "CI" CoreCLR tests..'
}
2017-01-16 22:31:14 +01:00
else
{
$ExcludeTag = @()
Write-Host -Foreground Green 'Running all CoreCLR tests..'
}
2017-01-16 22:31:14 +01:00
# Remove telemetry semaphore file in CI
$telemetrySemaphoreFilepath = Join-Path $env:CoreOutput DELETE_ME_TO_DISABLE_CONSOLEHOST_TELEMETRY
if ( Test-Path "${telemetrySemaphoreFilepath}" ) {
Remove-Item -Force ${telemetrySemaphoreFilepath}
}
2017-09-19 23:56:32 +02:00
Start-PSPester -Terse -bindir $env:CoreOutput -outputFile $testResultsNonAdminFile -Unelevate -Tag @() -ExcludeTag ($ExcludeTag + @('RequireAdminOnWindows'))
Write-Host -Foreground Green 'Upload CoreCLR Non-Admin test results'
Update-AppVeyorTestResults -resultsFile $testResultsNonAdminFile
2017-09-19 23:56:32 +02:00
Start-PSPester -Terse -bindir $env:CoreOutput -outputFile $testResultsAdminFile -Tag @('RequireAdminOnWindows') -ExcludeTag $ExcludeTag
Write-Host -Foreground Green 'Upload CoreCLR Admin test results'
Update-AppVeyorTestResults -resultsFile $testResultsAdminFile
2017-01-16 22:31:14 +01:00
Start-PSxUnit -SequentialTestResultsFile $SequentialXUnitTestResultsFile -ParallelTestResultsFile $ParallelXUnitTestResultsFile
Write-Host -ForegroundColor Green 'Uploading PSxUnit test results'
Update-AppVeyorTestResults -resultsFile $SequentialXUnitTestResultsFile
Update-AppVeyorTestResults -resultsFile $ParallelXUnitTestResultsFile
#
# Fail the build, if tests failed
@(
$testResultsNonAdminFile,
2017-03-23 21:04:52 +01:00
$testResultsAdminFile
) | ForEach-Object {
Test-PSPesterResults -TestResultsFile $_
}
@(
$SequentialXUnitTestResultsFile,
$ParallelXUnitTestResultsFile
) | ForEach-Object {
Test-XUnitTestResults -TestResultsFile $_
}
Set-BuildVariable -Name TestPassed -Value True
}
#Implement AppVeyor 'after_test' phase
function Invoke-AppVeyorAfterTest
{
[CmdletBinding()]
param()
if(Test-DailyBuild)
{
## Publish code coverage build, tests and OpenCover module to artifacts, so webhook has the information.
2017-01-16 22:31:14 +01:00
## Build webhook is called after 'after_test' phase, hence we need to do this here and not in AppveyorFinish.
$codeCoverageOutput = Split-Path -Parent (Get-PSOutput -Options (New-PSOptions -Configuration CodeCoverage))
$codeCoverageArtifacts = Compress-CoverageArtifacts -CodeCoverageOutput $codeCoverageOutput
Write-Host -ForegroundColor Green 'Upload CodeCoverage artifacts'
$codeCoverageArtifacts | ForEach-Object { Push-AppveyorArtifact $_ }
}
}
function Compress-CoverageArtifacts
{
param([string] $CodeCoverageOutput)
# 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)
Add-Type -AssemblyName System.IO.Compression.FileSystem
$resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath((Join-Path $PSScriptRoot '..\test\tools\OpenCover'))
$zipOpenCoverPath = Join-Path $pwd 'OpenCover.zip'
2017-01-16 22:31:14 +01:00
[System.IO.Compression.ZipFile]::CreateFromDirectory($resolvedPath, $zipOpenCoverPath)
$null = $artifacts.Add($zipOpenCoverPath)
$zipCodeCoveragePath = Join-Path $pwd "CodeCoverage.zip"
Write-Verbose "Zipping ${CodeCoverageOutput} into $zipCodeCoveragePath" -verbose
[System.IO.Compression.ZipFile]::CreateFromDirectory($CodeCoverageOutput, $zipCodeCoveragePath)
$null = $artifacts.Add($zipCodeCoveragePath)
return $artifacts
}
function Get-ReleaseTag
{
$metaDataPath = Join-Path -Path $PSScriptRoot -ChildPath 'metadata.json'
$metaData = Get-Content $metaDataPath | ConvertFrom-Json
$releaseTag = $metadata.NextReleaseTag
if($env:APPVEYOR_BUILD_NUMBER)
{
$releaseTag = $releaseTag.split('.')[0..2] -join '.'
$releaseTag = $releaseTag+'.'+$env:APPVEYOR_BUILD_NUMBER
}
return $releaseTag
}
# Implements AppVeyor 'on_finish' step
function Invoke-AppveyorFinish
{
$exitCode = 0
try {
$releaseTag = Get-ReleaseTag
# Build packages
$packages = Start-PSPackage -Type msi,nupkg,zip -ReleaseTag $releaseTag -SkipReleaseChecks
$artifacts = New-Object System.Collections.ArrayList
foreach ($package in $packages) {
$null = $artifacts.Add($package)
}
if ($env:APPVEYOR_REPO_TAG_NAME)
{
$preReleaseVersion = $env:APPVEYOR_REPO_TAG_NAME
}
else
{
$previewVersion = $releaseTag.Split('-')
$previewPrefix = $previewVersion[0]
$previewLabel = $previewVersion[1].replace('.','')
if(Test-DailyBuild)
{
$previewLabel= "daily{0}" -f $previewLabel
}
$preReleaseVersion = "$previewPrefix-$previewLabel.$env:APPVEYOR_BUILD_NUMBER"
}
# Smoke Test MSI installer
Write-Verbose "Smoke-Testing MSI installer" -Verbose
$msi = $artifacts | Where-Object { $_.EndsWith(".msi") }
$msiLog = Join-Path (Get-Location) 'msilog.txt'
$msiExecProcess = Start-Process msiexec.exe -Wait -ArgumentList "/I $msi /quiet /l*vx $msiLog" -NoNewWindow -PassThru
if ($msiExecProcess.ExitCode -ne 0)
{
Push-AppveyorArtifact msiLog.txt
$exitCode = $msiExecProcess.ExitCode
throw "MSI installer failed and returned error code $exitCode. MSI Log was uploaded as artifact."
}
Write-Verbose "MSI smoke test was successful" -Verbose
# only publish assembly nuget packages if it is a daily build and tests passed
if((Test-DailyBuild) -and $env:TestPassed -eq 'True')
{
Publish-NuGetFeed -OutputPath .\nuget-artifacts -ReleaseTag $preReleaseVersion
$nugetArtifacts = Get-ChildItem .\nuget-artifacts -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName }
if($nugetArtifacts)
{
$artifacts.AddRange($nugetArtifacts)
}
}
if (Test-DailyBuild)
{
# produce win-arm and win-arm64 packages if it is a daily build
Start-PSBuild -Runtime win-arm -PSModuleRestore -Configuration 'Release' -ReleaseTag $releaseTag
$arm32Package = Start-PSPackage -Type zip -WindowsRuntime win-arm -ReleaseTag $releaseTag -SkipReleaseChecks
$artifacts.Add($arm32Package)
Start-PSBuild -Runtime win-arm64 -PSModuleRestore -Configuration 'Release' -ReleaseTag $releaseTag
$arm64Package = Start-PSPackage -Type zip -WindowsRuntime win-arm64 -ReleaseTag $releaseTag -SkipReleaseChecks
$artifacts.Add($arm64Package)
}
$pushedAllArtifacts = $true
2017-01-16 22:31:14 +01:00
$artifacts | ForEach-Object {
Write-Host "Pushing $_ as Appveyor artifact"
if(Test-Path $_)
{
if($env:Appveyor)
{
Push-AppveyorArtifact $_
}
}
else
{
$pushedAllArtifacts = $false
Write-Warning "Artifact $_ does not exist."
}
if($env:NUGET_KEY -and $env:NUGET_URL -and [system.io.path]::GetExtension($_) -ieq '.nupkg')
{
log "pushing $_ to $env:NUGET_URL"
Start-NativeExecution -sb {dotnet nuget push $_ --api-key $env:NUGET_KEY --source "$env:NUGET_URL/api/v2/package"} -IgnoreExitcode
}
}
if(!$pushedAllArtifacts)
{
throw "Some artifacts did not exist!"
}
2017-01-16 22:31:14 +01:00
}
catch {
Write-Host -Foreground Red $_
}
finally {
# A throw statement would not make the build fail. This function is AppVeyor specific
# and is the only command executed in 'on_finish' phase, so it's safe that we request
# the current runspace to exit with the specified exit code. If the exit code is non-zero,
# AppVeyor will fail the build.
# See this link for details:
# https://help.appveyor.com/discussions/problems/4498-powershell-exception-in-test_script-does-not-fail-build
$host.SetShouldExit($exitCode)
}
}