Geofrey Bundala
ClickPesa Engineering Blog
4 min readMay 30, 2022

--

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
    — PATCH
    is counter for bug fixes
    MINOR is counter for functionalities
    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

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 sequentiallyrunSequence('upVersion', 'saveVersion', 'updateChangeLog');});gulp.task('upVersion', async function () {let ver = require('./package.json').version; //version defined in the package.json fileconsole.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) {patchNumber++;splitString[2] = String(patchNumber);} else {splitString[2] = String(0);if (minorNumber < 9) {minorNumber++;splitString[1] = String(minorNumber);} else {splitString[1] = String(0);majorNumber++;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.jsonreturn gulp.src(['./package.json']).pipe(jsonModify({key: 'version',value: process.env.VERSION,}),).pipe(gulp.dest('./'));});gulp.task('updateChangeLog', async function () { // add changes into changelog filelet messages = process.argv[4];console.log(messages);messages = messages.replace('>', '*');console.log(messages);if (messages != '') {gulp.src('./changelog.md').pipe(gap.prependText(messages)).pipe(gap.prependText(`# v${process.env.VERSION}`)).pipe(gulp.dest('./'));} else {console.log('no commit messages');}});

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 RELEASE_DETAILS
  2. 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}"
  3. On gulpfile.js
master:
- step:
name: bump version

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 autoVersion()called 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. Starting with upVersion() task
    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 2. 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 3. This is changelog.md 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 provider👌 IMPROVE: Use one queue to process different jobs🐛 FIX: Add EBUREAU_WALLET_PAYOUT_TRANSACTION_QUEUE initialization📦 NEW: Implement a queue for wallet payouts🤖 TEST: update changelog file and bump version

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

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

--

--