Version bumping the Node.js and Angularjs app releases

As I used to develop apps alone, I did not care much about git collaboration* or README.md or *documentation or some versioning systems. This became my weakness after I started working some projects with other developers. Though I did some reading about git and team collaboration, I could not understand until I become in a team. However, I finally passed the git thing with my colleague's help.

After we have all done writing a bunches of features for our MVP, it is time to let the web see our app for the first time. As a lead developer, I am about to release my app. My colleague proposed me to use a standard visioning system for our app based on Semantic Versioning and the release branches on A successful Git branching model for git workflow.

So, I did some reading on both. And I realised that our app is not complex enough and does not need to apply Semantic Versioning's completely. I think it would be time consuming for our app and we have to leave some "MUST" specification of the Semantic Versioning if we are gonna apply. However, I see no such drawbacks to conclude both of them as our own versioning rule. We will be fine if we pick the best features from both as long as we stick with it.

In this post, I will document the standard versioning system that I use in my projects - currently Node.js API app (Loopback) and Angularjs (with Gulp) and how I gonna apply conveniently using npm and gulp command, for later use for myself.

First we will look into Semantic versioning system's specifications that we can follow and not follow.

Semantic Versioning

Standard version number

MAJOR.MINOR.PATCH

MAJOR - version when incompatible API changes is made

MINOR - version when functionality in a backwards-compatible manner is added

PATCH - version when backwards-compatible bug fixes

Specification (SemVer)

  1. MUST declare a public API
  2. MUST take the form X.Y.Z where X, Y and Z are non-negative integers without leading zeroes. Each element MUST increase numerically.
  3. The version MUST NOT be modified once released. MUST be released as a new version for any modifications.
  4. Major version X
    4.1. zero (0.y.z) for initial development
    4.2. Version 1.0.0 defines the production the public API.
    4.3. MUST be incremented if any backwards incompatible changes are introduced to the public API.
    4.4. MUST reset the minor version to 0 when major version is incremented.
  5. Minor version Y
    5.1. Y MUST Be incremented if new, backwards compatible functionality is introduced to the public API.
    5.2. MUST be incremented if any public API functionality is marked as deprecated.
    5.3. MAY be incremented if new functionality or improvements are introduce within the private code.
    5.4. MUST reset the patch version to 0 when minor version is incremented.
  6. Patch version Z
    6.1. MUST be incremented if backwards compatible bug fixes are introduced.
  7. Pre-release version
    7.1. MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately after the patch version.
    7.2. MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z].
    7.3. MUST NOT be empty identifiers.
    7.4. MUST NOT include leading zeroes in numeric identifiers
    7.5 Pre-release versions have a lower precedence than normal versions.
  8. Build metadata
    8.1. MAY be denoted by appending a plus sign and a series of dot separated identifiers immediately following the patch or pre-release version.
    8.2 MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z].
    8.3. MUST NOT be empty identifiers.
  9. Precedence MUST be calculated by separating the version into major, minor, patch and pre-release identifiers in that order. (where metadata does not figure into precedence)

Release branches with versioning

The only difference with Semantic versioning and versioning used in release branches in this post - A successful Git branching model is that it uses Hotfix instead of PATCH word (However I would like to stick with patch to be more formal :P). Most of them describes in that post is how to release a version, which we are gonna do very soon with npm and gulp.

Implementation in Node.js App

Create a release branch

In order to release an app, we will create a release branch from the develop branch. As it is our first production ready that using standard versioning system, this release will become version 1.0 (rather than 0.x.x as we are not in initial development anymore). However, 1.0 will be the app version that will be displayed on app's website, package.json and bower.json (if applicable):

$ git checkout -b release-1.0 develop

Bump the app version

We will use native npm command to bump version in package.json file. You can see their official documentation at here. As we are gonna release our major version 1.0, make sure the version in package.json start with major version 0.x.x, and type the following command to bump major version. The -m config option will be used by npm as a commit message in a version commit.

Make sure you got a clean working directory unless you can use with -f option to force bumping

$ npm version major -m "Version %s"

If this is a minor release use minor version command. If if the version is about Patch or Hotfix, create a hotfix branch first as described in A successful Git branching model.

Implementation in Angularjs App

Create a release branch

Same as in Node.js app

Bump the app version (with Gulp)

We first need to install gulp-bump, gulp-shell, gulp-tap, gulp-util and gulp-load-plugins plugins and save as development dependencies in our project, to enable bump version from gulp command.

$ npm install --save-dev gulp-bump gulp-bump gulp-shell gulp-tap gulp-util gulp-load-plugins

After that add the following to your gulpfile.js to create gulp bump tasks with Major | Minor | Patch versioning options.

var $ = require('gulp-load-plugins')({lazy: true});

var bumpVersion = function(type) {  
  type = type || 'patch';
  var version = '';
  gulp.src(['./bower.json', './package.json'])
    .pipe($.bump({type: type}))
    .pipe(gulp.dest('./'))
    .pipe($.tap(function(file) {
      version = JSON.parse(file.contents.toString()).version;
    })).on('end', function() {
      var color = $.util.colors;
      gulp.src('')
        .pipe($.shell([
          'git commit --all --message "Version ' + version + '"',
          (type != 'patch' ?
           'git tag --annotate "v' + version +
           '" --message "Version ' + version + '"' : 'true')
        ], {ignoreErrors: false}))
        .pipe($.tap(function() {
          $.util.log(color.green("Version bumped to ") +
            color.yellow(version) + color.green(", don't forget to push!"));
        }));
    });
};

gulp.task('bump', function() { bumpVersion('patch'); });  
gulp.task('bump:patch', function() { bumpVersion('patch'); });  
gulp.task('bump:minor', function() { bumpVersion('minor'); });  
gulp.task('bump:major', function() { bumpVersion('major'); });  

As we are about to bump major version from the initial version 0.x.x, type gulp:bump major. That gulp bump command will do all the blessing as following:

  1. Look up the type of version we are about to change (major | minor | patch)
  2. Change the version number in package.json and bower.json accordingly.
  3. Do a git commit with a message along with its version number
  4. If the bumped version is not Patch type, do a git tag with its vx.x annotation and a message of its version number.
  5. Log the finished tasks or errors if any.

After bumping version

It depends on each individual's git work flow. According to A successful Git branching model, the release branch (whether major and minor or patch (hotfix) versions) must be merged back to both develop and master branches.

Nay Win Myint

A JavaScript full-stack web developer, also interested in Android application development and Graphic design.

United Kingdom