Skip to content

Commit 4049b3a

Browse files
vhvb1989Copilot
authored andcommitted
Enforce minimum code coverage threshold in CI pipeline (Azure#7188)
* Enforce minimum code coverage threshold in CI pipeline Add a coverage gate to the CodeCoverage_Upload stage that fails the build when total statement coverage drops below a configured minimum. - New Test-CodeCoverageThreshold.ps1 script parses 'go tool cover -func' output and fails if coverage is below the threshold - New MinimumCoveragePercent parameter on code-coverage-upload.yml (default 0 = disabled) - Set threshold to 80% in release-cli.yml to validate the mechanism (will be adjusted to the actual floor after verification) Fixes Azure#7187 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix coverage threshold step to use PowerShell task directly The previous approach nested 'pwsh -File' inside a pwsh inline step, which caused $CoverageFile to be treated as a literal string instead of the script parameter. Switch to PowerShell@2 task with targetType: filePath so arguments are passed correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix PowerShell variable interpolation in go tool cover argument Double-quote the '-func=$CoverageFile' argument so PowerShell expands the variable. Without quotes, PowerShell passes the literal string '$CoverageFile' to the go command. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Set workingDirectory to cli/azd for go tool cover module resolution go tool cover -func needs to resolve Go module paths from the coverage file. Run the threshold check from cli/azd/ where go.mod lives, and pass the absolute path to cover.out. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Set threshold to 48% to match current baseline - Add ValidateRange(0,100) on MinimumCoveragePercent parameter - Find total line by 'total:' prefix instead of assuming last line - Match both integer and decimal percentages with invariant culture parsing - Add condition: succeededOrFailed() to PublishCodeCoverageResults so coverage reports are still uploaded when the threshold check fails Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2517925 commit 4049b3a

3 files changed

Lines changed: 82 additions & 0 deletions

File tree

eng/pipelines/release-cli.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ extends:
125125

126126
- template: /eng/pipelines/templates/stages/code-coverage-upload.yml
127127
parameters:
128+
MinimumCoveragePercent: 48
128129
DownloadArtifacts:
129130
- cover-win
130131
- cover-lin

eng/pipelines/templates/stages/code-coverage-upload.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ parameters:
55
- cover-win
66
- cover-lin
77
- cover-mac
8+
- name: MinimumCoveragePercent
9+
type: number
10+
default: 0
811
stages:
912
- stage: CodeCoverage_Upload
1013
condition: and(succeeded(), ne(variables['Skip.LiveTest'], 'true'))
@@ -59,7 +62,18 @@ stages:
5962
}
6063
displayName: Merge code coverage files
6164
65+
- ${{ if gt(parameters.MinimumCoveragePercent, 0) }}:
66+
- task: PowerShell@2
67+
inputs:
68+
pwsh: true
69+
targetType: filePath
70+
filePath: $(Build.SourcesDirectory)/eng/scripts/Test-CodeCoverageThreshold.ps1
71+
arguments: -CoverageFile $(Build.SourcesDirectory)/cover.out -MinimumCoveragePercent ${{ parameters.MinimumCoveragePercent }}
72+
workingDirectory: $(Build.SourcesDirectory)/cli/azd
73+
displayName: Check code coverage threshold
74+
6275
- task: PublishCodeCoverageResults@1
76+
condition: succeededOrFailed()
6377
inputs:
6478
codeCoverageTool: Cobertura
6579
summaryFileLocation: '$(Build.SourcesDirectory)/**/coverage.xml'
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env pwsh
2+
3+
<#
4+
.SYNOPSIS
5+
Checks that code coverage meets a minimum threshold.
6+
7+
.DESCRIPTION
8+
Parses a Go coverage profile using 'go tool cover -func' to extract the
9+
total statement coverage percentage, then fails if it is below the
10+
specified minimum.
11+
12+
.PARAMETER CoverageFile
13+
Path to the Go coverage profile (typically cover.out).
14+
15+
.PARAMETER MinimumCoveragePercent
16+
The minimum acceptable coverage percentage (0-100). The script exits
17+
with a non-zero code when coverage is below this value.
18+
19+
.EXAMPLE
20+
./Test-CodeCoverageThreshold.ps1 -CoverageFile cover.out -MinimumCoveragePercent 48
21+
#>
22+
23+
param(
24+
[Parameter(Mandatory = $true)]
25+
[string]$CoverageFile,
26+
27+
[Parameter(Mandatory = $true)]
28+
[ValidateRange(0, 100)]
29+
[double]$MinimumCoveragePercent
30+
)
31+
32+
$ErrorActionPreference = 'Stop'
33+
34+
if (-not (Test-Path $CoverageFile)) {
35+
throw "Coverage file '$CoverageFile' not found"
36+
}
37+
38+
Write-Host "Checking code coverage threshold (minimum: $MinimumCoveragePercent%)..."
39+
40+
# Use 'go tool cover -func' to get per-function coverage and the total line.
41+
$output = & go tool cover "-func=$CoverageFile"
42+
if ($LASTEXITCODE) {
43+
throw "go tool cover -func failed: $output"
44+
}
45+
46+
# Find the "total:" summary line, which looks like: "total: (statements) 48.3%"
47+
$totalLine = $output | Where-Object { $_ -match '^\s*total:' } | Select-Object -Last 1
48+
49+
if (-not $totalLine) {
50+
throw "Could not find 'total:' line in 'go tool cover -func' output"
51+
}
52+
53+
# Match both integer (100%) and decimal (48.3%) percentages using invariant culture
54+
if ($totalLine -match '(\d+(?:\.\d+)?)%') {
55+
$coveragePercent = [double]::Parse($matches[1], [System.Globalization.CultureInfo]::InvariantCulture)
56+
} else {
57+
throw "Could not parse coverage percentage from: $totalLine"
58+
}
59+
60+
Write-Host "Total statement coverage: $coveragePercent%"
61+
62+
if ($coveragePercent -lt $MinimumCoveragePercent) {
63+
Write-Host "##vso[task.logissue type=error]Code coverage $coveragePercent% is below the minimum threshold of $MinimumCoveragePercent%."
64+
exit 1
65+
}
66+
67+
Write-Host "Coverage $coveragePercent% meets the minimum threshold of $MinimumCoveragePercent%. ✓"

0 commit comments

Comments
 (0)