We all always need our code to be versioned irrespective of what type of code it is. even if it is a Bash Script, PowerShell Script and more often a terraform code.

Most of you must have creating a lot of terraform modules and would like to version them automatically.

Yes, you could use the conventional git commits and tools like standard version, release-please, to release new versions and actively use them.

However my workflow has Azure Repo as my repository and we have branch policies set in between the main branch (At least we have it for fancy 😬). My standard version setup and conventional git commit works charmingly on my feature branches and main branches when there is nothing to block my commits. A pull request to the main branch is a show stopper here 😟. And you cannot really tag the main branch with a release as the branch policies block you directly committing to the main branch.

None
No Buddy. You're at the wrong place!!!

You could find more ways when you have your code in GitHub, and use release-please instead of standard-version and directly add it with Github actions.

None
So, What do we Azure Repo people do ??

Enough with the long boring story ! Let's do this with Azure DevOps !!!!

Start with a new repository in azure repos ( Yes! you could use your existing repo as well )

Let us do some basic local settings before you clone your repository locally!

  • Install standard-version globally in your device.
npm i -g standard-version
  • Now You could create a new branch out of the main branch and start working on your stuff
  • create the file package.json and add the below npm run script in to the package.json

Keep the version to 0.0.0 , as I have seen standard version complaining for an initial version

$ git checkout -b feature/feature1

$ vi package.json
{   
  "scripts": {     
    "release": "standard-version"   
  },  
  "version": "0.0.0" 
}

github.com/ visit to know more about standard version

  • keep the below pipeline as part of your code repo. You could use the pipeline task as part of your existing build/release pipeline.

If you are managing multiple repositories and would like to version them all, I would definitely recommend to keep a separate pipeline just for the release versioning part with multiple repo checkout in it

trigger:
- main

pool:
  vmImage: windows-latest

jobs:
- job: releasePlease
  displayName: Release Please 🚀
  steps:
  - checkout: self
    persistCredentials: true
    clean: true
  
  - task: NodeTool@0
    inputs:
      versionSource: 'spec'
      versionSpec: '18.x'
      checkLatest: true
    displayName: Install Node
      
  - pwsh: |  
    npm install -g npm@9.1.1
    git config --global user.email "<add user email>"
    git config --global user.name "<add user uname>"
    $commitMessage = "$(Build.SourceVersionMessage)"
    git fetch --all
    git switch $(basename $(Build.SourceBranch))
    npm i -g standard-version

    function releasenow {
      try {
        git push --follow-tags origin main
      } catch {
        Write-Host "An error occurred:"
      }
    }

    if ( $commitMessage -match "BREAKING CHANGE" -or $commitMessage -match "chore!" -or $commitMessage -match "feat!" ){
      write-host "Bump major version"
      $version = (Get-Content ./package.json | ConvertFrom-Json).version
      $currentMajorVersion = $version.Split(".")[0]
      Write-Host "Current Version : $currentMajorVersion"
      $bumpMajorVersion = [int]$currentMajorVersion + 1
      Write-host "Bumped Version = $bumpMajorVersion.0.0"
      # run npm release to set new version
      standard-version --release-as "${bumpMajorVersion}.0.0" --releaseCommitMessageFormat "$commitMessage [skip ci]"
      releasenow
    } elseif ($commitMessage -match "feat" -or $commitMessage -match "chore" -or $commitMessage -match "fix") {
      standard-version --releaseCommitMessageFormat "$commitMessage [skip ci]"
      releasenow
    } else {
      Write-Host "If you intend to do symantic release, Please follow conventional commits."
      Write-Host "You're commit is considered as a fix version"
      standard-version --release-as "patch" --releaseCommitMessageFormat "$commitMessage [skip ci]"
      releasenow
    }
  displayName: Release
  continueOnError: true

As you note, the pipeline is using the default windows-agent(It was my personal choice 😊), You can use ubuntu also as the default pool agent.

