feat: migrate CI to Github Actions (#27)

Migrate CI from Azure Pipelines to GitHub Actions, point it to the main branch.
This commit is contained in:
Andrea Spadaccini 2020-12-25 23:15:44 +00:00 коммит произвёл GitHub
Родитель 0742b2035b
Коммит a88b40aac3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 252 добавлений и 248 удалений

45
.github/workflows/CI.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,45 @@
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: "0 0 * * *"
jobs:
powershell-core:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Restore
run: tools/restore.ps1
shell: pwsh
- name: test
run: tools/run-tests.ps1
shell: pwsh
windows-powershell:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Restore
run: tools/restore.ps1
shell: powershell
- name: test
run: tools/run-tests.ps1
shell: powershell
- name: Test manifest
run: Test-ModuleManifest -Path ./FeatureFlags.psd1
shell: powershell

Просмотреть файл

@ -1,4 +1,3 @@
[![Build Status](https://dev.azure.com/PowerShell-FeatureFlags/PowerShell-FeatureFlags/_apis/build/status/microsoft.PowerShell-FeatureFlags?branchName=master)](https://dev.azure.com/PowerShell-FeatureFlags/PowerShell-FeatureFlags/_build/latest?definitionId=1&branchName=master)
[![Nuget](https://img.shields.io/nuget/v/FeatureFlags.PowerShell)](https://www.nuget.org/packages/FeatureFlags.PowerShell/1.0.0)
[![Platforms](https://img.shields.io/powershellgallery/p/FeatureFlags.svg)](https://www.powershellgallery.com/packages/FeatureFlags/)
[![FeatureFlags](https://img.shields.io/powershellgallery/v/FeatureFlags.svg)](https://www.powershellgallery.com/packages/FeatureFlags/)

Просмотреть файл

@ -1,74 +0,0 @@
trigger:
branches:
include:
- master
paths:
exclude:
- README.md
- examples/*
- .gitignore
schedules:
- cron: "0 0 * * *"
displayName: Daily CI build
always: true
branches:
include:
- master
jobs:
- job: PowerShellCore
strategy:
matrix:
linux:
imageName: 'ubuntu-18.04'
mac:
imageName: 'macos-10.15'
windows:
imageName: 'windows-2019'
pool:
vmImage: $(imageName)
steps:
- task: PowerShell@2
displayName: Restore
inputs:
filePath: 'tools/restore.ps1'
pwsh: true
- task: PowerShell@2
displayName: Test
inputs:
filePath: 'tools/run-tests.ps1'
pwsh: true
- task: PublishTestResults@2
inputs:
testResultsFormat: "NUnit"
testResultsFiles: "test/results.xml"
- job: WindowsPowershell
pool:
vmImage: 'vs2017-win2016'
steps:
- task: PowerShell@2
displayName: Restore
inputs:
filePath: 'tools/restore.ps1'
- task: PowerShell@2
displayName: Test
inputs:
filePath: 'tools/run-tests.ps1'
- task: PublishTestResults@2
inputs:
testResultsFormat: "NUnit"
testResultsFiles: "test/results.xml"
- task: PowerShell@2
displayName: TestManifest
inputs:
targetType: inline
script: 'Test-ModuleManifest -Path ./FeatureFlags.psd1'

Просмотреть файл

@ -4,18 +4,20 @@
https://github.com/pester/Pester/wiki/Installation-and-Update.
After updating, run the Invoke-Pester cmdlet from the project directory.
#>
$ModuleName = "FeatureFlags"
Get-Module $ModuleName | Remove-Module -Force
Import-Module $PSScriptRoot\test-functions.psm1
BeforeAll {
$ModuleName = "FeatureFlags"
Get-Module $ModuleName | Remove-Module -Force
Import-Module $PSScriptRoot\test-functions.psm1
$VerbosePreference = "Continue"
$Module = Import-Module $PSScriptRoot\..\${ModuleName}.psd1 -Force -PassThru
if ($null -eq $Module) {
Write-Error "Could not import $ModuleName"
exit 1
$VerbosePreference = "Continue"
$Module = Import-Module $PSScriptRoot\..\${ModuleName}.psd1 -Force -PassThru
if ($null -eq $Module) {
Write-Error "Could not import $ModuleName"
exit 1
}
Write-Host "Done."
Write-Host $Module.ExportedCommands.Values.Name
}
Write-Host "Done."
Write-Host $Module.ExportedCommands.Values.Name
Describe 'Confirm-FeatureFlagConfig' {
Context 'Validation of invalid configuration' {
@ -270,22 +272,24 @@ Describe 'Get-FeatureFlagConfigFromFile' {
Describe 'Test-FeatureFlag' {
Context 'allowlist condition' {
Context 'Simple allowlist configuration' {
$serializedConfig = @"
{
"stages": {
"all": [
{"allowlist": [".*"]}
]
},
"features": {
"well-tested": {
"stages": ["all"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"all": [
{"allowlist": [".*"]}
]
},
"features": {
"well-tested": {
"stages": ["all"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
It 'Rejects non-existing features' {
Test-FeatureFlag "feature1" "Storage/master" $config | Should -Be $false
@ -297,25 +301,27 @@ Describe 'Test-FeatureFlag' {
}
Context 'Chained allowlist configuration' {
$serializedConfig = @"
{
"stages": {
"test-repo-and-branch": [
{"allowlist": [
"storage1/.*",
"storage2/dev-branch"
]}
]
},
"features": {
"experimental-feature": {
"stages": ["test-repo-and-branch"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"test-repo-and-branch": [
{"allowlist": [
"storage1/.*",
"storage2/dev-branch"
]}
]
},
"features": {
"experimental-feature": {
"stages": ["test-repo-and-branch"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
It 'Returns true if the regex matches' {
Test-FeatureFlag "experimental-feature" "storage1/master" $config | Should -Be $true
@ -330,22 +336,24 @@ Describe 'Test-FeatureFlag' {
Context 'denylist condition' {
Context 'Reject-all configuration' {
$serializedConfig = @"
{
"stages": {
"none": [
{"denylist": [".*"]}
]
},
"features": {
"disabled": {
"stages": ["none"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"none": [
{"denylist": [".*"]}
]
},
"features": {
"disabled": {
"stages": ["none"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
It 'Rejects everything' {
Test-FeatureFlag "disabled" "Storage/master" $config | Should -Be $false
@ -355,23 +363,25 @@ Describe 'Test-FeatureFlag' {
}
Context 'Reject single-value configuration' {
$serializedConfig = @"
{
"stages": {
"all-except-important": [
{"denylist": ["^important$"]}
]
},
"features": {
"some-feature":
{
"stages": ["all-except-important"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"all-except-important": [
{"denylist": ["^important$"]}
]
},
"features": {
"some-feature":
{
"stages": ["all-except-important"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
# Given that the regex is ^important$, only the exact string "important" will match the denylist.
It 'Allows the flag if the predicate does not match exactly' {
@ -387,22 +397,24 @@ Describe 'Test-FeatureFlag' {
}
Context 'Reject multiple-value configuration' {
$serializedConfig = @"
{
"stages": {
"all-except-important": [
{"denylist": ["storage-important/master", "storage-important2/master"]}
]
},
"features": {
"some-feature": {
"stages": ["all-except-important"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"all-except-important": [
{"denylist": ["storage-important/master", "storage-important2/master"]}
]
},
"features": {
"some-feature": {
"stages": ["all-except-important"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
It 'Allows predicates not matching the denylist' {
Test-FeatureFlag "some-feature" "storage1/master" $config | Should -Be $true
@ -418,23 +430,25 @@ Describe 'Test-FeatureFlag' {
}
Context 'Mixed allowlist/denylist configuration' {
$serializedConfig = @"
{
"stages": {
"all-storage-important": [
{"allowlist": ["storage.*"]},
{"denylist": ["storage-important/master", "storage-important2/master"]}
]
},
"features": {
"some-feature": {
"stages": ["all-storage-important"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"all-storage-important": [
{"allowlist": ["storage.*"]},
{"denylist": ["storage-important/master", "storage-important2/master"]}
]
},
"features": {
"some-feature": {
"stages": ["all-storage-important"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
It 'Rejects storage important / important2 master branches' {
Test-FeatureFlag "some-feature" "storage-important/master" $config | Should -Be $false
@ -454,34 +468,36 @@ Describe 'Test-FeatureFlag' {
}
Context 'Probability condition' {
$serializedConfig = @"
{
"stages": {
"all": [
{"probability": 1}
],
"none": [
{"probability": 0}
],
"10percent": [
{"probability": 0.1}
]
},
"features": {
"well-tested": {
"stages": ["all"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"all": [
{"probability": 1}
],
"none": [
{"probability": 0}
],
"10percent": [
{"probability": 0.1}
]
},
"not-launched": {
"stages": ["none"]
},
"10pc-feature": {
"stages": ["10percent"]
"features": {
"well-tested": {
"stages": ["all"]
},
"not-launched": {
"stages": ["none"]
},
"10pc-feature": {
"stages": ["10percent"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
It 'Always allows with probability 1' {
Test-FeatureFlag "well-tested" "storage-important/master" $config | Should -Be $true
@ -523,25 +539,27 @@ Describe 'Test-FeatureFlag' {
}
Context 'Complex allowlist + denylist + probability configuration' {
$serializedConfig = @"
{
"stages": {
"all-storage-important-50pc": [
{"allowlist": ["storage.*"]},
{"denylist": ["storage-important/master", "storage-important2/master"]},
{"probability": 0.5}
]
},
"features": {
"some-feature": {
"stages": ["all-storage-important-50pc"]
BeforeAll {
$serializedConfig = @"
{
"stages": {
"all-storage-important-50pc": [
{"allowlist": ["storage.*"]},
{"denylist": ["storage-important/master", "storage-important2/master"]},
{"probability": 0.5}
]
},
"features": {
"some-feature": {
"stages": ["all-storage-important-50pc"]
}
}
}
}
"@
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
}
It 'Rejects storage important / important2 master branches' {
Test-FeatureFlag "some-feature" "storage-important/master" $config | Should -Be $false
@ -571,10 +589,12 @@ Describe 'Test-FeatureFlag' {
Describe 'Get-EvaluatedFeatureFlags' -Tag Features {
Context 'Verify evaluation of all feature flags' {
$serializedConfig = Get-Content -Raw "$PSScriptRoot\multiple-stages-features.json"
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig;
Mock New-Item -ModuleName FeatureFlags {}
BeforeAll {
$serializedConfig = Get-Content -Raw "$PSScriptRoot\multiple-stages-features.json"
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig;
Mock New-Item -ModuleName FeatureFlags {}
}
It 'Returns expected feature flags' {
$expected = @{ "filetracker"=$true; "newestfeature"=$true; "testfeature"=$false }
@ -587,20 +607,22 @@ Describe 'Get-EvaluatedFeatureFlags' -Tag Features {
Describe 'Out-EvaluatedFeaturesFiles' -Tag Features {
Context 'Verify output file content' {
$global:featuresJsonContent = New-Object 'System.Collections.ArrayList()'
$global:featuresIniContent = New-Object 'System.Collections.ArrayList()'
$global:featuresEnvConfigContent = New-Object 'System.Collections.ArrayList()'
BeforeAll {
$global:featuresJsonContent = New-Object 'System.Collections.ArrayList()'
$global:featuresIniContent = New-Object 'System.Collections.ArrayList()'
$global:featuresEnvConfigContent = New-Object 'System.Collections.ArrayList()'
$serializedConfig = Get-Content -Raw "$PSScriptRoot\multiple-stages-features.json"
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
$serializedConfig = Get-Content -Raw "$PSScriptRoot\multiple-stages-features.json"
Confirm-FeatureFlagConfig $serializedConfig
$config = ConvertFrom-Json $serializedConfig
Mock -ModuleName FeatureFlags New-Item {}
Mock -ModuleName $ModuleName Test-Path { Write-Output $true }
Mock -ModuleName $ModuleName Remove-Item {}
Mock -ModuleName $ModuleName Out-File { ${global:featuresJsonContent}.Add($InputObject) } -ParameterFilter { $FilePath.EndsWith("features.json") }
Mock -ModuleName $ModuleName Add-Content { ${global:featuresIniContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.ini") }
Mock -ModuleName $ModuleName Add-Content { ${global:featuresEnvConfigContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.env.config") }
Mock -ModuleName FeatureFlags New-Item {}
Mock -ModuleName $ModuleName Test-Path { Write-Output $true }
Mock -ModuleName $ModuleName Remove-Item {}
Mock -ModuleName $ModuleName Out-File { ${global:featuresJsonContent}.Add($InputObject) } -ParameterFilter { $FilePath.EndsWith("features.json") }
Mock -ModuleName $ModuleName Add-Content { ${global:featuresIniContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.ini") }
Mock -ModuleName $ModuleName Add-Content { ${global:featuresEnvConfigContent}.Add($Value) } -ParameterFilter { $Path.EndsWith("features.env.config") }
}
It 'Honors denylist' {
$features = Get-EvaluatedFeatureFlags -predicate "important" -config $config
@ -627,5 +649,7 @@ Describe 'Out-EvaluatedFeaturesFiles' -Tag Features {
}
}
Remove-Module $ModuleName
Remove-Module test-functions
AfterAll {
Remove-Module $ModuleName
Remove-Module test-functions
}

Просмотреть файл

@ -22,7 +22,7 @@ Function Test-StringArrays
if ($null -eq $Actual)
{
$Expected | Should Be $null
$Expected | Should -Be $null
return
}
@ -38,11 +38,11 @@ Function Test-StringArrays
Write-Host "Expected: $Expected" -ForegroundColor Red
}
$Actual.Count | Should Be $Expected.Count
$Actual.Count | Should -Be $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++)
{
$Actual[$i] | Should Be $Expected[$i]
$Actual[$i] | Should -Be $Expected[$i]
}
}
@ -68,7 +68,7 @@ Function Test-ObjectArrays
if ($Actual -eq $null)
{
$Expected | Should Be $null
$Expected | Should -Be $null
return
}
@ -84,7 +84,7 @@ Function Test-ObjectArrays
Write-Host "Expected: $Expected" -ForegroundColor Red
}
$Actual.Count | Should Be $Expected.Count
$Actual.Count | Should -Be $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++)
{
@ -114,13 +114,13 @@ Function Test-Hashtables
if ($Expected -eq $null)
{
$Actual | Should Be $null
$Actual | Should -Be $null
return
}
if ($Actual -eq $null)
{
$Expected | Should Be $null
$Expected | Should -Be $null
return
}
@ -129,7 +129,7 @@ Function Test-Hashtables
# Redundant, but tells Pester we tested something
# If the counts don't match, continue with the comparison so we contain
# find out what's missing in the test error log
$Actual.Count | Should Be $Expected.Count
$Actual.Count | Should -Be $Expected.Count
return
}
@ -141,11 +141,11 @@ Function Test-Hashtables
if ($null -eq $actualProperty.Value)
{
$actualProperty.Value | Should Be $expectedValue
$actualProperty.Value | Should -Be $expectedValue
}
else
{
$actualProperty.Value.GetType() | Should Be $expectedValue.GetType()
$actualProperty.Value.GetType() | Should -Be $expectedValue.GetType()
if ($expectedValue.GetType().FullName -eq 'System.Collections.Hashtable')
{
@ -164,12 +164,12 @@ Function Test-Hashtables
else
{
# Just assert their lengths for now
$actualProperty.Value.Count | Should Be $expectedValue.Count
$actualProperty.Value.Count | Should -Be $expectedValue.Count
}
}
else
{
$actualProperty.Value | Should Be $expectedValue
$actualProperty.Value | Should -Be $expectedValue
}
}
}
@ -187,11 +187,11 @@ Function Test-Hashtables
if ($null -eq $expectedProperty.Value)
{
$actualValue | Should Be $expectedProperty.Value
$actualValue | Should -Be $expectedProperty.Value
}
else
{
$actualValue.GetType() | Should Be $expectedProperty.Value.GetType()
$actualValue.GetType() | Should -Be $expectedProperty.Value.GetType()
if ($expectedProperty.Value.GetType().FullName -eq 'System.Collections.Hashtable')
{
@ -210,12 +210,12 @@ Function Test-Hashtables
else
{
# Just assert their lengths for now
$actualValue.Count | Should Be $expectedProperty.Value.Count
$actualValue.Count | Should -Be $expectedProperty.Value.Count
}
}
else
{
$actualValue | Should Be $expectedProperty.Value
$actualValue | Should -Be $expectedProperty.Value
}
}
}

Просмотреть файл

@ -1,17 +1,27 @@
# Mostly for use of CI/CD. Install Pester and run tests.
param (
# Set to true to install Pester 5.1.0, regardless of whether a Pester version
# is present in the environment.
[switch] $InstallPester = $false
)
$parentDir = Split-Path -Parent (Split-Path -Parent $PSCommandPath)
$testDir = Join-Path $parentDir -ChildPath "test"
# Debug info.
$PSVersionTable | Out-String
Write-Host "Checking for Pester > 4.0.0..."
$pester = Get-Module -ListAvailable | Where-Object {$_.Name -eq "Pester" -and $_.Version -gt '4.0.0'}
if ($pester.Count -eq 0) {
Write-Host "Cannot find the Pester module. Installing it."
Install-Module Pester -Force -Scope CurrentUser -RequiredVersion 4.10.1
} else {
Write-Host "Found Pester version $($pester.Version)."
# List Pester versions.
$pesterVersions = Get-Module -ListAvailable | Where-Object {$_.Name -eq "Pester" }
$pesterVersions | % { Write-Host $_.Name $_.Version }
if ($pesterVersions.Count -eq 0) {
Write-Warning "No Pester found, will install Pester 5.1.0"
$InstallPester = $true
}
if ($InstallPester) {
Install-Module Pester -Force -Scope CurrentUser -RequiredVersion 5.1.0
}
$FailedTests = Invoke-Pester $testDir -EnableExit -OutputFile "test/results.xml" -OutputFormat "NUnitXML" -CodeCoverage "$parentDir/FeatureFlags.psm1"