YAML Build Pipeline For .NET Core with Azure DevOps

YAML Build Pipeline For .NET Core with Azure DevOps
Forget the visual designer of Azure DevOps, in this tutorial I show how to create builds that are driven by YAML code that sits in your repository. Versioned build configurations and the ability to make build changes right from your code editor are just a few of the benefits of code-driven builds that no one wants to miss.

In this article you will learn:

  • What’s YAML and why it is useful
  • How to create YAML-driven builds
  • How to build, test, publish .Net Core projects on Azure DevOps

What is YAML?

YAML is a data serialization standard that is human readable. Its minimalistic syntax made it the format of choice for configuring various services, frameworks, and products like Docker, Kubernetes, and more. In the context of Azure DevOps it is used to drive the ours builds.

There are a couple of important points about YAML:

  • It’s a strict superset of JSON
  • Data is stored as key-value pairs (or maps)
  • Indentation is important

I think the best way to fully appreciate YAML is to compare it to JSON. Let’s take a look at how we can represent information about a blog post using JSON vs YAML:

{
	"title": "Learning YAML",
	"tags": [
		"YAML",
		"Tutorial"
	],
	"author": {
		"name": "Milan",
		"email": "milan@mail.com"
	}
}
title: Learning YAML
tags:
    - YAML
    - Tutorial
author:
    name: Milan
    email: milan@mail.com

YAML’s lack of braces and quotes definitely translates into improved readability. Once you get into more complex structures, JSON becomes a tangled mess while you would still easily scan and understand the YAML counterpart. If you would like to go through the full YAML syntax feel free to checkout Learn YAML in Y minutes.

Creating DevOps Build

Let’s now head over to Azure DevOps and create a build. I have prepared a sample repository to easily get us started - devops-yaml-netcore-build.

Make sure that you have an Azure DevOps account. You can quickly grab one from here. Once you have access, let’s create a project. An Azure DevOps project provides a ton of functionality like issue tracking, collaboration, source control, etc. but for the purposes of this article we are going to focus on the build pipeline functionality.

Forking Sample Repository (optional)

Currently the supported repository types for YAML-based builds in Azure DevOps are Azure Repos Git, GitHub, and GitHub Enterprise Server. To utilize the sample repository, it must be part of your Github account so it must be forked to your own account.

To do that, navigate to the repository, click on Fork (upper right) and choose your account.

Fork sample repository

Now you have a copy of this repository in your own account so that it can be connected to the build we are going to create.

Creating a project

Navigate to your DevOps dashboard - mine is https://mnankov.visualstudio.com/ - and click on “Create project” in the upper right corner and fill in the required information.

Create project button Create project dialog

Creating a build

Once the project is created, lets add a build using the menu on the left to navigate to the builds page. Navigate to pipelines Since this is our first build, click on “New pipeline” to launch the workflow for creating a new build pipeline.

The first step is to choose a source for the build. Let’s choose Github, authenticate, and select the devops-yaml-netcore-build we have forked earlier.

Choose source

Proceed to the next screen, where a template for our build must be select. This is where we tell DevOps to use a YAML file provided by us.

Choose YAML

Almost there. The only thing left to do is tell DevOps which YAML file is to be used. Select YAML file Many tutorials will advise to add your YAML file in the root folder but since we want to have better organization I have placed the file in the build folder. Let’s choose build/azure-pipelines.yml.

Select YAML file dialog

Confirm the file selection. After that we are ready to Save and Queue the build using the button at the top. This will manually trigger a build process which will execute all standard tasks for a .Net Core project - restore dependencies, build the code, test the code, and finally publish the code to a zip.

As far as the DevOps portal is concerned, we are done with it. The build process is now controlled by azure-pipelines.yml and we can proceed with our favorite editor of choice. To change the build process, we simply need to modify the YAML file.

Let’s not take a look at the build file available in the sample repository and break it apart.

Building .Net Core Using YAML

In order to create a build pipeline using YAML, your build file must adhere to the YAML schema for DevOps. On high-level each pipeline is composed of jobs. Each job is composed of steps where a step can be a script, a task, or a external template reference. DevOps Pipeline For simpler pipelines, the jobs container can be omitted and we can only provide steps.

As usual the best way to get started is to take a look at the real thing. Here is how a build pipeline for the sample .Net Core project looks like:

pool:
  vmImage: 'Ubuntu 16.04'

trigger:
  batch: true
  branches:
    include: ['master']
  
variables:
  buildConfiguration: 'Release'
  workingDirectory: '$(Build.SourcesDirectory)/src'

steps:
- script: dotnet restore ./All.sln
  displayName: Restore
  workingDirectory: $(workingDirectory)

- script: dotnet build ./All.sln --configuration $(buildConfiguration)
  displayName: Build
  workingDirectory: $(workingDirectory)

- script: dotnet test ./Tests.sln --configuration $(buildConfiguration) --logger trx
  displayName: Test
  workingDirectory: $(workingDirectory)

- task: PublishTestResults@2
  displayName: Publish Test Results
  condition: succeededOrFailed()
  inputs:
    testRunner: VSTest
    testResultsFiles: '**/*.trx'

- script: dotnet publish ./MyProject.Web --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)
  displayName: Publish
  workingDirectory: $(workingDirectory)
  
- task: PublishBuildArtifacts@1

Let’s break this apart.

The pool specifies which pool is to be used for jobs on this pipeline. We can think about pools in the context of Azure DevOps as containers for build agents. In our specific case, we simply want to make sure that we use build agents with a particular image (Ubuntu 16.04).

The trigger specifies what branches will trigger a continuous integration build. If trigger is not specified, all branches will trigger a build.

Variables logically allow us to shared data in our pipelines. We can reference variables using the following syntax: $(myVariableName). There are many built-in variables that come in handy so make sure to check the documentation for the full list.

And finally steps - the building blocks of the YAML pipelines. Our sample uses the script step which runs a script using cmd.exe on Windows and Bash on other platforms. Let’s take a closer look at an individual step:

- script: dotnet restore ./All.sln
  displayName: Restore
  workingDirectory: $(workingDirectory)

This one will execute the .Net CLI to restore dependencies in the specified solution file. The script step is actually a shortcut for using the command line task. The following is equivalent:

- task: CmdLine@2
  displayName: Restore
  inputs:
    script: dotnet restore ./All.sln 
    workingDirectory: $(workingDirectory)

The build pipeline is primarily composed of script steps which execute the usual .Net Cli commands to restore, build, and test the code. The only more peculiar tasks are PublishTestResults@2 and PublishBuildArtifacts@1. The first one will publish the test results to the Azure DevOps portal so that you can have detailed reports on the test runs. The latter one is responsible for publishing the build artifacts to the portal so that they can be downloaded.

So there you have it - we have created a fully automated build pipeline for a .Net Core project using YAML.

Wrap-up and Next Steps

DevOps Pipeline
I hope that this article is a good stepping stone for automating your builds using YAML and Azure DevOps. Be sure to check out the Github repository where you can find two variations of the YAML files - one which is using the DotNetCoreCLI@2 task and another one which shows how you can make the original build file more concise if you fancy it.

As always feel free to leave any comments or suggestions.

Published 7 Mar 2019

Building software and sharing knowledge.
Milan Nankov on Twitter