Install latest node version in Azure Pipelines

I have a build in Azure DevOps that suddenly starts failing on some agents during the build of an angular application. Looking at the log I found that error

You are running version v8.9.4 of Node.js, which is not supported by Angular CLI 8.0+.

Ok, the error is really clear, some developer upgraded Angular version on the project and node version installed in some of the build servers is old. Now the obvious situation is logging in ALL build servers, upgrade node js installation and the build should run on every agent. This is not a perfect solution, especially because because someone can add another build server with an outdated Node Js version and I’m stuck again.

Having some strong pre-requisite on build agent, like a specific version of NodeJs is annoying and needs to be addressed as soon as possible.

In this scenario you have two distinct way to solve the problem. The first solution is adding a custom capabilities to all the agents that have Node 10.9 at least, or even better, mark all agents capable to build Angular 8 with a Angular8 capability. Now you can mark all builds for projects with Angular 8 to demand Angular8 capability, and lets Azure DevOps choose the right agent for you.

Matching Agent Capabilities with Build Demands is a standard way to have your build definition match the correct agent that have all prerequisites preinstalled

But even better is using a Task (still in preview) that is able to install and configure required node version on the agent automatically.

image

Figure 1: Use Node.js ecosystem Task configured

Thanks to this task, you do not need to worry about choosing the right agent, because the task will install required NodeJs version for you automatically. This solution is perfect, because it lessen pre-requisites of build agent, it is the build that will automatically install all prerequisites if missing.

When you start having lots of builds and build agents, it is better not to have too many prerequisites for a build agent, because it will complicate deploy of new agents and can create intermittent failure.

If you can you should be able to preinstall all required prerequisites for the build with build definition, avoiding to require prerequisites on agents.

Happy Azure Devops Pipeline

Converting Existing pipeline to YAML, how to avoid double builds

Actually YAML build is the preferred way to create Azure DevOps Build Pipeline and converting existing build is really simple thanks to the “View YAML” button that can simply convert every existing pipeline in a YAML definition.

image

figure 1: Converting existing Pipeline in YAML is easy with the View YAML button present in editor page.

The usual process is, start a new feature branch to test pipeline conversion to YAML, create the YAML file and a Pipeline based on it, then start testing. Now a problem arise: until the YAML definition is not merged in ANY branch of your Git repository, you should keep the old UI Based Build and the new YAML build togheter.

What happens if a customer calls you because it has a bug in an old version, you create a support branch and then realize that in that branch the YAML build definition is not present. What if the actual YAML script is not valid for that code? The obvious solution is to keep the old build around until you are 100% sure that the build is not needed anymore.

During conversion from legacy build to YAML it is wise to keep the old build around for a while.

This usually means that you start to gradually remove triggers for branches until you merge all the way to master or the last branch, then you leave the definition around without trigger for a little while, finally you delete it.

The real problem is that usually there is a transition phase where you want both the old pipeline definition to run in parallel with the YAML one, but this will create a trigger for both the build at each publish.

SNAGHTML64e597

Figure 2: After a push both build, the old UI Based and the new based on YAML were triggered.

From figure 2 you can understand the problem: each time I push, I have two build that were spinned. Clearly you can start setting up triggers for build to handle this situation, but it is usually tedious.

The very best situation would be, trigger the right build based on the fact that the YAML definition file is present or not.

A viable solution is: abort the standard build if the corresponding YAML Build file is present in the source. This will perfectly work until YAML build file reach the last active branch, after that moment you can disable the trigger on the original Task based build or completely delete the build because all the relevant branches have now the new YAML definition.

To accomplish this, you can simple add a PowerShell Task in the original build, with a script that checks if the YAML file exists and if the test is positive aborts current build. Luckly enough I’ve found a script ready to use: Many tanks for the original author of the script. You can find the original script in GitHub and you can simply take the relevant part putting inside a standard PowerShell task.

SNAGHTMLb575bc

Figure 3: Powershell inline task to simply abort the build.

The script is supposed to work if you have a variable called Token where you place a Personal Access Token with sufficient permission to cancel the build, as explained in the original project on GitHub.

Here is my version of the script

