Skip to content

VSTS - YAML Build Definitions

2017-11-28

Using YAML to define your builds

In a previous post I talked about high-level and somewhat generic approaches to getting your CICD pipeline to be responsible for more than just your builds but also you infrastructure deployment.

Silvrback blog image

Now I'd like to walk though a an actual implementation of a build definition as code using a ASP.NET Core application being built on VSTS.

Here is a quick little slug of PowerShell to get a local repo started

$sourceDirectory = "src"
$solutionName = "Solution01"
$appName = "WebApp01"

# Get the default .gitignore from GitHub
Invoke-WebRequest -UseBasicParsing `
-Uri https://raw.githubusercontent.com/github/gitignore/master/VisualStudio.gitignore | `
Select-Object -ExpandProperty Content | `
Out-File .gitignore -Encoding Ascii

#Create a mvc & test project and wire them up to a solution file
dotnet new mvc -o ".\$sourceDirectory\$appName" -n "$appName"
dotnet new xunit -o ".\$sourceDirectory\$appName.Tests" -n "$appName.Tests"
dotnet add ".\$sourceDirectory\$appName.Tests\$appName.Tests.csproj" reference ".\$sourceDirectory\$appName\$appName.csproj"
dotnet new sln -o .\$sourceDirectory\ -n "$solutionName"
dotnet sln ".\$sourceDirectory\$solutionName.sln" add ".\$sourceDirectory\$appName\$appName.csproj"
dotnet sln ".\$sourceDirectory\$solutionName.sln" add ".\$sourceDirectory\$appName.Tests\$appName.Tests.csproj"

# Create a git repo
git init
git add -A
git commit -m "Adds sample application and tests"

# Verify the tests pass
dotnet test ".\$sourceDirectory\$appName.Tests\"

Now that you have a sample project to test with go create a VSTS project for you to build and ultimately deploy with. Once you have a project created push your repo to that projects repository.

Let's get started with some positional significant YAML

Add a file named .vsts-ci.yml to the root of your repo and push it to your remote.

steps:
- powershell: Write-Host "This agent is running PowerShell v$($PSVersionTable.PSVersion.Major)"
  displayName: PowerShell Version Report Script

In your build log you should see something similar in the PowerShell Version Report Script

Silvrback blog image sb_float_center

Outputting the agent's version of PowerShell is not all that interesting. Let's turn our attention to the sample project we built earlier. Let's get a restore and build command added to our build task list for the default phase.

steps:
- task: dotNetCoreCLI@1
  displayName: dotnet restore
  inputs:
    command: restore
    projects: "**/*.csproj"

- task: dotNetCoreCLI@1
  displayName: dotnet build
  inputs:
    command: build
    projects: "src/Solution01.sln"
    arguments: --configuration Release

These steps are using two dotnet core cli tasks to call dotnet restore against all of our csproj files and dotnet build against our solution file for a release configuration so we can get to a "build once and configure everywhere else" state.

The results are happy little green checkmarks - we like these.

Silvrback blog image sb_float_center

Our build summary is showing that two extra steps, dotnet restore and dotnet build showed up for our new addition to our .vsts-ci.yml file

As part of our sample bootstrap script we also added a sample, albeit, useless test (that isn't even an Assert.True(true)) but it still passes. Let's add that task to your YAML file.

steps:
- task: dotNetCoreCLI@1
  displayName: dotnet restore
  inputs:
    command: restore
    projects: "**/*.csproj"

- task: dotNetCoreCLI@1
  displayName: dotnet build
  inputs:
    command: build
    projects: "src/Solution01.sln"
    arguments: --configuration Release

- task: dotNetCoreCLI@1
  displayName: dotnet test
  inputs:
    command: test
    projects: "**/*Tests/*.csproj"
    arguments: --configuration Release --logger "trx;LogFileName=$(Agent.TempDirectory)\\Results.trx" --no-build

In our steps section we added the dotnet test task command which will wildcard down through the src directory and looking for project directories with Tests in the directory path. Again using a Release configuration and a --no-build flag so that we are preserving our binaries from our build step. We are also instructing the rest running to publish the test results to a temp directory in the well-known xml based trx file format.

Silvrback blog image sb_float_center

Now our build summary is showing an extra step of dotnet test appeared as a result of adding to the .vsts-ci.yml file.

Let's keep pressing forward. Test are fine to run but you need to have a report aggregator pick them up so we get the pass fail status of the build as they related to the tests and for historical context of our tests for the project.

steps:
- task: dotNetCoreCLI@1
  displayName: dotnet restore
  inputs:
    command: restore
    projects: "**/*.csproj"

- task: dotNetCoreCLI@1
  displayName: dotnet build
  inputs:
    command: build
    projects: "src/Solution01.sln"
    arguments: --configuration Release

- task: dotNetCoreCLI@1
  displayName: dotnet test
  inputs:
    command: test
    projects: "**/*Tests/*.csproj"
    arguments: --configuration Release --logger "trx;LogFileName=$(Agent.TempDirectory)\\Results.trx" --no-build

- task: PublishTestResults@1
  displayName: Publish Test Results
  inputs:
    testRunner: VSTest
    testResultsFiles: "$(Agent.TempDirectory)\\*.trx"

VSTS can pickup our unit test runners test results so that we can report directly against them on the current build but also to for build history.

Silvrback blog image sb_float_center

More checkmarks appearing this time for our Publish Test Results item in the list.

The last couple of steps to add are to run dotnet publish and to add the VSTS publish task so that the artifacts are available further down the pipeline.

steps:
- task: dotNetCoreCLI@1
  displayName: dotnet restore
  inputs:
    command: restore
    projects: "**/*.csproj"

- task: dotNetCoreCLI@1
  displayName: dotnet build
  inputs:
    command: build
    projects: "src/Solution01.sln"
    arguments: --configuration Release

- task: dotNetCoreCLI@1
  displayName: dotnet test
  inputs:
    command: test
    projects: "**/*Tests/*.csproj"
    arguments: --configuration Release --logger "trx;LogFileName=$(Agent.TempDirectory)\\Results.trx" --no-build

- task: PublishTestResults@1
  displayName: Publish Test Results
  inputs:
    testRunner: VSTest
    testResultsFiles: "$(Agent.TempDirectory)\\*.trx"

- task: dotNetCoreCLI@1
  displayName: dotnet publish
  inputs:
    command: publish
    arguments: --configuration Release --output $(Build.ArtifactStagingDirectory)
    zipAfterPublish: true

- task: publishBuildArtifacts@1
  displayName: Publish Artifacts
  inputs:
    PathtoPublish: $(Build.ArtifactStagingDirectory)
    ArtifactName: drop
    ArtifactType: Container

Add the similarly named items of dotnet publish and Publish Artifacts to the .vsts-ci.yml file we can see where the dotnet cli is collecting all the files needed to create the ASP.NET Core application into a specific directly and handing that off to the VSTS task of 'sharing' the designated directory so that the contents are available further downstream.

Silvrback blog image sb_float_center

With this final change we can see a partially complete CI pipeline here that goes through the a dotnet restore, dotnet build, dotnet test, Publish Test Results, dotnet publish and Publishing Artifacts. The missing part that we haven't added it deploying our infrastructure that would be used for solely for the CI process for integration tests. This would be in the form of the Deploy Resource Groups task. We will touch on that in the next post.

Silvrback blog image sb_float_center

Hopefully as this feature matures out of preview we'll see a way to visualize what that these code based pipelines look like in VSTS. Developer time validation / linting component is going to be a necessary as the rinse repeat workflow of changing an item, committing and pushing it rather time consuming. I could also imagine features that would work hand in hand with the dotnet templating cli so that as you bring in projects to your solution your YAML build script is created or updated accordingly.