Automate software versioning, release and changelog with CI/CD pipelines


5 min read

Automate software versioning, release and changelog with CI/CD pipelines

What is software release?

Is the final version of software released to the end-users after further enhancements and bugs fixes

What is software versioning ?

Software versioning is the process of numbering different releases of a particular software program for both internal use and release designation.

All released changes with version number are recorded in specific order into a file know as changelog

Common method of versioning

  1. Semantic versioning - this is common method used , where by it consist three groups of numbers Major, Minor and Patch ,semantic version has this structure MAJOR.MINOR.PATCH
    1. PATCH is counter for bug fixes
    2. MINOR is counter for functionalities
    3. MAJOR is a counter for product changes
  2. Date of release - The software version number is the date of the release example UBUNTU 22.04
  3. Sequential numbering - assigned a unique identifier that consists of one or more sequences of numbers or letters used to convey the significance of changes between releases. The level of significance are classified by changes from the previous release.

Why do we need of versioning

It allows programmers and all people to know when changes have been made and track changes enforced in the software. At the same time, it enables potential customers to be acquainted with new releases and recognize the updated versions.

How ClickPesa implement software versioning, release and changelog

As ClickPesa we like to automate our process in order to increase our productivity and smooth things by removing human errors, we decided to let pipeline handle versioning process , let see how we manage to achieve this

For branch or feature which are ready to be deployed in production means PR will be created to Production and merged to master , pipeline will run with master Branch

Actually we are using Bitbucket so pipeline is set as

     - step:
         name: bump version

We added step for bump version which will do the following

  1. First retrieve all merged branch commit as save them it bash variable as

  1. We use gulp which is javascript package for streaming task runner that lets developers automate many development tasks , so after having RELEASE_DETAILS we passed them into Gulpfile which handle all tasks

     - gulp autoversion --t "${RELEASE_DETAILS}"
  1. On gulpfile.js

    const gulp = require('gulp');
    const runSequence = require('gulp4-run-sequence');
    const jsonModify = require('gulp-json-modify');
    const gap = require('gulp-append-prepend');
    gulp.task('autoVersion', async function () { // Run tasks sequentially
     runSequence('upVersion', 'saveVersion', 'updateChangeLog');
    gulp.task('upVersion', async function () {
     let ver = require('./package.json').version; //version defined in the package.json file
     console.log('current version: ', ver);
     let splitString = ver.split('.', 3);
     let majorVersion = splitString[0].split('"', 1);
     let minorVersion = splitString[1].split('"', 1);
     let patchVersion = splitString[2].split('"', 1);
     let patchNumber = Number(patchVersion[0]);
     let minorNumber = Number(minorVersion[0]);
     let majorNumber = Number(majorVersion[0]);
     if (patchNumber < 9) {
       splitString[2] = String(patchNumber);
     } else {
       splitString[2] = String(0);
       if (minorNumber < 9) {
         splitString[1] = String(minorNumber);
       } else {
         splitString[1] = String(0);
         splitString[0] = String(majorNumber);
     process.env.VERSION = splitString.join('.');
     console.log(' new version : ', process.env.VERSION);
    gulp.task('saveVersion', async function () { // saving new version number into package.json
     return gulp
           key: 'version',
           value: process.env.VERSION,
    gulp.task('updateChangeLog', async function () { // add changes into changelog file
     let messages = process.argv[4];
     messages = messages.replace('>', '*');
     if (messages != '') {
         .pipe(gap.prependText(`# v${process.env.VERSION}`))
     } else {
       console.log('no commit messages');

As we you can see we call runSequence which call other three tasks upVersion, saveVersion and updateChangeLog

As we can see our trigger tasks which is autoVersioncalled from a pipeline and inside of it there is runSequence function which call three tasks upVersion, saveVersion and updateChangeLog in sequence

Let's take a look on each task
  1. start with upVersion task
    1. What it does is looking into package.json file and get the version number , depend on your way of versioning it can be Semantic or Sequential or other custom way what needed it you will do your magic of bumping up version number and save the new value in example above new version number is saved on this process.env.VERSION
  2. Then we are going to update Package.json file with new version number you can see on code above on this task saveVersion
  3. Final task is updateChangeLog file
    1. Remember we passed RELEASE_DETAILSon step 1 which has commits of branch which is merged all details are extracted and saved on variable message you can see on step above where version and commit are added on change log file
    2. This is file we have with our version 0.0.1 changes
# v0.0.1

* ๐Ÿ‘Œ IMPROVE: create PR for each branch from staging to master 
* ๐Ÿ› FIX:  Inject winston logger to the providers 
* ๐Ÿ› FIX: import statements path 
* ๐Ÿ‘Œ IMPROVE: Add wallet transfer transaction job before the payout 
* ๐Ÿ‘Œ IMPROVE: Use one queue to process different jobs 
* ๐Ÿ“ฆ NEW: Implement a queue for wallet payouts 
* ๐Ÿ‘Œ IMPROVE: add dependencies for gulp 
* ๐Ÿ› FIX: name of step for bump version 
* ๐Ÿค– TEST: update changelog file and bump version

Wow! Remember we modified two files package.json by updating version number and by adding changes.

Back on the pipeline process all change files will be committed and pushed as per your setup develop or master branch.