amura.cxg
amura.cxg

Reputation: 2427

Azure Pipelines Conditional and Persistent Variables

I'm trying to setup an Azure Pipeline that automatically updates version numbers based on which branch is being built. Given a master branch, a develop branch, and a version in the form {major.minor.patch.build} I want to achieve the following:

Whenever the pipeline is triggered by master:

Whenever the pipeline is triggered by develop:

An example of what I expect the version numbers to look like after multiple runs assuming a starting version of 1.0.0.0:

My first thought was to use a counter for the patch and build values but there doesn't seem to be a way of conditionally incrementing them or resetting the counter. My next thought was to use a couple script tasks that execute conditionally based on the branch, such as:

    #Update build
    - powershell: echo "##vso[task.setvariable variable=version.build;isOutput=true]$(version.build + 1)"
      condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop')

    #Reset build number
    - powershell: echo "##vso[task.setvariable variable=version.build;isOutput=true]0"
      condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')

    #Update patch
    - powershell: echo "##vso[task.setvariable variable=version.patch;isOutput=true]$(version.patch + 1)"
      condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')

However that approach isn't persistent across runs (not to mention $(version.patch + 1) doesn't work. I can't find any information on how to actually increment a variable in this way). My last idea was to have a version file in the repository and use that but I really don't want to have to go down that route.

Is there a way to achieve what I'm looking for using Pipelines or am I barking up the wrong tree trying to go about it this way?

Edit I should have mentioned in the question that at this point I have a pipeline setup that builds my application but does nothing with versioning. What I'm looking for at this point is a way to dynamically update a few persistent variables/values (specifically patch and build).

Upvotes: 3

Views: 2748

Answers (2)

Levi Lu-MSFT
Levi Lu-MSFT

Reputation: 30313

I have a workaround to achieve this with restful api.

For example you want to add above version tag to the build. You can first get the tag of the previous build with get tag api. Then you can update the version with the scripts and update it with updated tag. You can refer to below scripts in the powershell task.

# first get previous build id

$url = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds?definitions={pipeline Id}&`$top=2&api-version=5.1"

$result = Invoke-RestMethod -Uri $url -Headers @{authorization = "Bearer $(System.AccessToken)"} -Method get

$builds = $result.value
$bid = $builds[1].id

# get the build tag with get build tag api

$urltag ="$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$bid/tags?api-version=5.1"

$tagResult = Invoke-RestMethod -Uri $urltag -Headers @{authorization = "Bearer $(System.AccessToken)"} -Method get

# get the tag and update it

if($tagResult.value)
{
   $tagarr = [regex]::split($result.value, '\.')
   $patch = $tagarr[2]
   $build = $tagarr[3]
   $tag=""
   if("$(Build.SourceBranch)" -eq "refs/heads/master")
   {
      $patch = [int]$patch + 1
      # update the variables version.patch and version.build with below scripts, so that it can be referenced in the following tasks.
      #echo "##vso[task.setvariable variable=version.patch;isOutput=true]$patch"
      #echo "##vso[task.setvariable variable=version.build;isOutput=true]0"

     $tag = "$(version.major).$(version.minor).$patch.0"    
   }  
   if("$(Build.SourceBranch)" -eq "refs/heads/dev")
   {
      $build= [int]$build+ 1
      #echo "##vso[task.setvariable variable=version.patch;isOutput=true]$patch"
      #echo "##vso[task.setvariable variable=version.build;isOutput=true]$build"
      $tag = "$(version.major).$(version.minor).$patch.$build"  
   }

 #add $tag to current build.
  $urltagupdate = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/tags/$($tag)?api-version=5.1"

  Invoke-RestMethod -Uri $urltagupdate -Headers @{authorization = "Bearer $(System.AccessToken)"} -Method put

}else 
{
  #if the build is run for the first time and the tag is not exist.
  #you can omit this part by manually creating a initial tag for the build.

  $tag = ""
  if("$(Build.SourceBranchName)" -eq "refs/heads/master")
  {
    $tag = "1.0.1.0"
  }
  if("$(Build.SourceBranchName)" -eq "refs/heads/dev") 
  {
    $tag="1.0.0.1"
  }

 #add $tag to current build.

  $urltagupdate = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/tags/$($tag)?api-version=5.1"

  Invoke-RestMethod -Uri $urltagupdate -Headers @{authorization = "Bearer $(System.AccessToken)"} -Method put

}

Above is an example for build tags. If you want to tag repo branch you can use refs tag api.

The main idea is to get the previous tag via api and update the version.patch and version.build according to the build.sourcebranch in a powershell task.

Upvotes: 3

Eric Smith
Eric Smith

Reputation: 2560

So there is a counter variable that is available that can handle some of what you want.

Using it I was able to partially get it to work with what you are after.

trigger:
- master

pool:
  vmImage: 'ubuntu-latest'

variables:
  major: 1
  minor: 0
  # define b as a counter with the prefix as variable a, and seed as 100.
  #patch: $[counter(variables['major'], 1)]

  ${{ if eq( variables['Build.SourceBranchName'], 'master') }}:
    patch: $[counter(variables['major'], 1)]
    build: 0

  ${{ if eq( variables['Build.SourceBranchName'], 'develop') }}:
    build: $[counter(variables['major'], 1)]
    #patch: ?


name: $(major).$(minor).$(patch).$(build)

steps:
    - bash: echo $(major).$(minor).$(patch).$(build)

But as far as I see there is not a good way to get the current $(patch) counter variable when a build runs on develop. Side note I would be interested to know how to get at the custom counter values. You could likely use the API and go grab the Buildnumber from the last run and pull the patch number and reuse it, but that leads into this question you asked.

Is there a way to achieve what I'm looking for using Pipelines or am I barking up the wrong tree trying to go about it this way?

I would argue to change your approach. If you are crafting custom versioning schemes with powershell or yaml, at that point I would say versioning is important enough for you to use GitVersion or some equivalent. There is a learning curve to get started, but one of the default out of the box GitVersion modes would likely serve your purpose.

There is a pipeline task here that will handle computing a version for you, if you just add it to your pipeline.

- task: GitVersion@5
  inputs:
    runtime: 'core'
    configFilePath: 'GitVersion.yml'

Better to supply a GitVersion.yml, which is the scheme or instruction set for how to do the versioning.

There is a chocolatey package that you can install to experiment with locally. If you run GitVersion init in the root of your repo it will walk you though some versioning options, and create the GitVersion.yml for you.

mode: Mainline
branches:
  master:
    tag: ''
    regex: master
    increment: minor
  develop:
    tag: ''
    regex: develop
    increment: patch
ignore:
  sha: []
merge-message-formats: {}

As an example the above stock mainline mode gives me:

enter image description here

Upvotes: 3

Related Questions