You and your team are maintaing a set of backend services for an online shopping application. Scenario A, your colleague is pushing a bug fix to Order Service that caused some problems in the Cart Service. This new push has changed the state of the service from the old buggy state to the new fixed state. But say there is another team who want to quickly identify the magnitude of your change, i.e. did you break any logic? did you remove a class, or did you change a function signature that was used in other services?, or did you just fix a typo user message?

Scenario B, you are importing a 3rd party service such as Payment Service with a version 3.2.0, you notice two new releases with versions; 3.2.1 and 4.0.0. What is the difference between the two? What is the magnitude of change in the 3rd party service? Should you be worried when upgrading to this new version or not?

Given scenario A and B, the question we have here is, how can you assign a label of a certain service to keep track of its changes and its current state? Another valid question is, how can you tell of a certain software has released multiple bug fixes? That’s where Semantic Versioning comes in.

What is Semantic Versioning?

It is a solution to a problem known in software management called “Dependency Hell” – The bigger the system, the more packages needed to integrate in the system. It is a set of rules to define how software should be versioned and how it should increment based on the code changes.

MAJOR.MINOR.PATCH

  1. MAJOR version - when you make incompatible API changes.
  2. MINOR version - when you add functionality in a backward-compatible manner.
  3. PATCH version - when you make backward-compatible bug fixes.

Full Documentation in (https://semver.org/)

In this post I will show you to use a fully automated semantic versioning tool called (semantic-release) in a GitLab pipeline.

semantic-release tutorial

semantic-release GitHub

How to setup Release Stage in your GitLab pipeline

  1. Generate a GitLab access token and set it as an environment variable to your selected repository, and name it either as (GL_TOKEN or GITLAB_TOKEN).
  2. Create .releaserc file in either format (JSON / YAML / YML) and include the plugin(s) needed based on your preferences.

Here is a sample .releaserc.json file you can start with:

{
  "branches": ["main"],
  "prepare": false,
  "plugins": [
    ["@semantic-release/commit-analyzer",
      { 
        "releaseRules": [
          {"type": "style", "release": "patch"},
          {"type": "breaking", "release": "major"} 
        ],
      }
    ],
  "@semantic-release/release-notes-generator",
  "@semantic-release/gitlab"
  ]
}

Notice in the sample file above, the commit-analyzer plugin has two custom releaseRules which checks for a new rlease on the main branch based on a message convention:

type(scope): message

For example, the following commit message “style: repositioned the navbar” will trigger a PATCH release while the following commit message “breaking: deprecated the promocode API” will trigger a MAJOR release. If no rules match, it will search for the default releaseRules which you can find in semantic-release GitHub repo.

Create a .gitlab-ci.yml file and add the following stage:

stages:
- Release

semantic-release:
  stage: Release
  variables:
    GITLAB_TOKEN: ${GITLAB_TOKEN}
    GIT_AUTHOR_NAME: ${GITLAB_USER_NAME}
    GIT_AUTHOR_EMAIL: ${GITLAB_USER_EMAIL}
    GIT_COMMITTER_NAME: ${GITLAB_USER_NAME}
    GIT_COMMITTER_EMAIL: ${GITLAB_USER_EMAIL}

  before_script:
    - export PATH=$PATH:/usr/local/bin/npm
    - npm install --loglevel=error --save-dev semantic-release @semantic-release/gitlab-config

  script:
    - npx semantic-release -e @semantic-release/gitlab-config
  only: 
    - main

And there you go! You have an automated semantic versioning stage in your GitLab CI pipeline.