Azure DevOps pipelines
Azure DevOps uses YAML files to specify pipelines. We have two key types of pipelines:
- Continuous integration builds, with SonarCloud analysis
- Deployment builds
.Net builds
PR builds
We want a build to run whenever a new PR is created. This should be configured to be a required check. Doing so, ensures that a PR won’t be approved and merged when the build (or tests) fail.
The first part of the YAML therefore is to target the trigger conditions:
trigger: none
pr:
branches:
include:
- main
The trigger:none is to ensure it won’t run other than during a PR. The PR section will ensure the pipeline runs on any PR which targets the main branch (if you primary branch is still called master then you need to ensure it is that which is targetted).
Next, specify the pool and any variables:
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildConfiguration: 'Release'
buildPlatform: 'Any CPU'
sonarOrganization: '<SONAR ORG>'
sonarProjectKey: '<SONAR_PROJECT_KEY>'
sonarProjectName: '<SONAR_PROJECT_NAME>'
githubFeed: '<GITHUB_FEED_URL>' # Optional, only if adding GitHub nuget feed
githubUser: '<GITHUB_USER>' # Optional, only if adding GitHub nuget feed
steps:
- checkout: self
fetchDepth: 0
SonarCloud recommends the checkout step to ensure it has all the information it needs available. On a large/old project this checkout might be slow so pay attention to build times.
If the project requires access to private Nuget packages then that is setup next:
- task: NuGetAuthenticate@1
inputs:
nuGetServiceConnections: 'Synetec Nuget Feed'
- task: NuGetToolInstaller@1
- script: |
echo "Adding GitHub package source"
nuget sources Add -Name "GitHub" -Source $(GITHUB_FEED) -Username $(GITHUB_USER) -Password $(GitHubPackagesApiKey)
displayName: 'Add Synetec Package feed'
- script: |
nuget restore **/*.sln
Note: When running a restore, with an external feed this seemed the best way of doing it. It might work with the normal NuGetCommand after adding the private package source, though the ‘feedsToUse’ parameter needs to be resolved. This might come from using a nuget.config in the project.
If a private source isn’t required then use the built-in task:
steps:
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '**/*.sln'
feedsToUse: 'select'
The remaining steps are:
- Prepare SonarCloud
- Build
- Test
- Analyze with SonarCloud
- Publish with SonarCloud
A full YAML file with all the required steps is below:
trigger: none
pr:
branches:
include:
- main
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildConfiguration: 'Release'
buildPlatform: 'Any CPU'
sonarOrganization: '<SONAR ORG>'
sonarProjectKey: '<SONAR_PROJECT_KEY>'
sonarProjectName: '<SONAR_PROJECT_NAME>'
steps:
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '**/*.sln'
feedsToUse: 'select'
- task: SonarCloudPrepare@1
inputs:
SonarCloud: 'SonarCloud'
organization: '$(sonarOrganization)'
scannerMode: 'MSBuild'
projectKey: '$(sonarProjectKey)'
projectName: '$(sonarProjectName)'
extraProperties: |
sonar.cs.dotcover.reportsPaths=dotCover.Output.html
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest@2
inputs:
testSelector: 'testAssemblies'
testAssemblyVer2: |
**\bin\**\*test.dll
**\bin\**\*tests.dll
searchFolder: '$(System.DefaultWorkingDirectory)'
codeCoverageEnabled: true
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: SonarCloudAnalyze@1
inputs:
jdkversion: 'JAVA_HOME_21_X64'
- task: SonarCloudPublish@1
inputs:
pollingTimeoutSec: '300'