Skip to content

Commit 2f20fca

Browse files
authored
[6.1] Fix flaky code coverage job: pin tool versions and harden thread-job handling (#4611)
1 parent 9fb1b81 commit 2f20fca

1 file changed

Lines changed: 73 additions & 21 deletions

File tree

eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ jobs:
4949
variables:
5050
netFxDir: $(Build.SourcesDirectory)\coverageNetFx
5151
netCoreDir: $(Build.SourcesDirectory)\coverageNetCore
52+
# The first-run telemetry notice is written to stderr, which PowerShell
53+
# surfaces as an error record and fails the step.
54+
DOTNET_COVERAGE_TELEMETRY_OPTOUT: 1
5255

5356
steps:
5457
- ${{if eq(parameters.debug, true)}}:
@@ -68,10 +71,30 @@ jobs:
6871
debug: ${{ parameters.debug }}
6972

7073
# Install additional dotnet tools.
71-
- pwsh: |
72-
dotnet tool install --global dotnet-coverage
73-
dotnet tool install --global dotnet-reportgenerator-globaltool
74-
displayName: Install dotnet tools
74+
#
75+
# The dotnet CLI tool tasks need a working directory that contains at most
76+
# one project file, but they must still obey the repo's NuGet.config. We
77+
# copy the NuGet.config to a temp dir and run the tasks from there; the
78+
# tools are still installed globally for subsequent steps to use.
79+
#
80+
- pwsh: Copy-Item "$(Build.SourcesDirectory)\NuGet.config" "$(Agent.TempDirectory)\"
81+
displayName: Copy NuGet.config for tool installs
82+
83+
- task: DotNetCoreCLI@2
84+
displayName: Install dotnet-coverage tool
85+
inputs:
86+
command: custom
87+
custom: tool
88+
workingDirectory: $(Agent.TempDirectory)
89+
arguments: install --global dotnet-coverage --version 18.9.0
90+
91+
- task: DotNetCoreCLI@2
92+
displayName: Install dotnet-reportgenerator tool
93+
inputs:
94+
command: custom
95+
custom: tool
96+
workingDirectory: $(Agent.TempDirectory)
97+
arguments: install --global dotnet-reportgenerator-globaltool --version 5.5.11
7598

7699
- ${{ each targetFramework in parameters.targetFrameworks }}:
77100
- task: DownloadPipelineArtifact@2
@@ -120,20 +143,33 @@ jobs:
120143
foreach ($file in $toProcess){
121144
$jobs += Start-ThreadJob -ScriptBlock {
122145
$params = $using:file
123-
& dotnet-coverage merge $($params.File) --output $($params.OutputFile) --output-format xml
146+
# Redirect stderr into stdout so informational tool output
147+
# isn't surfaced as a PowerShell error record.
148+
$out = & dotnet-coverage merge $($params.File) --output $($params.OutputFile) --output-format xml 2>&1
149+
$exitCode = $LASTEXITCODE
150+
$out | Out-String | Write-Output
151+
if ($exitCode -ne 0) {
152+
throw "dotnet-coverage merge failed with exit code $exitCode for $($params.File)."
153+
}
124154
}
125155
}
126156
127157
if ($jobs.Count -eq 0) {
128-
Write-Error "No .coverage files found in $InputDirectoryPath. Cannot merge."
129-
exit 1
158+
throw "No .coverage files found in $InputDirectoryPath. Cannot merge."
130159
}
131160
132161
Write-Host "Merging started..."
133-
Wait-Job -Job $jobs
162+
$null = Wait-Job -Job $jobs
163+
164+
$failed = @($jobs | Where-Object { $_.State -ne 'Completed' })
134165
135166
foreach ($job in $jobs) {
136-
Receive-Job -Job $job -Wait -AutoRemoveJob
167+
Receive-Job -Job $job -ErrorAction Continue 2>&1 | Out-String | Write-Host
168+
Remove-Job -Job $job -Force
169+
}
170+
171+
if ($failed.Count -gt 0) {
172+
throw "$($failed.Count) of $($jobs.Count) merge jobs failed for $InputDirectoryPath."
137173
}
138174
}
139175
@@ -155,24 +191,40 @@ jobs:
155191
displayName: '[Debug] List converted files'
156192
157193
- pwsh: |
158-
$jobs = @()
159-
$jobs += Start-ThreadJob -ScriptBlock {
160-
& reportgenerator "-reports:coverageNetFxXml\*.coveragexml" "-targetdir:coveragereportNetFx" "-reporttypes:Cobertura;" "-assemblyfilters:+microsoft.data.sqlclient.dll" "-sourcedirs:$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\netfx\src;$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\src" "-classfilters:+Microsoft.Data.*"
161-
}
162-
163-
$jobs += Start-ThreadJob -ScriptBlock {
164-
& reportgenerator "-reports:coverageNetCoreXml\*.coveragexml" "-targetdir:coveragereportNetCore" "-reporttypes:Cobertura;" "-assemblyfilters:+microsoft.data.sqlclient.dll" "-sourcedirs:$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\netcore\src;$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\src" "-classfilters:+Microsoft.Data.*"
194+
function StartReportGenerator {
195+
param([string[]]$Arguments)
196+
197+
Start-ThreadJob -ArgumentList @(,$Arguments) -ScriptBlock {
198+
param([string[]]$reportGeneratorArgs)
199+
200+
# Redirect stderr into stdout so informational tool output isn't
201+
# surfaced as a PowerShell error record.
202+
$out = & reportgenerator @reportGeneratorArgs 2>&1
203+
$exitCode = $LASTEXITCODE
204+
$out | Out-String | Write-Output
205+
if ($exitCode -ne 0) {
206+
throw "reportgenerator failed with exit code $exitCode."
207+
}
208+
}
165209
}
166210
167-
$jobs += Start-ThreadJob -ScriptBlock {
168-
& reportgenerator "-reports:coverageNetCoreXml\*.coveragexml" "-targetdir:coveragereportAddOns" "-reporttypes:Cobertura;" "-assemblyfilters:+microsoft.data.sqlclient.alwaysencrypted.azurekeyvaultprovider.dll" "-sourcedirs:$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\add-ons\AzureKeyVaultProvider" "-classfilters:+Microsoft.Data.*"
169-
}
211+
$jobs = @()
212+
$jobs += StartReportGenerator @("-reports:coverageNetFxXml\*.coveragexml", "-targetdir:coveragereportNetFx", "-reporttypes:Cobertura;", "-assemblyfilters:+microsoft.data.sqlclient.dll", "-sourcedirs:$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\netfx\src;$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\src", "-classfilters:+Microsoft.Data.*")
213+
$jobs += StartReportGenerator @("-reports:coverageNetCoreXml\*.coveragexml", "-targetdir:coveragereportNetCore", "-reporttypes:Cobertura;", "-assemblyfilters:+microsoft.data.sqlclient.dll", "-sourcedirs:$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\netcore\src;$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\src", "-classfilters:+Microsoft.Data.*")
214+
$jobs += StartReportGenerator @("-reports:coverageNetCoreXml\*.coveragexml", "-targetdir:coveragereportAddOns", "-reporttypes:Cobertura;", "-assemblyfilters:+microsoft.data.sqlclient.alwaysencrypted.azurekeyvaultprovider.dll", "-sourcedirs:$(Build.SourcesDirectory)\src\Microsoft.Data.SqlClient\add-ons\AzureKeyVaultProvider", "-classfilters:+Microsoft.Data.*")
170215
171216
Write-Host "Running ReportGenerator..."
172-
Wait-Job -Job $jobs
217+
$null = Wait-Job -Job $jobs
218+
219+
$failed = @($jobs | Where-Object { $_.State -ne 'Completed' })
173220
174221
foreach ($job in $jobs) {
175-
Receive-Job -Job $job -Wait -AutoRemoveJob
222+
Receive-Job -Job $job -ErrorAction Continue 2>&1 | Out-String | Write-Host
223+
Remove-Job -Job $job -Force
224+
}
225+
226+
if ($failed.Count -gt 0) {
227+
throw "$($failed.Count) of $($jobs.Count) ReportGenerator jobs failed."
176228
}
177229
178230
Write-Host "Removing merged XML files..."

0 commit comments

Comments
 (0)