Why should we care about Semantic versioning?
What is it all about?
First of all, semver.
Semver is a specification outlining a method of encoding the nature of change between releases of a “public interface”,
As they said on the official page:
In the world of software management, there exists a dreaded place called “dependency hell.” The bigger your system grows and the more packages you integrate into your software, the more likely you are to find yourself, one day, in the pit of despair.
With Semantic Versioning a version consists of 3 elements and the change determined by this legality:
- Major — when there is a change in API
- Minor — when there is a functionality in a backward-compatible manner
- Patch — when there is a bug fix
Automating the versioning process has always been a challenge in Configuration Management, with microservices it must be automated, here enters Semantic-Release
Semantic Release is an Open-Source Software tool for automatically versioning your software with Semantic Versions, based on your Git commit messages
Sounds simple? Yes, it is!
The semantic release is a library that implements the “policy” of semver in a given project, it was initially designed to automatically release packages to npm, in addition, it has adopted the Angular commit convention
The commit prefix will help choose which version level we want to upgrade by the convention of the commit message:
- fix: patch change
- feat: minor change
- perf: major change
Another great benefit of semantic-release is that you can define which branch is a release branch and which is a pre-release and which aren’t eligible for release. The pre-release branch gets the ‘-’ / hyphen character with the branch name and build number. e.g example: 1.2.3-feature.1
How it actually works you can see at https://semantic-release.gitbook.io/semantic-release/
Ok, but why should I use it?
Let’s see our use case
Like everyone, we moved from a monolith to microservices. from the slow release once a month we moved to almost daily releases. We needed to find a fast solution that will work swiftly.
Our mixture of programming languages include maven, python, Docker containers and helm charts.
Each method needs its own version and we wanted to create uniformity.
In three words — Speed, efficiency & Predictability!
Let’s look at our GitLab (of course!).
As we said we have maven, python, and charts for now…
What do they all have in common? Nothing.
Here comes the semantic release.
We will demonstrate it on maven code:
We have 50 repositories divided into groups.
In every repository, there is a pom.xml file and in most of them Dockerfile either
We build the .gitlab-ci.yml with include files to create uniformity:
In every repo, we put the stage, variables, and maven-build files because every pom needs a maven build minimum.
In the repo that has Dockerfile, we insert deploy.gitlab-ci.yml too.
Where does the version change? between the build and the deploy.
We create another file called semantic-release.gitlab-ci.yml
The file contains:
- The user access token of the runner that runs the ci which is “gitlab-runner” in our case
- The command npm install @semantic-release/gitlab — the default is github so we had to install it
- The run command — npx
and of course, everything runs on node image.
In the root directory we add a “.releaserc.yml” file like the following:
This configuration file defines:
- Plugins - which plugin we want to install and his extension (for more details see https://semantic-release.gitbook.io/semantic-release/usage/plugins#plugins)
- Branches - which branch is release branch and which pre-release
Let’s walk through our refined release process:
What do we have in our pipeline until now?
- We made a change in our code
- Add commit with the desired convention (fix/feat/pref)
- The commit triggers the pipeline with the maven build with the existing version
- The semantic version kicks in and we have a new tag that indicates a release
Now we want to deploy but we still stuck with the old version in the pom.xml file.
We need to update the pom.xml.
For that, we need to put the new version into a variable.
It requires override of the release-job in the .gitlab-ci.yml:
When semantic-release is activated and a new version is created, the ‘created tag vx.y.z’ line is showed.
We take this line and isolate the version number and put it into the NEXT_VERSION variable, which is passed on as an artifact named build.env to the next job in line.
Worth noting the trigger of our release stage only happens in dev/master branches. This way we keep the pipeline area clean. Because semantic release commits the new tag to the repo and the commit triggers the pipeline again, the pipeline won’t succeed because the branch is not in the .releaserc.yml file.
semantic-versions and continuous deployment process
To update the pom with the new version we add before_script to the deploy stage:
- Update the pom with a new version
- Push to the repo, this commit is done with ‘ci skip’ to avoid endless pipeline loops
That’s it, we got ourselves a Reliable, Predictable, and Automatic release process — thanks to semantic-release. A reference repository with this example can be found here in semantic-release repo including our full gitlab-ci.yml.