1

I have an Azure DevOps pipeline that references two repos:

  • The code repo, which contains my application code
  • The config repo, which contains my environment variable config files.

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 run
  • MyApp 0.1.1.0 Change to application code
  • MyApp 0.1.1.1 Change to environment config
  • MyApp 0.1.2.0 Change to application code
  • MyApp 0.1.2.0 No change to code nor config
  • ...

I'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

4
  • The counter function evaluates a number that is incremented with each run of a pipeline. Commented Sep 3, 2024 at 15:53
  • 1
    Do you set the config repo as a submodule of code repo, or set it as a repository resource in the pipeline of code repo? Do you consider using the GitVersion tool? Commented Sep 4, 2024 at 3:04
  • @BrightRan-MSFT: They are two separate git repos, and I reference them as two separate repository resources near the top of my pipeline. Commented Sep 4, 2024 at 15:15
  • @ColinSmith, I posted an answer with some suggestions. Check and try it. Commented Sep 5, 2024 at 3:07

1 Answer 1

5

You can configure the pipeline to use the GitVersion tool combining with the counter function to automatically generate the expected version number.

  1. Install the GitTools extension to your Azure DevOps organization. It provides the pipeline tasks to install and execute the GitVersion tool for semantic versioning.

  2. 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
    
    • The {major} is the {major} in your version number.
    • The {minor} is the {minor} in your version number.
    • The {Patch} is the {codeVersion} in your version number.
  3. 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'
    
    . . .
    
  4. In the application code repository, create the tag "v0.1.0.0" to the latest commit (see "Create tags from the Commits view").

  5. 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.

enter image description here


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".

enter image description here


Sign up to request clarification or add additional context in comments.

3 Comments

Thanks so much for looking into this. I've learnt a lot about gitversion over the past couple of days :) This solution is very close! The main thing that it doesn't do is only to increment the version number when the config repo is actually updated. I know what you are saying about the pipeline not being triggered automatically if no commits were made to either repo... but someone may still trigger it manually. In those cases, I wouldn't want the config version number to increment because there were no changes to the config repo in the meantime.
@ColinSmith, I have updated my answer with more suggestions. See EDIT.
Okay, I will mark this as the answer as it's as close as I think you can get. In my case, I need the build to run in order to generate the build artefacts used in the later deployment stages of the pipeline. There's clearly no simple way of saying "Increment different bits of the version number based on whether git commits happened across various repos.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.