Azure devOps uses the azure devOps identities to checkout the code in the pipeline workspace, and a persist credentials flag allows you to contribute to the repo by reusing the credentials by its own.

We have to make some changes in the repository security settings for the git push to work without any issues. So, Keep reading!

Let us follow some discipline now onwards with the commits.

None


$ echo "Here comes the first feature" >> README.md  

$ git add .  

$ git commit -m "feat: The feature 1"  

$ git push --set-upstream origin feature/feature1

No worries! if you are person who like to do your git stuff along with you'r
IDE Just like me !.. Just do it from your favourite IDE.

I am adding the conventional commits here for your best learning 😇 . You could use tools like commitlint to help you with commit conventions

Conventional Commits

Ok, Let's park it here for some time

Its time to do some Azure DevOps Administration (You need admin privileges in your project to follow the steps )

I am not including the steps to set up the branch policies, as it does not fall under this scope. I hope you have the branch policies set in place. No problem if you don't have branch policies exist in your project ☺️

  • Navigate to the project settings in the left bottom, and select repository settings
None
None
  • Select repository of your choice to update the security changes (You could add securities and policies at the project level)
None
  • Make sure "Project Collection Service Accounts" has the permission allowed for Contribute, Create Branch, Create Tag, Read set

The below permission allows the above pipeline code to execute the git commands

None
  • Now, if you have branch policies set with the project or to the repository, You will not be allowed to push directly to the main branch. You will experience the same in the pipeline as well if you have not bypass the push
  • Set the "[Project Name] Build Service" to allow the bypass policies when pushing , Contribute, Create Tag permissions.
None

The above settings will be inherited to all the branches in the repository. To limit the permissions only to the main branch. select the main branch from the git refs permissions from the bottom and apply the same set of permissions

None

Ok, Now let's go back to where we pushed our feature branch….

Create a pull request to the main branch

  • Make sure the title bar in the pull request form has the conventional commit header (feat,fix,chore…) in it.
None

Azure DevOps always consider the PR title as the commit message, when you do a pull request. if you have not explicitly added a conventional commit header incase the title already doesn't have it, the pipeline always consider your PR as fix release (Change the logic in the PowerShell script if you wish to 😄).

  • Now approve and complete your pull request
  • let the build pipeline start running on completion of your PR.
None
  • Check the logs of the PowerShell task in the pipeline to see more details, With the example below, I have a new minor version now and it leaves a new tag in the repo
None
None

the standard version leaves an extra commit with a release tag on your main branch. I could not say it is a disadvantage (But that is how standard version works)

Now.. Start releasing your code with azure devOps 😇 😎

I am adding a bonus pipeline for those who wish to have a dedicated pipeline to release multiple repositories in the project

trigger:
- main

pool:
  vmImage: windows-latest


resources:
 repositories:
  - repository: "terraform-azure-vnet"
    type: "git"
    name: "vimal Templates/terraform-azure-vnet"
    trigger:
      - main
  - repository: "terraform-azure-subnet"
    type: "git"
    name: "vimal Templates/terraform-azure-subnet"
    trigger:
      - main
# add more repositories like the above ones, Donot forget to add the checkout steps

jobs:
- job: releasePlease
  displayName: Release Please 🚀
  steps:
  - checkout: self
    persistCredentials: true
    clean: true
  - checkout: terraform-azure-vnet
    condition: and(succeeded(), eq(variables['Build.Repository.Name'], 'terraform-azure-vnet'))
    persistCredentials: true
    clean: true
  - checkout: terraform-azure-subnet
    condition: and(succeeded(), eq(variables['Build.Repository.Name'], 'terraform-azure-subnet'))
    persistCredentials: true
    clean: true
# add checkout step for your repository

  - task: NodeTool@0
    inputs:
      versionSource: 'spec'
      versionSpec: '18.x'
      checkLatest: true
    displayName: Install Node
      
  - template: steps/release-please.yaml --> the powershell code goes here :)

Enjoy DevOps!