Categories
Automation CI/CD DevOps

Auto Tag Releases with Semantic Versions

If you developed modern CI/CD pipelines you probably stumbled on the need to auto tag releases with semantic versions. Today I’ll show how to do that automatically and which tools may help to achieve automatic tagging releases with semantic versions.

If you later find this article useful take a look at the disclaimer for information on how to thank me.

Introduction

What is Semantic Version?

While developing software and issuing its releases you’d usually want to version them. If you are issuing releases of modern software you probably tag them with semantic versions using some release pipeline which runs in some automation server (e.g. GitLab, Jenkins, Azure Devops…) The pipelines have to be clever enough to auto-tag each release with the next semantic version. Semantic version usually has 3 digits major.minor.patch, e.g. 1.2.1 with an optional build data or other unique id appended to it (e.g. release latest commit SHA1). After each release, one of the digits has to increment. Each digit increment denotes a different change which happened in the release:

  • breaking change – major.
  • non-breaking change – minor.
  • bug fix – patch.

Why to use Semantic Version?

While the semantic versioning specification answers that question, I’d try to do that in my own words as well. Semantic version increment conveys the essence of the release (breaking change – major, non-breaking change – minor, bug fix – patch). This in turn may influence the release deployment strategy, e.g. major upgrade may require downtime of the released app during the upgrade, while minor upgrade may not.

To achieve fully-automatic releases semantic versioning tagging, release pipeline has to magically decide which digit of the semantic version to increment. Let’s see the ideas behind auto-tagging of releases with semantic versions.

How to auto tag releases with semantic versions?

Semantic version increment signifies the nature of the change

While there could be many ways to implement semantic versioning auto-tagging of releases, one major idea stands behind all of them. Someone, somehow has to provide the information about the nature of the change (breaking change, non-breaking change, bug fix) to the release pipeline. Let’s assume that releases happen frequently e.g. after each merge (pull request (MR) merge to main branch. So each change is encapsulated in a MR whose property has to convey the nature of the change. Such property could be MR source branch name, MR label or tag, etc…

Semantic version increment example

The developer of the MR has to decide if it’s a breaking change or a simple bug fix. Hence, the developer has to attach the appropriate property to that MR (e.g. label “breaking change”. This way, the developer instructs the release pipeline to auto-increment major digit of the upcoming release’s version after the MR merge. The pipeline will then read the MR label, will build the release and tag it with the next semantic version. To explain by example, if before the MR merge the latest released version was 2.3.1, after the successful release, the version would be 3.0.0. If you followed the flow closely, you may rightfully ask where the latest version was stored before the increment. Obvious answer would be in the git tags. So the next semantic version should be stored there as well, by tagging the latest main branch commit with the version.

Auto tagging releases with semantic versions demo

Let’s see auto tagging of releases with semantic versions in action. I’ll show a demo app being built and released on GitLab. Feel free to fork it and play along.

I’ll first open a merge request (MR) with a sample change. MR’s source branch name will influence the release pipeline to auto-tag the release with an appropriate semantic version. So, MR source branch is test-PATCH. Next, I’ll merge the MR to main branch. Of course, CI pipeline should run before merging. But I’ll skip that for simplicity as our focus is on the release pipeline. Note that the last step of the pipeline is pushing semver as a git tag. This is a critical step for semver auto-tagging to work. You can see that the first semver that was generated is 0.0.1. That’s because there were no git tags yet when it ran.

How semantic version was generated?

As you can see, the release pipeline runs in a custom image I built for the demo. The image contains the necessary scripts to generate semver. The script utilizes git-semver open source project in order to create a semantic version based on MR source branch name. You can find the full source code of the image contents at my GitHub.

Next, open a second MR with a different branch name – test-MAJOR, merge it and see that the release pipeline incremented the major digit i.e. tagged the release with 1.0.0 semver. You can view the third pipeline which ran for MR with source branch name – test-MINOR. Not surprisingly, the next semver was 1.1.0.

If you prefer to self-host GitLab you can install it on Linode’s managed Kubernetes cluster. Check out how easy it is to create Kubernetes Cluster on Linode and how to run a self-hosted GitLab runner there. Linode is a cloud service provider recently purchased by Akamai. With this purchase, Akamai became a competitor in the cloud providers market. Create a Linode account and get 100$ credit using this link.

Summary

That’s it about auto tagging of releases with semantic versions. As always, feel free to share. If you found this article useful, take a look at the disclaimer for information on how to thank me.

You may find below articles interesting:

Recommended GitLab books on Amazon.