I have a .net core project with pipeline (YAML, multi-stage) pipeline set up in Azure DevOps. We have a bunch of unit tests that we execute at pipeline run time - everything is fine.
However - we would like to dig more deeper into our code coverage. So we have configured our task like this
- task: DotNetCoreCLI#2
displayName: Run UnitTests
enabled: true
inputs:
command: test
projects: '**/PM.UnitTests.csproj'
arguments: '--configuration $(buildConfiguration) --collect "Code coverage"'
The result is this
We can now see this in pipeline results:
This .coverage file can be downloaded and analyzed in e.g. Visual Studio.
However - we would really like to be able to see the results directly in Azure Pipelines. We have a Typescript project where we do this. The result is this:
Sadly, it is not at all apparent to me how to apply this to a .net core project.
Q1: Is it possible to have the same experience for .net core projects ... and how?
Additionally - we would like to be able to apply filtering on which parts of the code is used to calculate the code coverage percentage.
Q2: Is it correctly understood that this is done using a .runsettings file?
Thank you :-)
/Jesper
For reference, this is my complete YAML pipeline in my test .net core solution. The solution is super simple - a .net core class library and a .net core test class library
pool:
vmImage: vs2017-win2016
steps:
- task: DotNetCoreCLI#2
inputs:
command: 'restore'
feedsToUse: 'select'
vstsFeed: 'd12c137f-dac4-4ea7-bc39-59bd2b784537'
- task: DotNetCoreCLI#2
displayName: Build
inputs:
projects: '**/*.csproj'
arguments: --configuration $(BuildConfiguration) --no-restore
- task: DotNetCoreCLI#2
displayName: Run UnitTests
inputs:
command: test
projects: '**/Test.Coverage/Test.Coverage.csproj'
arguments: '--configuration $(BuildConfiguration) --collect:"XPlat Code Coverage" '
- script: 'dotnet tool install -g dotnet-reportgenerator-globaltool '
displayName: 'Install dotnet-reportgenerator-globaltool'
- script: 'reportgenerator -reports:$(Agent.TempDirectory)/**/coverage.cobertura.xml -targetdir:$(build.sourcesdirectory) -reporttypes:"Cobertura"'
displayName: 'Executes reportgenerator'
- task: PublishCodeCoverageResults#1
displayName: 'Publish code coverage from $(build.sourcesdirectory)/Cobertura.xml'
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(build.sourcesdirectory)/Cobertura.xml'
You could use the dotnet-reportgenerator-globaltool package to generate the HTML Code Coverage Report.
Here is an example:
- task: DotNetCoreCLI#2
displayName: Test
inputs:
command: test
projects: '$(Parameters.TestProjects)'
arguments: '--configuration $(BuildConfiguration) --collect:"XPlat Code Coverage" '
- script: 'dotnet tool install -g dotnet-reportgenerator-globaltool '
displayName: 'Command Line Script'
- script: 'reportgenerator -reports:$(Agent.TempDirectory)/**/coverage.cobertura.xml -targetdir:$(build.sourcesdirectory) -reporttypes:"Cobertura"'
displayName: 'Command Line Script'
- task: PublishCodeCoverageResults#1
displayName: 'Publish code coverage from $(build.sourcesdirectory)/Cobertura.xml'
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(build.sourcesdirectory)/Cobertura.xml'
Result:
What you're looking for is a tool that can generate a report based on the coverage information. You're looking for a ReportGenerator.
ReportGenerator converts coverage reports generated by coverlet, OpenCover, dotCover, Visual Studio, NCover, Cobertura, JaCoCo, Clover, gcov or lcov into human readable reports in various formats.
There are quite a few examples out there working with the dotnet-reportgenerator-globaltool for .NET Core projects.
You might find this Learn -> Usage page helpful.
Coverlet might also be an interesting addition to your toolbelt.
Related
I have a dotnet core project (API, not web) which is linked to an Azure Repo Git. We build the project within Visual Studio (professional 2022) and publish it to a folder locally.
I'm then taking that folder copy and pasting it under C:\inetpub\wwwroot\coinia-api-new\ in my Windows Server. This API is an IIS falling under default website. The API then works as expected and yields result when we hit it.
If I perform the same task using Azure Build Pipeline and then publish the artifact to the Windows server using Azure Release pipeline, at the same path (C:\inetpub\wwwroot\coinia-api-new), the build and release succeed but the API doesn't work and gives error 500.
I'm using a standard dotnet core build template for building the solution. I have also tried using MSBuild and VsBuild.
And standard IIS Deploy task for the deployment using release pipeline.
The builds succeed and reproduces the artifact but after deploying, the API still doesn't work.
Below is the yaml for build pipeline:
# Agent Queue 'Azure Pipelines' was used with unrecognized Agent Specification, vmImage property must be specified to determine image - https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#software
variables:
- name: BuildParameters.RestoreBuildProjects
value: '**/COINIA.CORE.API.csproj'
- name: BuildParameters.TestProjects
value: ''
name: $(date:yyyyMMdd)$(rev:.r)
jobs:
- job: Job_1
displayName: Agent job 1
pool:
name: Azure Pipelines
steps:
- checkout: self
fetchDepth: 1
- task: DotNetCoreCLI#2
displayName: Restore
inputs:
command: restore
projects: $(BuildParameters.RestoreBuildProjects)
- task: DotNetCoreCLI#2
displayName: Build
inputs:
projects: $(BuildParameters.RestoreBuildProjects)
arguments: --configuration $(BuildConfiguration)
- task: VSBuild#1
displayName: Build solution COINIA.CORE.API/COINIA.CORE.API.csproj
enabled: False
inputs:
solution: COINIA.CORE.API/COINIA.CORE.API.csproj
- task: DotNetCoreCLI#2
displayName: Test
enabled: False
inputs:
command: test
projects: $(BuildParameters.TestProjects)
arguments: --configuration $(BuildConfiguration)
- task: DotNetCoreCLI#2
displayName: Publish
inputs:
command: publish
publishWebProjects: false
projects: $(BuildParameters.RestoreBuildProjects)
arguments: --configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)
zipAfterPublish: false
- task: PublishBuildArtifacts#1
displayName: Publish Artifact
condition: succeededOrFailed()
inputs:
PathtoPublish: $(build.artifactstagingdirectory)
TargetPath: '\\my\share\$(Build.DefinitionName)\$(Build.BuildNumber)'
...
And here is my release pipeline snippets:
I have a .NET Core solution with two projects (proj1 and proj2). When I run a build in Azure DevOps, I use the DotNetCoreCLI#2 command like this:
- task: DotNetCoreCLI#2
displayName: 'Build output'
inputs:
command: 'build'
projects: '**/*.csproj'
arguments: '--output $(System.DefaultWorkingDirectory)/publish_output'
As is, all the artifacts from both builds end up in the publish_output directory. What I'd like is for there to be a proj1 and proj2 subdirectory created under publish_output and the artifacts from the projects placed appropriately.
Is this possible? I could not find a "currently being build project name" variable to append to the output directory.
Thank you Simply Ged. Posting your suggestions as answer to help other community members.
Add modifyOutputPath as a input as below and check
Here is the code
# Publish projects to specified folder.
- task: DotNetCoreCLI#2
displayName: 'dotnet publish'
inputs:
command: 'publish'
publishWebProjects: false
projects: '**/*.csproj'
arguments: '-o $(Build.ArtifactStagingDirectory)/Output'
zipAfterPublish: true
modifyOutputPath: true
Follow the Publish Projects for further instructions.
My code for the azure pipeline
I have Problems using the azure pipeline, which will automatically pack my libraries on azure devops git and push it as a nugetpackage to artifacts.
I get the error ##[error]No packages matched the search pattern.
at dotnet push
Until that step everything works. I want to pack it as a nuget so i can use it in other projects.
Its a API Client i wrote for my own API.
Try using the artifact path from artifact staging directory
- task: DotNetCoreCLI#2
displayName: 'dotnet build'
inputs:
command: 'build'
arguments: '--configuration $(buildConfiguration)'
projects: '**/*.csproj'
- task: DotNetCoreCLI#2
displayName: "dotnet pack"
inputs:
command: 'pack'
arguments: '--configuration $(buildConfiguration)'
packagesToPack: '**/*.csproj'
nobuild: true
versioningScheme: 'off'
- task: NuGetCommand#2
displayName: 'nuget push'
inputs:
command: 'push'
feedsToUse: 'select'
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg'
nuGetFeedType: 'internal'
publishVstsFeed: '<Name of Your Feed>'
versioningScheme: 'off'
allowPackageConflicts: true
https://medium.com/#gstvribs/how-to-use-and-deploy-azure-devops-artifacts-on-azure-pipelines-with-dotnet-dockerized-8cebd724f752
Please add to dotnet pack for instance setting like this to your inputs:
packDirectory: "$(Build.ArtifactStagingDirectory)/packages"
And then to dotnet push setting like this:
packagesToPush: '$(Build.ArtifactStagingDirectory)/packages/*.*nupkg'
In your approach it simply try to find packages in defualt folder but you didn't put packages there.
Consider the following project structure:
MyProject.Api.a
MyProject.Api.b
MyProject.Data, referenced by a and b
I've set up a pipeline in Azure devOps that performs a restore, build and publish like this:
jobs:
- job: api-a
steps:
- task: DotNetCoreCLI#2
displayName: Restore
inputs:
command: restore
projects: "MyProject.Api.a.csproj"
- task: DotNetCoreCLI#2
displayName: Build
inputs:
command: build
projects: "MyProject.Api.a.csproj"
arguments: "--configuration $(buildConfiguration)"
- task: DotNetCoreCLI#2
displayName: Publish
inputs:
command: publish
projects: "MyProject.Api.a.csproj"
publishWebProjects: false
arguments: "--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)/project-api-a-publish"
- task: PublishPipelineArtifact#1
displayName: Publish release Artifact
inputs:
targetPath: "$(Build.ArtifactStagingDirectory)/project-api-a-publish"
artifactName: "a-publish"
- job: api-b
steps:
- task: DotNetCoreCLI#2
displayName: Restore
inputs:
command: restore
projects: "MyProject.Api.b.csproj"
- task: DotNetCoreCLI#2
displayName: Build
inputs:
command: build
projects: "MyProject.Api.b.csproj"
arguments: "--configuration $(buildConfiguration)"
- task: DotNetCoreCLI#2
displayName: Publish
inputs:
command: publish
projects: "MyProject.Api.b.csproj"
publishWebProjects: false
arguments: "--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)/project-api-b-publish"
- task: PublishPipelineArtifact#1
displayName: Publish release Artifact
inputs:
targetPath: "$(Build.ArtifactStagingDirectory)/project-api-b-publish"
artifactName: "b-publish"
problem is that MyProject.Data is not buildable from source, it needs an external tool to run first that generates some C# classes. Before this step, the project won't be able to build.
So I added this:
- task: DotNetCoreCLI#2
displayName: "Restore tools"
inputs:
workingDirectory: "MyProject.Data"
command: custom
custom: tool
arguments: restore --interactive --configfile ../NuGet.config
- task: DotNetCoreCLI#2
displayName: my-codegen-tool
inputs:
workingDirectory: "MyProject.Data"
command: custom
custom: tool
arguments: run my-codegen-tool
This all works, but the codegen tool needs to run on each API project job I'm running, making my build quite slow.
I was hoping there would be some way to only run the codegen tool once, and then all API projects could use the binaries from the data project where the files were generated?
Ideally, I would have to be able to prebuild the data project in a separate job, publish the dll's as an artifact, and then use those dll's in my subsequent API builds. I'm guessing this would be possible with dotnet build --no-dependencies, but that would implicate I need to build everything else separately as well, which is undesirable from a maintainability point of view.
You can try to combine multiple jobs into one job. First generate the required C # classes through the first two tasks, and then build with wildcards (e.g. **/*.csproj for all .csproj files in all subfolders) in the dotnet build task.
You can consider removing restore tasks, because dotnet restore is run implicitly in dotnet build.
This is stated here : You don't have to run dotnet restore because it's run implicitly by all commands that require a restore to occur, such as dotnet new, dotnet build, dotnet run, dotnet test, dotnet publish, and dotnet pack. To disable implicit restore, use the --no-restore option.
- job:
steps:
- task: DotNetCoreCLI#2
displayName: "Restore tools"
inputs:
workingDirectory: "MyProject.Data"
command: custom
custom: tool
arguments: restore --interactive --configfile ../NuGet.config
- task: DotNetCoreCLI#2
displayName: my-codegen-tool
inputs:
workingDirectory: "MyProject.Data"
command: custom
custom: tool
arguments: run my-codegen-tool
- task: DotNetCoreCLI#2
displayName: Build
inputs:
command: build
projects: "**/*.csproj"
arguments: "--configuration $(buildConfiguration)"
Is it possible to specify the target framework for a ClassLibrary project using Azure Devops Build Pipelines and the DotNetCoreCLI#2 task? Or do we have to revert to using a script and manually calling the dotnet publish command?
A snippet from my pipeline YAML
variables:
buildConfiguration: 'Debug'
steps:
- task: DotNetCoreCLI#2
inputs:
command: 'publish'
publishWebProjects: false # Required when command == Publish
projects: 'TestProject/Test/Test.csproj'
configuration: '$(BuildConfiguration)'
And from my .csproj:
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net45</TargetFrameworks>
</PropertyGroup>
I'm using the documentation here for the DotNetCoreCLI#2 task but it's not always great.
EDIT: I should add that at the moment the Build completely fails because:
The 'Publish' target is not supported without specifying a target framework.
The current project targets multiple frameworks, please specify the framework
for the published application.
The DotNetCoreCLI#2 task could be extended with the arguments property, on which you could specify the framework (as well as the build configuration):
steps:
- task: DotNetCoreCLI#2
inputs:
command: 'publish'
publishWebProjects: false # Required when command == Publish
projects: 'TestProject/Test/Test.csproj'
arguments: '--configuration $(BuildConfiguration) --framework netcoreapp2.1'