44# See the LICENSE file in the project root for more information. #
55# ################################################################################
66
7- # This job processes code coverage reports generated during test runs,
8- # merges them, generates code coverage reports, publishes them to the
9- # pipeline, and uploads them to CodeCov.
7+ # This job processes code coverage reports generated during test runs, merges
8+ # them, generates code coverage reports, publishes them to the pipeline, and
9+ # uploads them to CodeCov.
1010
1111parameters :
12-
12+
1313 # True to include debug steps.
1414 - name : debug
1515 type : boolean
1616 default : false
1717
18- # The pool image to use.
19- - name : image
20- type : string
21-
22- # The agent pool name.
23- - name : pool
24- type : string
25-
26- # Array of target frameworks to process code coverage for:
27- #
28- # e.g. [net462, net8.0, net9.0]
29- #
30- - name : targetFrameworks
31- type : object
32-
3318 # True to upload code coverage results to CodeCov.
3419 - name : upload
3520 type : boolean
3621
3722jobs :
38- - job : CodeCoverage
23+ - job : publish_code_coverage
3924 displayName : Publish Code Coverage
4025
4126 pool :
42- name : ${{ parameters.pool }}
43- ${{ if eq(parameters.pool, 'Azure Pipelines') }} :
44- vmImage : ${{ parameters.image }}
45- ${{ else }} :
46- demands :
47- - imageOverride -equals ${{ parameters.image }}
27+ name : Azure Pipelines
28+ vmImage : ubuntu-latest
4829
4930 variables :
50- netFxDir : $(Build.SourcesDirectory)\coverageNetFx
51- netCoreDir : $(Build.SourcesDirectory)\coverageNetCore
31+ # Use a temp directory that is cleaned up after each job runs. This helps
32+ # avoid disk space issues on pooled agents that may run many jobs before
33+ # being retired.
34+ - name : workingDir
35+ value : $(Agent.TempDirectory)/coverage
5236
5337 steps :
5438
5539 - ${{if eq(parameters.debug, true)}} :
56- - pwsh : Get-Volume
40+ - script : df -h
5741 displayName : ' [Debug] Show Disk Usage'
5842
59- - pwsh : ' Get-ChildItem env: | Sort-Object Name '
43+ - script : env | sort
6044 displayName : ' [Debug] List Environment Variables'
6145
6246 # Install the .NET SDK.
@@ -65,150 +49,103 @@ jobs:
6549 debug : ${{ parameters.debug }}
6650
6751 # Install additional dotnet tools.
68- - pwsh : |
69- dotnet tool install --global dotnet-coverage
70- dotnet tool install --global dotnet-reportgenerator-globaltool
71- displayName: Install dotnet tools
72-
73- - pwsh : |
74- Write-Host "Removing leftover coverage files from previous runs..."
75- Remove-Item $(netFxDir) -Recurse -Force -ErrorAction SilentlyContinue
76- Remove-Item $(netCoreDir) -Recurse -Force -ErrorAction SilentlyContinue
77-
78- Write-Host "Removing leftover merged XML files from previous runs..."
79- Remove-Item coverageNetFxXml -Recurse -Force -ErrorAction SilentlyContinue
80- Remove-Item coverageNetCoreXml -Recurse -Force -ErrorAction SilentlyContinue
81-
82- Write-Host "Removing leftover reports from previous runs..."
83- Remove-Item coveragereportNetFx -Recurse -Force -ErrorAction SilentlyContinue
84- Remove-Item coveragereportNetCore -Recurse -Force -ErrorAction SilentlyContinue
85- Remove-Item coveragereportAddOns -Recurse -Force -ErrorAction SilentlyContinue
86-
87- - ${{ each targetFramework in parameters.targetFrameworks }} :
88- - task : DownloadPipelineArtifact@2
89- displayName : ' Download Coverage Reports [${{ targetFramework }}]'
90- inputs :
91- itemPattern : ' **\${{ targetFramework }}*'
92- ${{ if startsWith(targetFramework, 'net4') }} :
93- targetPath : $(netFxDir)
94- ${{ else }} :
95- targetPath : $(netCoreDir)
52+ #
53+ # We must work around a bug in the dotnet CLI that prevents tool installs
54+ # when multiple project/solution files are present in the working
55+ # directory:
56+ #
57+ # https://github.com/dotnet/sdk/issues/9623
58+ #
59+ # Our repo root (the default working directory for tasks) contains more
60+ # than one project file. However, we must also obey the NuGet.config in
61+ # the repo root to ensure that tools are installed from the correct feeds.
62+ # We accomplish this by copying the NuGet.config to a temp dir and then
63+ # instructing the dotnet CLI tasks to run from that directory. They will
64+ # still install the tools globally for subsequent tasks to use.
65+ #
66+ - script : cp "$(Build.SourcesDirectory)/NuGet.config" "$(Agent.TempDirectory)/"
67+ displayName : Copy NuGet.config for tool installs
68+
69+ - task : DotNetCoreCLI@2
70+ displayName : Install dotnet-coverage tool
71+ inputs :
72+ command : custom
73+ custom : tool
74+ workingDirectory : $(Agent.TempDirectory)
75+ arguments : install --global dotnet-coverage --version 18.3.2
9676
9777 - ${{if eq(parameters.debug, true)}} :
98- - pwsh : Get-Volume
78+ - script : df -h
9979 displayName : ' [Debug] Show Disk Usage'
10080
101- - pwsh : Get-ChildItem $(netFxDir) -Recurse -File -Filter *.coverage
102- displayName : ' [Debug] List coverageNetFx files'
103-
104- - pwsh : Get-ChildItem $(netCoreDir) -Recurse -File -Filter *.coverage
105- displayName : ' [Debug] List coverageNetCore files'
106-
107- - pwsh : |
108- function MergeFiles {
109- param(
110- [string]$InputDirectoryPath,
111- [string]$OutputDirectoryName
112- )
113-
114- $files = Get-ChildItem $InputDirectoryPath -Recurse -File -Filter *.coverage
115-
116- # echo $files
117- mkdir $OutputDirectoryName
118- $counter=0
119-
120- $toProcess = @()
121-
122- foreach ($file in $files) {
123- $toProcess += @{
124- File = $file.FullName
125- OutputFile = "$OutputDirectoryName\$counter.coveragexml"
126- }
127-
128- $counter++
129- }
130-
131- $jobs = @()
132- foreach ($file in $toProcess){
133- $jobs += Start-ThreadJob -ScriptBlock {
134- $params = $using:file
135- & dotnet-coverage merge $($params.File) --output $($params.OutputFile) --output-format xml
136- }
137- }
138-
139- Write-Host "Merging started..."
140- Wait-Job -Job $jobs
141-
142- foreach ($job in $jobs) {
143- Receive-Job -Job $job -Wait -AutoRemoveJob
144- }
145- }
146-
147- MergeFiles -InputDirectoryPath "$(netFxDir)" -OutputDirectoryName "coverageNetFxXml"
148- MergeFiles -InputDirectoryPath "$(netCoreDir)" -OutputDirectoryName "coverageNetCoreXml"
149-
150- Write-Host "Removing original coverage files..."
151- Remove-Item $(netFxDir) -Recurse -Force -ErrorAction SilentlyContinue
152- Remove-Item $(netCoreDir) -Recurse -Force -ErrorAction SilentlyContinue
153- displayName: Convert coverage files to xml
81+ # Download all of the coverage reports from the test jobs.
82+ #
83+ # These artifacts contain the .coverage files generated during test runs,
84+ # along with a bunch of other files that we ignore. We only download the
85+ # .coverage files.
86+ #
87+ - task : DownloadPipelineArtifact@2
88+ displayName : Download Coverage Reports
89+ inputs :
90+ # All of our coverage report artifact names start with 'net'.
91+ itemPattern : ' **/net*/**/*.coverage'
92+ targetPath : $(workingDir)/originals
15493
15594 - ${{if eq(parameters.debug, true)}} :
156- - pwsh : Get-Volume
95+ - script : df -h
15796 displayName : ' [Debug] Show Disk Usage'
15897
159- - pwsh : |
160- dir coverageNetFxXml\
161- dir coverageNetCoreXml\
162- displayName: '[Debug] List converted files'
163-
164- - pwsh : |
165- $jobs = @()
166- $jobs += Start-ThreadJob -ScriptBlock {
167- & 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.*"
168- }
98+ - script : ls -alFR "$(workingDir)/originals"
99+ displayName : ' [Debug] List coverage files'
169100
170- $jobs += Start-ThreadJob -ScriptBlock {
171- & 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.*"
172- }
173-
174- $jobs += Start-ThreadJob -ScriptBlock {
175- & 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.*"
176- }
177-
178- Write-Host "Running ReportGenerator..."
179- Wait-Job -Job $jobs
180-
181- foreach ($job in $jobs) {
182- Receive-Job -Job $job -Wait -AutoRemoveJob
183- }
184-
185- Write-Host "Removing merged XML files..."
186- Remove-Item coverageNetFxXml -Recurse -Force -ErrorAction SilentlyContinue
187- Remove-Item coverageNetCoreXml -Recurse -Force -ErrorAction SilentlyContinue
188- displayName: Run ReportGenerator
101+ # Merge them all into a single Cobertura XML file.
102+ - script : >-
103+ dotnet-coverage merge "$(workingDir)/originals/**/*.coverage"
104+ --output "$(workingDir)/merge/Cobertura.xml"
105+ --output-format cobertura
106+ --log-file "$(workingDir)/merge/merge.log"
107+ --log-level Verbose
108+ displayName: Merge coverage files to Cobertura XML
189109
190110 - ${{if eq(parameters.debug, true)}} :
191- - pwsh : Get-Volume
111+ - script : df -h
192112 displayName : ' [Debug] Show Disk Usage'
193113
114+ # Publish the Cobertura XML coverage file as a pipeline artifact for
115+ # debugging purposes.
116+ - task : PublishPipelineArtifact@1
117+ displayName : Publish Cobertura XML Artifact
118+ inputs :
119+ targetPath : $(workingDir)/merge
120+ artifact : Cobertura Merge Results
121+
122+ # Publish the Cobertura reports to the pipeline to be viewed in the Azure
123+ # DevOps pipeline run UI.
194124 - task : PublishCodeCoverageResults@2
195125 displayName : Publish code coverage results
196126 inputs :
197- summaryFileLocation : ' *\ Cobertura.xml'
127+ summaryFileLocation : $(workingDir)/merge/ Cobertura.xml
198128
129+ # Publish the Cobertura reports to CodeCov, if desired.
199130 - ${{if eq(parameters.upload, true)}} :
200- - pwsh : |
201- #download Codecov CLI
202- $ProgressPreference = 'SilentlyContinue'
203- Invoke-WebRequest -Uri https://cli.codecov.io/latest/windows/codecov.exe -Outfile codecov.exe
204-
205- ./codecov --verbose upload-process --fail-on-error -t $(CODECOV_TOKEN) -f "coveragereportNetFx\Cobertura.xml" -F netfx
206- ./codecov --verbose upload-process --fail-on-error -t $(CODECOV_TOKEN) -f "coveragereportNetCore\Cobertura.xml" -F netcore
207- ./codecov --verbose upload-process --fail-on-error -t $(CODECOV_TOKEN) -f "coveragereportAddOns\Cobertura.xml" -F addons
131+ # Download the CodeCov CLI
132+ - script : |
133+ curl -o "$(workingDir)/codecov" https://cli.codecov.io/latest/linux/codecov
134+ chmod +x "$(workingDir)/codecov"
135+ displayName: Download CodeCov CLI
136+
137+ # Upload the report.
138+ #
139+ # We use the pipeline name as the "flag" to help distinguish reports
140+ # uploaded from different pipelines.
141+ #
142+ - script : >-
143+ $(workingDir)/codecov
144+ --verbose
145+ upload-process
146+ --fail-on-error
147+ -t $(CODECOV_TOKEN)
148+ --disable-search
149+ -f "$(workingDir)/merge/Cobertura.xml"
150+ -F $(Build.DefinitionName)
208151 displayName: Upload to CodeCov
209-
210- - pwsh : |
211- Write-Host "Removing reports..."
212- Remove-Item coveragereportNetFx -Recurse -Force -ErrorAction SilentlyContinue
213- Remove-Item coveragereportNetCore -Recurse -Force -ErrorAction SilentlyContinue
214- Remove-Item coveragereportAddOns -Recurse -Force -ErrorAction SilentlyContinue
0 commit comments