Every now and then, I come across teams who have developed a custom way of versioning their applications, artifacts etc. Some prefer manual tagging git commits with sem version, others prefer a file to be committed. There are several ways to accomplish the same, and folks usually go with a route that suits their needs. I want to showcase here an automated way of doing semantic versioning using git tags and describe
.
I am only interested in knowing when it is a major or a minor change.
What this means is that when a developer makes a change, only they know whether they added a new feature or it is no longer backward compatible. They don’t really care if it was patch version 1 2,3 and so on.
Even for a consumer of an artifact, it really doesn’t matter if you released 2.1.0 and then jumped to 2.1.10 or 3.0.1 and skipped versions in the middle.
Every commit is a patch unless specified.
Any commit can go to production. You are making a change, and at the very least it is a patch release.
Semantic Versioning
Lets say we have the following git history:
d99640b (HEAD -> master, tag: v/1.1) commit 4
6031055 commit 3
3287fc8 commit 2
77b39e9 (tag: v/1.0) first commit
Here is a function that generates a sem version.
function sem_version() {
# usage: sem_version ["v/*"]
# Requires current commit or a previous commit to be tagged.
# Tags should be of pattern v/d+.d+ eg: v/3.10
IFS=-
set -- `git describe --always --tags --match "$1"`
unset IFS
local major_minor=$1
local patch=$2
local v=`basename $major_minor.${patch:-0}`
echo $v
}
If you were to execute this on the latest commit, you will get the following output:
sem_version 'v/*'
# output: 1.1.0
If the same thing was executed at the very first commit:
git checkout 77b39e9
sem_version 'v/*'
# output: 1.0.0
If the same thing was executed at commit3:
git checkout 6031055
sem_version 'v/*'
# output: 1.0.2
Lets see what git describe
is doing.
git checkout 6031055
git describe --always --tags --match "v/*"
# output: v/1.0-2-g6031055
The above output is giving you the latest tag that matches v/*
, number of commits, 2
, since that tag and the current commit, 6031055
, prepended with letter g
.
The sem_version
method simply uses this to create a semantic version, with patch number is just the number of commits. The basename
command simply removes v/
from the git describe
output. You can also use the commit sha to add metadata to your semantic version. Ex: 1.0.2+6031055
. Build metadata is ignored when determining precedence anyways.
Ensure that you have your very first commit tagged with 'v/' pattern.
This makes it easier and consistent way of automating sem versionig in your CI/CD tools. And as a human, one would only need to look at tags that matches pattern v/*
. You can have your CI/CD tools push proper semantic versioned tags, but there will always be a clear distinction between automated vs manual tags.