More and more build scenarios using Azure Pipelines require complex customization which have been simplified by the Configuration As Code feature that has been available in Azure DevOps for a couple years now. When making the switch from the GUI to YAML I struggled quite a bit with build numbers not working the same exact way since you can’t customize the Version Number. As Microsoft has iterated on Configuration as Code there are now functions that support incrementing Version Numbers
GUI Version Number
Semantic versioning is what many projects use to handle the product version. A semantic version would be 1.0.0.0
, {MajorVersion}.{MinorVersion}.{PatchVersion}.{RevisionVersion}
. To configure semantic versioning in the GUI of Azure Pipelines you first create the variables and then you configure the versioning strategy.
- Major Version
- Minor Version
- Patch Version
Build Number Format
Once you have the variables in place you are ready to update the Version Number configuration. Navigate to the Options tab which is where you can now program your Build Number Format. Using the variables we created we can program our semantic versioning scheme that will auto increment with each build.
1
$(MajorVersion).$(MinorVersion).$(PatchVersion)$(rev:.r)
Accessing Build Number
Everything is configured and now you can access your variable in powershell or various tasks that define your pipeline. The Build Number Format defines
$(Build.BuildNumber)
Configuration as Code
Configuration as Code is using YAML instead of the GUI to configure your build. The #1 benefit of using YAML is you can commit the changes to source control and track a version history. Configuration as Code runs the same build process but it can be much more powerful for customization. A downside though is there isn’t 100% feature parity between the GUI and YAML but the team at Azure DevOps is always working hard to get it as close as possible.
Counter
There is a technique in YAML templates called a counter, which is a function that generates an auto incremented integar every time it is invoked. The counter is really easy to use and assign to parameters or variables.
1
2
variables:
Version.Revision: $[counter(my-counter, 0)]
You can create as many counters as you want and make them unique to the strings you pass into the function.
- Identifier - any string that uniquely identifies the counter
- Seed - the starting index of your counter
The problem we ran into constantly is we didn’t like having a hard-coded version number floating in multiple places throughout our template. We wanted to use it once and have the variable be referenced. The identifier of the counter supports using variables so we store our version number there.
1
2
3
variables:
Version.MajorMinor: 1.0
Version.Revision: $[counter(variables['Version.MajorMinor'], 0)]
Docs
- https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#set-variables-using-expressions
- https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#user-defined-variables
-Happy Coding