Skip to content

Commit

Permalink
CI Build updates: build queuing and Windows PS7 (#260)
Browse files Browse the repository at this point in the history
This updates the CI build with two new features:

1. **Validation against Windows PS7 (PowerShell Core)**
   This pipeline previously had three platforms that it tested against:
   * `Windows PS5`
   * `Linux PS7 (PowerShell Core)`
   * `macOS PS7 (PowerShell Core)`

   With this change, we add a fourth platform: `Windows PS7 (PowerShell Core)`

2. **Build queuing**
   The unit tests operate against live GitHub accounts (as opposed to mocking out the execution of the API calls).  Each platform has its own account that it operates against to alllow each platform to be tested in parallel.  However, if more than one build is queued at once, then the builds can start to stomp over the expected state in the tests accounts.  This change adds a new `job` to the pipeline which will create a "queue" of running builds by only allowing a build to continue processing once all previously queued builds have completed.

Initial idea came from here:
  https://developercommunity.visualstudio.com/idea/365730/prevent-parallel-execution-of-the-same-build-defin.html

- Fixes #239
  • Loading branch information
HowardWolosky authored Jul 1, 2020
1 parent 2740026 commit 79e5ac2
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 44 deletions.
41 changes: 39 additions & 2 deletions build/pipelines/azure-pipelines.ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,42 @@ trigger:
#- master

jobs:
- job: Windows
- job: waitForRunningBuilds
displayName: 'Wait for Running Builds'
pool:
vmImage: 'windows-latest'
timeoutInMinutes: 360
continueOnError: true
steps:
- template: ./templates/wait-runningBuild.yaml
parameters:
buildQueryPersonalAccessToken: $(BuildQueryPersonalAccessToken)

- job: windowsWPS
displayName: 'Windows [Windows PowerShell]'
pool:
vmImage: 'windows-latest'
dependsOn: waitForRunningBuilds
steps:
- template: ./templates/verify-testConfigSettingsHash.yaml
parameters:
usePowerShellCore: false
- template: ./templates/run-staticAnalysis.yaml
parameters:
usePowerShellCore: false
- template: ./templates/run-unitTests.yaml
parameters:
gitHubAccessToken: $(WindowsPS5CIGitHubAccessToken)
gitHubOwnerName: $(WindowsPS5CIGitHubOwnerName)
gitHubOrganizationName: $(WindowsPS5CIGitHubOrganizationName)
platformName: 'Windows'
usePowerShellCore: false

- job: windows
displayName: 'Windows [PowerShell Core]'
pool:
vmImage: 'windows-latest'
dependsOn: waitForRunningBuilds
steps:
- template: ./templates/verify-testConfigSettingsHash.yaml
- template: ./templates/run-staticAnalysis.yaml
Expand All @@ -32,9 +65,11 @@ jobs:
gitHubOrganizationName: $(WindowsCIGitHubOrganizationName)
platformName: 'Windows'

- job: Linux
- job: linux
displayName: 'Linux [PowerShell Core]'
pool:
vmImage: 'ubuntu-latest'
dependsOn: waitForRunningBuilds
steps:
- template: ./templates/verify-testConfigSettingsHash.yaml
- template: ./templates/run-staticAnalysis.yaml
Expand All @@ -46,8 +81,10 @@ jobs:
platformName: 'Linux'

- job: macOS
displayName: 'macOS [PowerShell Core]'
pool:
vmImage: 'macOS-latest'
dependsOn: waitForRunningBuilds
steps:
- template: ./templates/verify-testConfigSettingsHash.yaml
- template: ./templates/run-staticAnalysis.yaml
Expand Down
33 changes: 24 additions & 9 deletions build/pipelines/templates/run-staticAnalysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,34 @@
# report the results.
#

parameters:
- name: 'usePowerShellCore'
default: true
type: boolean

steps:
- powershell: |
Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Scope CurrentUser -Force -Verbose
- task: PowerShell@2
displayName: 'Install PSScriptAnalyzer'
inputs:
pwsh: eq('${{ parameters.usePowerShellCore }}', true)
errorActionPreference: 'stop'
targetType: 'inline'
script: |
Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Scope CurrentUser -Force -Verbose
- powershell: |
$results = try { Invoke-ScriptAnalyzer -Settings ./PSScriptAnalyzerSettings.psd1 -Path ./ –Recurse -ErrorAction Stop } catch { 'Unexpected Error'; $_.Exception.StackTrace; if ($IsCoreCLR) { Get-Error }; throw }
$results | ForEach-Object { Write-Host "##vso[task.logissue type=$($_.Severity);sourcepath=$($_.ScriptPath);linenumber=$($_.Line);columnnumber=$($_.Column);]$($_.Message)" }
$null = New-Item -Path ..\ -Name ScriptAnalyzer -ItemType Directory -Force
./build/scripts/ConvertTo-NUnitXml.ps1 -ScriptAnalyzerResult $results -Path ../ScriptAnalyzer/test-results.xml
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PowerShell@2
displayName: 'Run Static Code Analysis (PSScriptAnalyzer)'
inputs:
pwsh: eq('${{ parameters.usePowerShellCore }}', true)
errorActionPreference: 'stop'
workingDirectory: '$(System.DefaultWorkingDirectory)'
targetType: 'inline'
script: |
$results = try { Invoke-ScriptAnalyzer -Settings ./PSScriptAnalyzerSettings.psd1 -Path ./ –Recurse -ErrorAction Stop } catch { 'Unexpected Error'; $_.Exception.StackTrace; if ($IsCoreCLR) { Get-Error }; throw }
$results | ForEach-Object { Write-Host "##vso[task.logissue type=$($_.Severity);sourcepath=$($_.ScriptPath);linenumber=$($_.Line);columnnumber=$($_.Column);]$($_.Message)" }
$null = New-Item -Path ..\ -Name ScriptAnalyzer -ItemType Directory -Force
./build/scripts/ConvertTo-NUnitXml.ps1 -ScriptAnalyzerResult $results -Path ../ScriptAnalyzer/test-results.xml
- task: PublishTestResults@2
displayName: 'Publish ScriptAnalyzer Test Results'
Expand Down
26 changes: 20 additions & 6 deletions build/pipelines/templates/run-unitTests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,31 @@ parameters:
- name: 'platformName'
default: 'Windows'
type: string
- name: 'usePowerShellCore'
default: false
type: boolean

steps:
- powershell: |
Install-Module -Name Pester -Repository PSGallery -Scope CurrentUser -AllowClobber -SkipPublisherCheck -RequiredVersion 4.10.1 -Force -Verbose
- task: PowerShell@2
displayName: 'Install Pester'
inputs:
pwsh: eq('${{ parameters.usePowerShellCore }}', true)
errorActionPreference: 'stop'
workingDirectory: '$(System.DefaultWorkingDirectory)'
targetType: 'inline'
script: |
Install-Module -Name Pester -Repository PSGallery -Scope CurrentUser -AllowClobber -SkipPublisherCheck -RequiredVersion 4.10.1 -Force -Verbose
- powershell: |
$null = New-Item -Path ..\ -Name Pester -ItemType Directory -Force
Invoke-Pester -CodeCoverage .\*.ps*1 -CodeCoverageOutputFile ../Pester/coverage.xml -CodeCoverageOutputFileFormat JaCoCo -EnableExit -Strict -OutputFile ../Pester/test-results.xml -OutputFormat NUnitXml
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PowerShell@2
displayName: 'Run Unit Tests via Pester'
inputs:
pwsh: eq('${{ parameters.usePowerShellCore }}', true)
errorActionPreference: 'stop'
workingDirectory: '$(System.DefaultWorkingDirectory)'
targetType: 'inline'
script: |
$null = New-Item -Path ..\ -Name Pester -ItemType Directory -Force
Invoke-Pester -CodeCoverage .\*.ps*1 -CodeCoverageOutputFile ../Pester/coverage.xml -CodeCoverageOutputFileFormat JaCoCo -EnableExit -Strict -OutputFile ../Pester/test-results.xml -OutputFormat NUnitXml
env:
ciAccessToken: ${{ parameters.gitHubAccessToken }}
ciOwnerName: ${{ parameters.gitHubOwnerName }}
Expand Down
64 changes: 37 additions & 27 deletions build/pipelines/templates/verify-testConfigSettingsHash.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,45 @@
# the same in order to ensure the correct warning messages are presented to developers running UTs.
#

parameters:
- name: 'usePowerShellCore'
default: true
type: boolean

steps:
- powershell: |
. ./Helpers.ps1
$content = Get-Content -Path ./Tests/Config/Settings.ps1 -Raw -Encoding UTF8
$hash = Get-SHA512Hash -PlainText $content
$configurationFile = Get-Content -Path ./GitHubConfiguration.ps1 -Raw -Encoding Utf8
if($configurationFile -match "'testConfigSettingsHash' = '([^']+)'")
{
if($hash -ne $Matches[1])
- task: PowerShell@2
displayName: 'Set GitHubConfiguration.ps1 testConfigSettingsHash value.'
inputs:
pwsh: eq('${{ parameters.usePowerShellCore }}', true)
errorActionPreference: 'stop'
workingDirectory: '$(System.DefaultWorkingDirectory)'
targetType: 'inline'
script: |
. ./Helpers.ps1
$content = Get-Content -Path ./Tests/Config/Settings.ps1 -Raw -Encoding UTF8
$hash = Get-SHA512Hash -PlainText $content
$configurationFile = Get-Content -Path ./GitHubConfiguration.ps1 -Raw -Encoding Utf8
if($configurationFile -match "'testConfigSettingsHash' = '([^']+)'")
{
if($hash -ne $Matches[1])
{
$configHash = $Matches[1]
$message = @(
"`$testConfigSettingsHash value does not match the Get-SHA512Hash of file ./Tests/Config/Settings.ps1. If the contents of ",
"Settings.ps1 has been updated, please update the `$testConfigSettingsHash value in ./GitHubConfiguration.ps1's ",
"Import-GitHubConfiguration function. You can generate the new hash value using the commands:",
"",
"`t. ./Helpers.ps1",
"`tGet-SHA512Hash -PlainText (Get-Content -Path ./Tests/Config/Settings.ps1 -Raw -Encoding Utf8)",
"",
"`$testConfigSettingsHash = $configHash",
"Get-SHA512Hash(Settings.ps1) = $hash")
throw ($message -join [Environment]::NewLine)
}
} else
{
$configHash = $Matches[1]
$message = @(
"`$testConfigSettingsHash value does not match the Get-SHA512Hash of file ./Tests/Config/Settings.ps1. If the contents of ",
"Settings.ps1 has been updated, please update the `$testConfigSettingsHash value in ./GitHubConfiguration.ps1's ",
"Import-GitHubConfiguration function. You can generate the new hash value using the commands:",
"",
"`t. ./Helpers.ps1",
"`tGet-SHA512Hash -PlainText (Get-Content -Path ./Tests/Config/Settings.ps1 -Raw -Encoding Utf8)",
"",
"`$testConfigSettingsHash = $configHash",
"Get-SHA512Hash(Settings.ps1) = $hash")
"`$testConfigSettingsHash value not found in ./GitHubConfiguration.ps1. Please ensure ",
"the default hash value is set within the Import-GitHubConfiguration function.")
throw ($message -join [Environment]::NewLine)
}
} else
{
$message = @(
"`$testConfigSettingsHash value not found in ./GitHubConfiguration.ps1. Please ensure ",
"the default hash value is set within the Import-GitHubConfiguration function.")
throw ($message -join [Environment]::NewLine)
}
displayName: 'Set GitHubConfiguration.ps1 testConfigSettingsHash value.'
39 changes: 39 additions & 0 deletions build/pipelines/templates/wait-runningBuild.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# This template contains the necessary jobs to create a queue for this pipeline and ensure that
# there is never more than one concurrent build actively running at any given time.
#

#--------------------------------------------------------------------------------------------------
# This template is dependent on the following pipeline variables being configured within the pipeline.
#
# 1. buildQueryPersonalAccessToken - An Azure DevOps Personal Access Token with Build READ permission.
# It should be configured as a "secret".
#--------------------------------------------------------------------------------------------------

parameters:
- name: 'buildQueryPersonalAccessToken'
type: string
- name: 'usePowerShellCore'
default: true
type: boolean

steps:
- task: PowerShell@2
displayName: 'Wait for previously queued builds'
inputs:
pwsh: eq('${{ parameters.usePowerShellCore }}', true)
errorActionPreference: 'stop'
workingDirectory: '$(System.DefaultWorkingDirectory)'
targetType: 'inline'
script: |
$params = @{
PersonalAccessToken = $env:buildReadAccessToken
OrganizationName = 'ms'
ProjectName = 'PowerShellForGitHub'
BuildDefinitionId = $(System.DefinitionId)
BuildId = $(Build.BuildId)
}
./build/scripts/Wait-RunningBuild.ps1 @params
env:
buildReadAccessToken: ${{ parameters.buildQueryPersonalAccessToken }}
Loading

0 comments on commit 79e5ac2

Please sign in to comment.