if (Test-Path Assets/Builds/BaseCi.yaml) 
{
    Write-Host "Abort the build because corresponding YAML build file is present"
    $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/builds/$($env:BUILD_BUILDID)?api-version=2.0"
    $pat = "$(token)" 
    $pair = ":${pat}"
    $b  = [System.Text.Encoding]::UTF8.GetBytes($pair)
    $token = [System.Convert]::ToBase64String($b)
    $body = @{ 'status'='Cancelling' } | ConvertTo-Json
    
    $pipeline = Invoke-RestMethod -Uri $url -Method Patch -Body $body -Headers @{
        'Authorization' = "Basic $token";
        'Content-Type' = "application/json"
    }
    Write-Host "Pipeline = $($pipeline)"
}
else
{
   write-host "YAML Build is not present, we can continue"
}

This is everything you need, after this script is added to the original build definition, you can try to queue a build for a branch that has the YAML build definition on it and wait for the execution to be automatically canceled, as you can see in Figure 4:

image

Figure 4: Build cancelled because YAML definition file is present.

With this workaround we still have double builds triggered, but at least when the branch contains the YAML file, the original build definition will imeediately cancel after check-out, because it knows that a corresponding YAML build was triggered. If the YAML file is not present, the build runs just fine.

This is especially useful because it avoid human error, say that a developer manually trigger the old build to create a release or to verify something, if he trigger the old build on a branch that has the new YAML definition, the build will be automatically aborted, so the developer can trigger the right definition.

Gian Maria.

Error publishing .NET core app in Azure Devops YAML Build

Short story, I’ve created a simple YAML build for a .NET core project where one of the task will publish a simple .NET core console application. After running the build I’ve a strange error in the output

No web project was found in the repository. Web projects are identified by presence of either a web.config file or wwwroot folder in the directory.

This is extremely strange, because the project is not a web project, it is a standard console application written for .NET core 2.2 so I do not understand why it is searching a web.config file.

Then I decided to create a standard non YAML build, and when I dropped the task on the build I immediately understood the problem. This happens because dotnet core task with command publish is assuming by default that a web application is going to be published.

image

Figure 1: Default value for the dotnet publish command is to publish Web Project

Since I’ve no Web Project to publish I immediately change  my YAML definition to explicitly set to False the publishWebProjects property.

  - task: DotNetCoreCLI@2
    displayName: .NET Core Publish
    inputs:
      command: publish
      projects: '$(serviceProject)'
      arguments: '--output $(Build.ArtifactStagingDirectory)'
      configuration: $(BuildConfiguration)
      workingDirectory: $(serviceProjectDir)
      publishWebProjects: False
      zipAfterPublish: true

And the build was fixed.

Gian Maria.

Is Manual Release in Azure DevOps useful?

When people creates a release in AzureDevOps, they primarily focus on how to make the release automatic, but to be 100% honest, automation in only one side of the release, and probably not the more useful.

First of all Release is about auditing and understand which version of the software is released where and by whom. In this scenario what is more important is “how I can deploy my software in production”.

image

Figure 1: Simple release composed by two steps.

In Figure 1 I created a simple release with two stages, this will clearly states that to go to production I need to deploy on a Test Machine stage, then Deploy to production. I do not want to give you a full tutorial, MSDN is full of nice example, but if you look at the picture, you can notice small user icons before each stage, that allows you to specify who can approve a release in that stage or if the release should start automatically.

What is more important when you plan the release, is not how you can automate the deployment, but how to structure release flow: stages, people, etc.

As I strongly suggests, even if you do not have any idea on how to automate the release, you MUST have at least a release document, that contains detailed instruction on how to install the software. When you have a release document, you can simply add that document to source control and create a release definition completely manual.

If the release document is included in the source control, you can simply store as result of build artifacts, then it will be automatically downloaded and you can simply create a release like this. In Figure 2 you can see a typical two phase release for stages defined in Figure 1.

image

Figure 2: A manual release uses a copy file to copy artifacts to a well known location then a manual step.

I usually have an agent based phase because I want to copy artifacts data from the agent directory to a well known directory. Agent directory is clumsy, and can be deleted by cleanup so I want my release files to be copied in a folder like c:\release (Figure 3)

image

Figure 3: The only automatic task is copying all the artifacts to c:\release folder

After this copy, I have another phase, this time it is agentless, because it has only a Manual deploy action, that simply specify to the user where to find the instruction for manual deploy.

image

Figure 4: Manual step in the release process

