I've got a Azure DevOps pipeline running a SonarCloud analysis on a .NET solution. My pipeline looks like this:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
displayName: 'Install .Net SDK 7'
inputs:
packageType: 'sdk'
version: '7.x'
- task: NuGetToolInstaller@1
displayName: 'Set up NuGet'
- task: NuGetCommand@2
displayName: 'Restore solution'
inputs:
restoreSolution: '$(solution)'
- task: SonarCloudPrepare@1
displayName: 'Prepare SonarCloud analysis configuration'
inputs:
# inputs
- task: DotNetCoreCLI@2
displayName: Build solution
inputs:
command: build
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI@2
displayName: Run tests and collect coverage
inputs:
command: test
arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage"'
projects: '**/*.Test.csproj'
publishTestResults: true
# Install code coverage tool, then run it
- script: |
dotnet tool install -g dotnet-reportgenerator-globaltool
reportgenerator -reports:$(Agent.WorkFolder)/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/CoverageResults -reporttypes:'HtmlInline_AzurePipelines;Cobertura'
displayName: Create code coverage report
# Publish the combined code coverage to the pipeline
- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage report'
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(Build.SourcesDirectory)/CoverageResults/Cobertura.xml'
reportDirectory: '$(Build.SourcesDirectory)/CoverageResults'
- task: SonarCloudAnalyze@1
displayName: 'Run SonarCloud analysis'
- task: SonarCloudPublish@1
displayName: 'Publish results on build summary'
This pipeline correctly builds and publishes code coverage to Azure DevOps, but in the logs I can see that the JaCoCo sensor is being used to detect coverage. I've created the following sonar-project.properties file to remedy this, as the documentation suggests:
sonar.cs.opencover.reportsPaths=**/CoverageResults/Cobertura.xml
sonar.coverageReportPaths=**/CoverageResults/Cobertura.xml
I've also tried all other properties to get sonar to use coverlet/opencover to import the xml file but it continues to use JaCoCo:
INFO: Sensor C# Analysis Log [csharp] (done) | time=27ms
INFO: Sensor C# Properties [csharp]
INFO: Sensor C# Properties [csharp] (done) | time=1ms
INFO: Sensor JaCoCo XML Report Importer [jacoco]
INFO: 'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations: target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml,build/reports/jacoco/test/jacocoTestReport.xml
INFO: No report imported, no coverage information will be imported by JaCoCo XML Report Importer
INFO: Sensor JaCoCo XML Report Importer [jacoco] (done) | time=4ms
My question is basically: what am I doing wrong?
In the logs it says 'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations
, so I've also tried adding that property to see if it does register as defined, but the logs keep saying 'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations
. This almost certainly confirms that the property file is not read/found/used. How can I verify/remedy this?
EDIT:
It does use OpenCover to read files! While analyzing projects it gives the jacoco error on every project, but at the end of the analysis the following logs can be found:
10:52:04.687 WARN: Could not import coverage report '/home/vsts/work/_temp/_fv-az362-389_2024-01-12_10_50_50/In/fv-az362-389/coverage.cobertura.xml' because 'Missing root element <CoverageSession> in /home/vsts/work/_temp/_fv-az362-389_2024-01-12_10_50_50/In/fv-az362-389/coverage.cobertura.xml at line 2'. Troubleshooting guide: https://community.sonarsource.com/t/37151
10:52:04.688 DEBUG: The current user dir is '/home/vsts/work/1'.
10:52:04.688 INFO: Parsing the OpenCover report /home/vsts/work/_temp/36304613-f19c-4f5c-a98f-7312cff254c4/coverage.cobertura.xml
10:52:04.689 WARN: Could not import coverage report '/home/vsts/work/_temp/36304613-f19c-4f5c-a98f-7312cff254c4/coverage.cobertura.xml' because 'Missing root element <CoverageSession> in /home/vsts/work/_temp/36304613-f19c-4f5c-a98f-7312cff254c4/coverage.cobertura.xml at line 2'. Troubleshooting guide: https://community.sonarsource.com/t/37151
10:52:04.689 DEBUG: The current user dir is '/home/vsts/work/1'.
10:52:04.689 INFO: Parsing the OpenCover report /home/vsts/work/_temp/20fc0c63-9b9c-4596-9dd8-e9895e44a7ed/coverage.cobertura.xml
10:52:04.690 WARN: Could not import coverage report '/home/vsts/work/_temp/20fc0c63-9b9c-4596-9dd8-e9895e44a7ed/coverage.cobertura.xml' because 'Missing root element <CoverageSession> in /home/vsts/work/_temp/20fc0c63-9b9c-4596-9dd8-e9895e44a7ed/coverage.cobertura.xml at line 2'. Troubleshooting guide: https://community.sonarsource.com/t/37151
10:52:04.690 DEBUG: The current user dir is '/home/vsts/work/1'.
10:52:04.690 INFO: Parsing the OpenCover report /home/vsts/work/_temp/45be504f-f9ed-444a-be8a-f504da051afe/coverage.cobertura.xml
10:52:04.691 WARN: Could not import coverage report '/home/vsts/work/_temp/45be504f-f9ed-444a-be8a-f504da051afe/coverage.cobertura.xml' because 'Missing root element <CoverageSession> in /home/vsts/work/_temp/45be504f-f9ed-444a-be8a-f504da051afe/coverage.cobertura.xml at line 2'. Troubleshooting guide: https://community.sonarsource.com/t/37151
10:52:04.691 DEBUG: The current user dir is '/home/vsts/work/1'.
10:52:04.691 INFO: Parsing the OpenCover report /home/vsts/work/_temp/_fv-az362-389_2024-01-12_10_50_42/In/fv-az362-389/coverage.cobertura.xml
10:52:04.692 WARN: Could not import coverage report '/home/vsts/work/_temp/_fv-az362-389_2024-01-12_10_50_42/In/fv-az362-389/coverage.cobertura.xml' because 'Missing root element <CoverageSession> in /home/vsts/work/_temp/_fv-az362-389_2024-01-12_10_50_42/In/fv-az362-389/coverage.cobertura.xml at line 2'. Troubleshooting guide: https://community.sonarsource.com/t/37151
10:52:04.692 DEBUG: Analyzing coverage after aggregate found '0' coverage files.
10:52:04.693 DEBUG: The total number of file count statistics is '0'.
It's finding all my coverage reports now, but I'm getting an error. Is it something with my dotnetcli test command that is causing this?
I have found the solution. Cobertura is not compatible with OpenCover anymore. Using
- task: DotNetCoreCLI@2
displayName: Run tests and collect coverage
inputs:
command: test
arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage"'
projects: '**/*.Test.csproj'
publishTestResults: true
results in a Cobertura xml. The file cannot be imported by sonar because Cobertura differs from OpenCover. It seems Sonars documentation on Cobertura is incorrect, since they specify
sonar.cs.opencover.reportsPaths
Comma-delimited list of paths to the coverage reports produced by OpenCover or Coverlet.
And they have multiple examples that are doing this. Turns out, this doesn't work. I have changed my coverage output format to OpenCover and now it works!
- task: SonarCloudPrepare@1
displayName: 'Prepare SonarCloud analysis configuration'
inputs:
#inputs
extraProperties: |
sonar.tests=**/*.Test.csproj
sonar.cs.nunit.reportsPaths=**/*.trx
sonar.cs.opencover.reportsPaths=$(Agent.WorkFolder)/**/coverage.opencover.xml
- task: DotNetCoreCLI@2
displayName: Build solution
inputs:
command: build
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI@2
displayName: Run tests and collect coverage
inputs:
command: test
arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage;Format=opencover"'
projects: '**/*.Test.csproj'
publishTestResults: true
# Install code coverage tool, then run it
- script: |
dotnet tool install -g dotnet-reportgenerator-globaltool
reportgenerator -reports:$(Agent.WorkFolder)/**/coverage.opencover.xml -targetdir:$(Build.SourcesDirectory)/CoverageResults -reporttypes:'HtmlInline_AzurePipelines_Dark;Cobertura'
displayName: Create code coverage summary report
# Publish the combined code coverage to the pipeline
- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage summary report'
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(Build.SourcesDirectory)/CoverageResults/Cobertura.xml'
reportDirectory: '$(Build.SourcesDirectory)/CoverageResults'
- task: SonarCloudAnalyze@1
displayName: 'Run SonarCloud analysis'
- task: SonarCloudPublish@1
displayName: 'Publish results on build summary'