Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/pipelines/release-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ extends:

- template: /eng/pipelines/templates/stages/code-coverage-upload.yml
parameters:
MinimumCoveragePercent: 48
DownloadArtifacts:
- cover-win
- cover-lin
Expand Down
14 changes: 14 additions & 0 deletions eng/pipelines/templates/stages/code-coverage-upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ parameters:
- cover-win
- cover-lin
- cover-mac
- name: MinimumCoveragePercent
type: number
default: 0
stages:
- stage: CodeCoverage_Upload
condition: and(succeeded(), ne(variables['Skip.LiveTest'], 'true'))
Expand Down Expand Up @@ -59,7 +62,18 @@ stages:
}
displayName: Merge code coverage files

- ${{ if gt(parameters.MinimumCoveragePercent, 0) }}:
- task: PowerShell@2
inputs:
pwsh: true
targetType: filePath
filePath: $(Build.SourcesDirectory)/eng/scripts/Test-CodeCoverageThreshold.ps1
arguments: -CoverageFile $(Build.SourcesDirectory)/cover.out -MinimumCoveragePercent ${{ parameters.MinimumCoveragePercent }}
workingDirectory: $(Build.SourcesDirectory)/cli/azd
displayName: Check code coverage threshold

- task: PublishCodeCoverageResults@1
condition: succeededOrFailed()
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(Build.SourcesDirectory)/**/coverage.xml'
Expand Down
68 changes: 68 additions & 0 deletions eng/scripts/Test-CodeCoverageThreshold.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env pwsh

<#
.SYNOPSIS
Checks that code coverage meets a minimum threshold.

.DESCRIPTION
Parses a Go coverage profile using 'go tool cover -func' to extract the
total statement coverage percentage, then fails if it is below the
specified minimum.

.PARAMETER CoverageFile
Path to the Go coverage profile (typically cover.out).

.PARAMETER MinimumCoveragePercent
The minimum acceptable coverage percentage (0-100). The script exits
with a non-zero code when coverage is below this value.

.EXAMPLE
./Test-CodeCoverageThreshold.ps1 -CoverageFile cover.out -MinimumCoveragePercent 48
#>

param(
[Parameter(Mandatory = $true)]
[string]$CoverageFile,

[Parameter(Mandatory = $true)]
[ValidateRange(0, 100)]
[double]$MinimumCoveragePercent
)

$ErrorActionPreference = 'Stop'

if (-not (Test-Path $CoverageFile)) {
throw "Coverage file '$CoverageFile' not found"
}

Write-Host "Checking code coverage threshold (minimum: $MinimumCoveragePercent%)..."

# Use 'go tool cover -func' to get per-function coverage and the total line.
$output = & go tool cover "-func=$CoverageFile" 2>&1
if ($LASTEXITCODE -ne 0) {
throw "go tool cover -func failed: $output"
}

# Find the "total:" summary line, which looks like: "total: (statements) 48.3%"
$totalLine = $output | Where-Object { $_ -match '^\s*total:' } | Select-Object -Last 1

if (-not $totalLine) {
throw "Could not find 'total:' line in 'go tool cover -func' output"
}

# Match both integer (100%) and decimal (48.3%) percentages using invariant culture
if ($totalLine -match '(\d+(?:\.\d+)?)%') {
$coveragePercent = [double]::Parse($matches[1], [System.Globalization.CultureInfo]::InvariantCulture)
} else {
throw "Could not parse coverage percentage from: $totalLine"
}

Write-Host "Total statement coverage: $coveragePercent%"

if ($coveragePercent -lt $MinimumCoveragePercent) {
Write-Host "##vso[task.logissue type=error]Code coverage $coveragePercent% is below the minimum threshold of $MinimumCoveragePercent%."
Write-Host "##vso[task.complete result=Failed;]Coverage threshold not met."
exit 1
}

Write-Host "Coverage $coveragePercent% meets the minimum threshold of $MinimumCoveragePercent%. ✓"