Working with a customer right now and trying to accomplish a number of tasks. First, is getting them accustomed to an enterprise branching strategy. The second, is helping them become familiar with Azure DevOps and configuring some build and deployment pipelines. The customer has their own versioning strategy – different across multiple projects – and they communicate with the business stakeholders features and resolved bugs in each release. Using a “one-size-fits-all” versioning configuration doesn’t work, and Azure DevOps only allows for a few custom placeholders in the Build Number. Luckily, Git-flow allows you to tag merges with custom versions so I simply had to configure Azure DevOps to use the latest tag on the master
branch for the build number.
Being that this isn’t a post about Git-flow, I’m not going to spend a lot of time on it. If you want more info, check out the cheatsheet or a great explanation by Atlassian.
Instead, as I stated in the first paragraph, I’m going to focus on how to use Git-flow tags for versioning in Azure DevOps.
If you are following the Git-flow process, you know that every time you finish a release or hotfix, a tag is placed on the master
branch. Of course, you’ll be pushing your tags from the local repository to the remote, as well. Essentially, what we’ll need to do is have Azure DevOps receive the latest tag and then apply it to the current build. Keep in mind that this build is building off of the master
branch and, therefore, assumes that a tag is present.
One final note…the customer I’m working with currently uses the version format ‘vX.X.X’ for this specific project. The code below takes the substring of that version starting with the second character. The resulting format is ‘X.X.X’.
Remove the Build Number
Azure DevOps will automatically set a build number format using the current date/time and a revision number. We don’t need any of that as we’re going to use our own. Additionally, if you leave the build number empty, the default build number will simply be the revision (which we’ll use in a second).
- In the build pipeline definition, click on the “Options” tab.
- Clear the textbox for the Build Number Format (second textbox) and leave it empty.
PowerShell Script
Now, we’ll use a PowerShell script to set the build number. Note, that you could use a bash script to do this, but, this particular customer was building a .NET Web Api so it made sense to use PowerShell.
- While still in the build pipeline definition, click on the “Tasks” tab.
- Add a new PowerShell task.
- Drag and move the new PowerShell task to the first task in the build configuration.
- Set the following configuration properties on the task:
Display name: Get Tag/Set Build Version
Type: Inline
Script:$git = "cmd.exe /C git describe --abbrev=0 --tags"; $tag = (Invoke-Expression -Command:$git).substring(1); $rev = $env:BUILD_BUILDNUMBER; $build = "$tag-$rev"; Write-Host "$("##vso[build.updatebuildnumber]") $($build)";
Here’s what’s going on in the script:
- PowerShell runs the
git
command to grab the latest tag on themaster
branch. - Get the substring (explained above) and strip out the preceding “v” from the tag (e.g. vX.X.X => X.X.X).
- Get the revision number from the Azure DevOps subsystem (read about this below).
- Create the newly formatted build number (e.g. X.X.X-XXX).
- Use the Azure DevOps output API to update the build number.
In the previous section, I explained that if we left the “Build Number Format” empty, then Azure DevOps would revert to the default build/revision number. Azure DevOps requires that each build have a unique build number. In our case, setting the build number to simply the last tag could cause a build failure if the build is attempted more than once for that tag. Therefore, we append the default build/revision number to our custom build number to ensure a successful build (at least in regards to the number, itself). My customer can still communicate the features and resolved bugs for the given build; they simply ignore the appended revision number.
While trying to figure this out took me quite a few hours (that last line in the code isn’t documented anywhere, grrrrhhhh!), the solution is very simple. If you decide to queue a build and monitor the logs simultaneously, you’ll see the build number update automatically after this task.
Thanks for this.
I iterated on it and have some feedback:
– From what I see now, builds can have the same buildnumber, I’m not adding the ‘rev’ and am not getting errors.
– The space should be removed from ‘##vso[build.updatebuildnumber]”) $($build)’
– If you build on the tags, and not on the master branch, you can get the tag from the $env:BUILD_SOURCEBRANCHNAME
David,
Thanks for the feedback. This post was based on a solution provided to the customer in late 2018, early 2019. So, it’s quite possible some things have changed. When I get some time, I’ll take a look and make the appropriate changes.
One thing, though. Your last point would be incorrect.
$env:BUILD_SOURCEBRANCHNAME
contains the name of the branch in which the tag was applied. So, if the tag was applied tomaster
(which is normally the case), then the environment variable would returnmaster
. It seems you are suggesting to branch per release and name the branch the tag name. You could do that, and then your suggestion would be correct. But that’s not typicalgit-flow
. Typicalgit-flow
is keeping all production code on themaster
branch and simply tagging it on the branch–very similar to just placing a “bookmark” on the branch.Does this need to run on a hosted agent? Cloud agents can’t seem to find the git command.
Dylan, I’m not sure if I understand your question… it could just be the terminology. Typically, a “hosted agent” is considered an agent hosted by Microsoft. But, it seems that maybe you are considering “cloud agents” as those hosted by Microsoft. Either way, all of Microsoft’s hosted agents should have git installed and available (otherwise, they wouldn’t be able to pull your repo to the hosted agent’s machine). Let me know if you need more help.