I have an Azure DevOps pipeline that references two repos:
The idea is that I should be able to make simple config changes and re-deploy without changing the application's version number (since the code hasn't changed). However, I still want to be able to see in the version number that the config changed between runs.
As a result, I'm thinking of using a version number in the following format:
{major}.{minor}.{codeVersion}.{configVersion}
... with the following constraints:
{major}
and {minor}
are to be set manually by the developer.{codeVersion}
should auto-increment every time there is a commit to the code repo as long as {major}.{minor}
hasn't changed.{configVersion}
should auto-increment every time there is a commit to the config repo as long as {major}.{minor}.{codeVersion}
hasn't changed.Note: I understand that running the pipeline twice in quick succession would result in the same version number for both runs. This is acceptable since there were no changes to either repo in between.
As an example, I might expect to see a bunch of pipeline runs with the following names:
MyApp 0.1.0.0
Initial pipeline runMyApp 0.1.1.0
Change to application codeMyApp 0.1.1.1
Change to environment configMyApp 0.1.2.0
Change to application codeMyApp 0.1.2.0
No change to code nor configI've had a look at the counter function, but it doesn't seem to do what I need. My first attempt was this:
name: '$(Build.DefinitionName)-$(major).$(minor).$(codeVersion).$(configVersion)'
variables:
- major: 0
- minor: 1
- codeVersion: $[counter(format('{0}.{1}', variables['major'], variables['minor']), 0)]
- configVersion: $[counter(format('{0}.{1}.{2}', variables['major'], variables['minor'], variables['codeVersion']), 0)]
Unfortunately, with this code the codeVersion
value always changed on every run even if I made no commits to the code repo. Then, as a result, the configVersion
value was always zero.
I've tried a few variations of the above where I tried referencing the $(Build.SourceVersion)
variable to get the git commit ID but I haven't been able to find a working solution yet.
The fundamental issue seems to be that the counter function increments when the supplied values haven't changed, whereas I want it to increment when the commit ID of the code repo has changed.
Edit: Fixed typo
You can configure the pipeline to use the GitVersion tool combining with the counter
function to automatically generate the expected version number.
Install the GitTools extension to your Azure DevOps organization. It provides the pipeline tasks to install and execute the GitVersion tool for semantic versioning.
In the root of your application code repository, add the GitVersion.yml
file referencing the sample below.
# GitVersion.yml
mode: mainline
tag-prefix: '[vV]?'
assembly-informational-format: '{Major}.{Minor}.{Patch}'
major-version-bump-message: '\+semver:\s?(major)'
minor-version-bump-message: '\+semver:\s?(minor)'
patch-version-bump-message: '\+semver:\s?(code|app)'
no-bump-message: '\+semver:\s?(none|skip)'
commit-message-incrementing: Enabled
update-build-number: false
{major}
is the {major}
in your version number.{minor}
is the {minor}
in your version number.{Patch}
is the {codeVersion}
in your version number.In the root of your application code repository, add the pipeline main YAML file referencing the sample below.
# azure-pipelines.yml
# Set the CI trigger.
trigger:
branches:
include:
- main
# Set the environment config repository as a repository resource.
# And enable resource trigger.
resources:
repositories:
- repository: config
type: git
name: envConfig
ref: main
trigger:
branches:
include:
- main
# Generate the part "{configVersion}" of the app version number.
variables:
configVersion: $[ counter(resources.repositories.self.version, 0) ]
steps:
- checkout: self
fetchDepth: 0
- task: gitversion/setup@3
displayName: 'Install GitVersion'
inputs:
versionSpec: '5.x'
preferLatestVersion: true
# Generate the part "{major}.{minor}.{codeVersion}" of the app version number.
# The task sets the variable "$(GitVersion.FullSemVer)" to pass the generated value.
- task: gitversion/execute@3
displayName: 'Generate Version'
# Combine the generated values to get the full app version number.
# Update the pipeline build/run number with the full app version number.
- bash: |
echo "##vso[task.setvariable variable=APP_VERSION;]$(GitVersion.FullSemVer).$(configVersion)"
echo "##vso[build.updatebuildnumber]$(GitVersion.FullSemVer).$(configVersion)"
displayName: 'Set App Version'
- bash: |
echo "APP_VERSION = $(APP_VERSION)"
displayName: 'Print App Version'
. . .
In the application code repository, create the tag "v0.1.0.0
" to the latest commit (see "Create tags from the Commits view").
Use above azure-pipelines.yml
file to create a pipeline for the application code repository.
With above configurations:
When you trigger the pipeline at the first time to run for the latest commit which has the tag "v0.1.0.0
", the generated app version number is the '0.1.0.0
'.
When you push a new commit to the main branch of application code repository, the pipeline gets automatically triggered by the CI trigger. The number of '{codeVersion}
' +1. For example, the full app version number changes from "v0.1.0.0
" to "v0.1.1.0
".
When you push a new commit to the main branch of application code repository, and the commit message contains the string "+semver: code
" or "+semver: app
", the number of '{codeVersion}
' also can +1.
when you push a new commit to the main branch of environment config repository, the pipeline gets automatically triggered by the resource trigger. The number of '{configVersion}
' +1. For example, the full app version number changes from "v0.1.1.0
" to "v0.1.1.1
".
When you push a another new commit to the main branch of application code repository, the pipeline gets automatically triggered by the CI trigger. The number of '{codeVersion}
' +1, and the number of '{configVersion}
' gets back to 0
. For example, the full app version number changes from "v0.1.1.1
" to "v0.1.2.0
".
When you push a new commit to the main branch of application code repository, and the commit message contains the string "+semver: minor
", the number of '{minor}
' +1, the numbers of '{codeVersion}
' and '{configVersion}
' gets back to 0
.
When you push a new commit to the main branch of application code repository, and the commit message contains the string "+semver: major
", the number of '{major}
' +1, the numbers of '{minor}
', '{codeVersion}
' and '{configVersion}
' gets back to 0
.
If no commit pushed to neither the application code repository nor the environment config repository, the pipeline will not get automatically triggered. So, no new version number generated.
EDIT:
Try to configure the pipeline like as below.
trigger:
branches:
include:
- main
resources:
repositories:
- repository: config
type: git
name: envConfig
ref: main
trigger:
branches:
include:
- main
jobs:
- ${{ if notIn(variables['Build.Reason'], 'Manual') }}:
- job: build
variables:
configVersion: $[ counter(resources.repositories.self.version, 0) ]
steps:
- checkout: self
fetchDepth: 0
- task: gitversion/setup@3
displayName: 'Install GitVersion'
inputs:
versionSpec: '5.x'
preferLatestVersion: true
- task: gitversion/execute@3
displayName: 'Generate Version'
- bash: |
echo "##vso[task.setvariable variable=APP_VERSION;]$(GitVersion.FullSemVer).$(configVersion)"
echo "##vso[build.updatebuildnumber]$(GitVersion.FullSemVer).$(configVersion)"
displayName: 'Set App Version'
- bash: |
echo "APP_VERSION = $(APP_VERSION)"
displayName: 'Print App Version'
. . .
- ${{ else }}:
- job: noUpdate
steps:
- script: |
echo "This run is not automatically triggered by any new updates."
echo "Skip all the build steps. Version number will not be updated."
With above configurations, if the pipeline is triggered by manual (Build.Reason = Manual
), the whole build
job will not run, the number of configVersion
also will not increase, and the noUpdate
gets run.
In the condition expression "${{ if notIn(xxxx) }}
", you can add more than one items. For example, "${{ if notIn(variables['Build.Reason'], 'Manual', 'Schedule') }}
", the build
job will not run when the pipeline is triggered by manual or schedule (Build.Reason = Schedule
). See the documentation "Expressions".
The available values of Build.Reason
are listed in the image below. You also can see the documentation "Use predefined variables".