This is important because as you can see in Figure 4 I can instruct the user on where to find release document (it is in source code, the build add to the artifact, and finally it is copied in the release folder). Another important aspect is the ability to notify specific user to perform manual task.

Having a manual release document stored in source code allows you to evolve the file along with the code. Each code version has correct release document.

Since I use GitVersion to change name of the build based on GitVersion tags I prefer going to the Options tab of the release and change the name of the release.

image

Figure 5: Configure release name format to include BuildNumber

Release usually have a simple increasing number $(Rev:rr), but having something like Release-34 as release name does not tell me anything useful. If you are curious on what you can use in the Release Name Format field, you can simply check official documentation. From that document you read that you can use BuildNumber, that will contain build number of first artifact of the build. In my opinion this is a real useful information if the build name contains GitVerison tags, it allows you to have a meaningful release name.

SNAGHTML25af5e9

Figure 6: New release naming in action.

If you look at Figure 6 you can argue that build name is visible below release number, so the new numbering method (1) does not add information respect the standard numbering with only increase number (2).

This is true until you do not move to the deployment group or in other UI of Release Management, because there are many places where you can see only release Name. If you look at Figure 7 you can verify that with the old numbering system I see the release number for each machine, and for machine (1) I know that the latest release is Release 16, while for a machine where I release after numbering change I got Release – 34 – JarvisPackage 4.15.11

image

Figure 7: Deployment groups with release names

Thanks to release file definition and Azure DevOps release management, I have a controlled environment where I can know who started a release, which are the artifact deployed to each environment and who perform manual deploy.

Having a release definition completely manual is a great starting point, because it states what, who and how your software must be deployed, and will log every release, who started it, who performed manual  release, who approved release for every stage, etc.

Once everything works, I usually start writing automation script to automate steps of the release document. Each time a step is automated, I remove it from the deploy document or mark explicitly as “not to do because it is automated”.

Happy DevOps.

Run code coverage for Python project with Azure DevOps

Creating a simple build that runs Python tests written with PyTest framework is really simple, but now the next step is trying to have code coverage. Even if I’m pretty new to Python, having code coverage in a build is really simple, thanks to a specific task that comes out-of-the-box with Azure DevOps: Publish Code Coverage.

In Azure DevOps you can create build with Web Editor or with simple YAML file, I prefer YAML but since I’ve demonstrated in the old post YAML build for Python, now I’m creating a simple build with standard Web Editor

Instead of creating a Yaml Build, this time I’m going to demonstrate a classic build: here is the core part of the build.

image

Figure 1: Core build to run tests and have code coverage uploaded to Azure DevOps

As you can see, I decided to run test with a Bash script running on Linux, here is the task configuration where I’ve added Pytest options to have code coverage during test run.

image

Figure2: Configuration of Bash script to run Pytests

The task is configured to run an inline script (1), command line (2) contains –cov options to specify Python modules I want to monitor for code coverage, then a couple of –cov-report options to have output in xml and HTML format. Finally I’ve specified the subfolder that contains the module I want to test (3) and finally I’ve configured the task con Continue on Error (4), so if some of the tests fails the build will be marked as Partially failed.

Thanks to Pytest running code coverage is just a matter of adding some options to command line

After a build finished you can find in the output how Pytest generates Code Coverage reporting, it create a file called coverage.xml then an entire directory called htmlcov that contains a report for code coverage.

image

Figure 3: Result of running tests with code coverage.

If you look at Figure 1 you can see that the build final task is a Publish Code Coverage Task, whose duty is to grab output of the Pytest run and upload to the server. Configuration is really simple, you need to choose Cobertura as Code coverage tool (the format used by Pytest) and the output of test run. Looking at output of Figure 3 you can double check that the summary file is called coverage.xml and all the report directory is in htmlcov subdirectory.

image

Figure 4: Configuration for Publish Code Coverage task.

Once you run the build, you can find Code Coverage result on the summary page, as well as Code Coverage Report published as Build artifacts, the whole configuration will take you no more than 10 minutes.

image

Figure 5: Artifacts containing code coverage reports as well as code coverage percentage are accessible from Build Summary page.

Finally you have also a dedicated tab for Code Coverage, showing the HTML summary of the report

image

Figure 6: Code coverage HTML report uploaded in a dedicated Code Coverage tab in build result

Even if the code coverage output is not perfectly formatted you can indeed immediately verify percentage of code coverage of your test.

Gian Maria.