Pipeline run versioning in Azure DevOps

Mar 24, 2024 - 3 min read

Assigning meaningful version numbers to pipeline runs is a critical practice in Continuous Integration (CI) for ensuring smooth traceability. Azure DevOps provides several tokens for use in the ‘name’ field of your pipeline file, enabling customizable versioning. However, for scenarios requiring more intricate versioning logic, the run number can be dynamically updated using the UpdateBuildNumber logging command.

Various versioning strategies exist, and though this isn’t an exhaustive list, it includes some practical snippets. These can be used as is, combined, or tweaked to better fit your workflow, offering a solid starting point for efficient version management.

Default

Default pattern for the name field is $(Date:yyyyMMdd).$(Rev:r), which results in a version like 20240324.1.

# name field purposely omitted

steps:
  - powershell: Write-Host 'Hello, versioning'

Partial version hardcoded

Major and minor version parts are updated manually, but the patch version increments automatically with every build.

name: 1.2.$(Rev:r)

steps:
  - powershell: Write-Host 'Hello, versioning'

$(Rev:r) is a special variable format that only works in the build number field. A new counter is created or reused based on the rest of the version specified.

Example runs:

  1. 1.2.$(Rev:r) ➞ 1.2.1 (counter for 1.2 created at 1)
  2. 1.2.$(Rev:r) ➞ 1.2.2 (counter for 1.2 now at 2)
  3. 3.4.$(Rev:r) ➞ 3.4.1 (counter for 3.4 created at 1)
  4. 1.2.$(Rev:r) ➞ 1.2.3 (counter for 1.2 now at 3)

Version file contents

Version is taken from a file in the repository, for example version.txt, and used as-is.

steps:
  - task: PowerShell@2
    displayName: Set run number
    inputs:
      targetType: inline
      script: |
        $versionFromFile = Get-Content 'version.txt' -Raw
        Write-Host "##vso[build.updatebuildnumber]$versionFromFile"

version.txt file example content:

1.2.3

Commit hash

A counter is created for each commit hash, for example b895c37c91f49b77428a64f6777d8ab6bce59d86 becomes b895c37c.1.

variables:
  - name: commitHashCounter
    value: $[counter(format('commit-hash-{0}', variables['Build.SourceVersion']), 1)]

steps:
  - task: PowerShell@2
    displayName: Set run number
    inputs:
      targetType: inline
      script: |
        $preferredHashLength = 8
        $commitHash = '$(Build.SourceVersion)'
        $shortHash = $commitHash.Substring(0, [Math]::Min($commitHash.Length, $preferredHashLength)) # shorten hash to preferred length if possible
        Write-Host "##vso[build.updatebuildnumber]$shortHash.$(commitHashCounter)"

Branch name

Each branch gets its own counter and a version-safe name is used as a prefix, for example feat/my-branch becomes feat-my-branch.1.

variables:
  - name: branchCounter
    value: $[counter(format('branch-{0}', variables['Build.SourceBranch']), 1)]

steps:
  - task: PowerShell@2
    displayName: Set run number
    inputs:
      targetType: inline
      script: |
        $branchName = '$(Build.SourceBranch)'
        $safeBranchName = ($branchName -replace '^refs/heads|[^A-Za-z0-9]+', '-').Trim('-')
        Write-Host "##vso[build.updatebuildnumber]$safeBranchName.$(branchCounter)"

External pipeline resource

Version is inherited from another pipeline run with an appended counter, for example, other-pipeline-run.1 #2. A unique counter is created for each run number of the referenced pipeline. This approach is beneficial when managing separate build and deploy pipelines, allowing the deploy pipeline to inherit the build pipeline’s version while accounting for multiple deployment attempts of the same build.

resources:
  pipelines:
    - pipeline: alias-of-my-other-pipeline
      source: MyOtherPipelineName

variables:
  - name: pipelineResourceCounter
    value: $[counter(format('pipeline-resource-{0}', variables['resources.pipeline.alias-of-my-other-pipeline.runname']), 1)]

steps:
  - task: PowerShell@2
    displayName: Set run number
    inputs:
      targetType: inline
      script: |
        $artifactVersion = '$(resources.pipeline.alias-of-my-other-pipeline.runname)'
        Write-Host "##vso[build.updatebuildnumber]$artifactVersion #$(pipelineResourceCounter)"