NuGet package versioning with DevOps

nuget packages

In this new post, I show you how to implement a NuGet package versioning with Azure DevOps in your pipeline. This is a continuation of my previous post “NuGet package with Azure DevOps“. In that post I explained how to create a pipeline for creating a NuGet package and push it in your private artifacts.

This seems to be another area, where the documentation certainly does exist, but most of it just didn’t answer our questions. A task that should have been easy and straightforward to solve, really wasn’t. At all.

So either our use case was unique, we were asking the wrong questions, or the documentation wasn’t comprehensive enough – I’ll leave that distinction to someone else!

Scenario

So, we want to configure Azure DevOps pipelines with the intention of publishing new package versions to our internal NuGet stream automatically with each successful Release build of the package. This would enable us to easily share different versions of our package. Also, we can promote each stable build to the PreRelease stream. So, different teams can effortlessly access them for testing purposes before picking the stable versions for their production builds.

Then, the configuration and requirements are as follows:

  1. .NET Project
  2. Azure DevOps pipeline configured with YAML
  3. An internal NuGet feed to publish the packages to
  4. Unique, incremental semver-compatible version numbers for each update of the package
  5. No need to bump the number manually

Which versioning options do we have and how do they function?

There is a few different options to choose from, when you’re configuring your build pipeline’s NuGet packaging -step’s versioning scheme. The next chapter is meant to help you choose the one that suits you the best!

Available versioning schemes

  • byPrereleaseNumber
    • Configure the package version with a number of other switches, namely:
      • majorVersion
      • minorVersion
      • patchVersion
      • packTimezone (documentation says this is required, but it just seemed to default to UTC without)
  • byEnvVar
    • Reference a variable to set the package version
  • byBuildNumber
    • Build NAME is used as the number – it needs to be something that can be parsed as a build number. Note that this’ll also literally change the names of your builds in the build history and all emails.
  • off
    • Configure the package version using either the project file or a separate nuspec file.

What is the issue?

Most of us who have used Azure Pipelines builds and release at some point have used or come across a built-in variable called Rev. This Rev variable is a built-in variable that is only accessible in the Build Number Format and Release Number Format in Azure Pipelines builds and releases. This Rev variable is an automatically incrementing number that is tracked by Azure Pipelines. And when used in combination with some other values in a Build Number Format or Release Number Format, it will automatically increment starting from one unless any part of the Build/Release Number has changed.

We have an Azure Pipelines Build Definition that uses the following Build Number Format.

1.0.$(Rev:r)

When this build triggers for the first time, the version number will be 1.0.1. And if you trigger it again the version number will be 1.0.2. Now if you change the build number format to the following.

1.1.$(Rev:r)

And now if you trigger a build, the version number will be 1.1.1 and subsequently 1.1.2, 1.1.3 etc. Now you see that as soon as the rest of the build/release number changes the Rev variable will reset and start incrementing from one.

The Problems With $(Rev)

Some of the major problem with Rev variable is that,

  • The Rev variable will always increment starting from 1.
  • The Rev variable can not be referenced from anywhere other than the Build/Release Number format.

If you forget about Rev not being able to be referenced from anywhere other than Build/Release number, what bothers me the most (And I’m sure others as well) is that we can not control where it starts to increment. Meaning if you want to start incrementing from 0 or may be 100, you are out of luck. There is may workaround to this, but one of the simplest is the one we are going to talk about next.

Creating an Equivalent for $(Rev)

You can easily use a Counter Expression to simulate the functionality of Rev in the Variables section in Azure Pipelines Build/Releases. And the best part is this being a custom variable, it can be referenced from anywhere in the build/release pipeline.

For this we will be using the counter Expression Function. This function has the following syntax

counter(name, seed)

The parameter name is the name of the variable that is created behind the scene. And when the same variable is referenced from the build/release the value is incremented by 1.

The parameter seed is the starting value for the variable. It defaults to 0 and you can set any integer value. Eg. 100. And when first triggers it will start from 100 and in the next trigger it will increment to 101 etc.

And you can guess that if change the variable name that we reference, it will create a new variable and start incrementing from the seed value, which is the trick we need to use to simulate the $(Rev) variable.

Solution and implementation

So, for having a NuGet package versioning with DevOps, we have to add some variables in the pipeline. In your pipeline, click on the top right button Variables. Then, add the following variables:

  • Major: the major version of your package. In the image below is 1
  • Minor: the minor version of your package. In the image below is 0
  • PackageVersionType: this is the place where you can specified if this version is -pre release, -alfa, -beta or whatever you prefer
  • Patch: here is where the magic happen. I’ll explain in a second
  • PackageVersion: is creating the string for the version of this package

With this, I have an incremental, automatic Patch-version of my PackageVersion variable, with Major and Minor being updated manually by yours truly. I also have the optional PackageVersionType, in case I want to label a package explicitly as being “preview” or anything else.

VariableFunction
Patch
$[counter(format('{0}.{1}', variables['Major'], variables['Minor']), 0)]
PackageVersion
$(Major).$(Minor).$(Patch)$(PackageVersionType)
Add variables in the Azure DevOps pipeline - NuGet package versioning with DevOps
Add variables in the Azure DevOps pipeline

Update the pipeline

Now, you have the variable to use in the pipeline but we have to amend it. In the section Prepare the package, I added the versioningScheme to read the value from the variables and versionEnvVar to read the PackageVersion.

    # Starter pipeline
    # Start with a minimal pipeline that you can customize to build and deploy your code.
    # Add steps that build, run tests, deploy, and more:
    # https://aka.ms/yaml

    trigger:
    - main

    pool:
      vmImage: ubuntu-latest

    steps:
    - task: DotNetCoreCLI@2
      displayName: Restore packages
      inputs:
        command: 'restore'
        projects: '**/*.csproj'
        feedsToUse: 'select'
    - task: DotNetCoreCLI@2
      displayName: Build project
      inputs:
        command: 'build'
        projects: '**/*.csproj'
    - task: DotNetCoreCLI@2
      displayName: Run tests
      inputs:
        command: 'test'
        projects: '**/*[Te]ests/*.csproj'
    - task: NuGetCommand@2
      displayName: Prepare the package
      inputs:
        command: 'pack'
        packagesToPack: '**/*.csproj'
        versioningScheme: 'byEnvVar'
        versionEnvVar: 'PackageVersion'
    - task: NuGetCommand@2
      displayName: Push the package
      inputs:
        command: 'push'
        packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg'
        nuGetFeedType: 'internal'
        publishVstsFeed: 'c800d0d7-e2af-4567-997f-de7cf7888e6c'

Wrap up

In conclusion, we have a nice pipeline for NuGet package versioning with DevOps! For more details about how to use variables on a pipeline on Azure DevOps, see the Microsoft documentation.

Please, leave your comment of chat with us in the Forum.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.