I use tags in git to version my code and push changes to production. Tagging is fairly simple using the git cli, but I’ve made it even easier using a simple Grunt task.

Git tags are what we use to push code to production. Whenever a new tag is pushed to origin master, our CI server (Jenkins) does a build and then pushes that new code to production. The act of tagging is fairly trivial, but it is a few different commands. Typically one would do the following:

[sourcecode]

$ git describe —tags —abbrev=0

1.200.69

$ git tag 1.200.70

$ git push origin —tags

[/sourcecode]

Unfortunately, thats a tiny bit of a pain and still a manual process prone to error. Someone recently pushed 3 different tags to our master branch of “list”, “describe”, and “show”. Obviously a mistake when using git commands but still, these random tags have actually proved to be a much bigger pain in the rear than they should!

We already had Grunt setup for this particular project because we were using a simple build command to compile our SASS files. Including a new task specifically for git tagging seemed a no-brainer. Now to push a tag we simply execute:

[sourcecode]

$ grunt tag

[/sourcecode]

By default it will do a :patch, but additional options can be included for minor and major i.e.:

[sourcecode]

$ grunt tag:minor

[/sourcecode]

The code is fairly straightforward:

[sourcecode lang=”javascript”]

// jshint node:true

‘use strict’;

var semver = require(‘semver’),

exec = require(‘exec’);

module.exports = function(grunt) {

‘use strict’;

require(‘load-grunt-tasks’)(grunt);

var gitVersion;

grunt.initConfig({

pkg: grunt.file.readJSON(‘package.json’),

shell: {

options: {

stdout: true,

stderr: true

},

tag: {

command: [

‘git tag <%= grunt.option(“tag”) %>’,

‘git push origin –tags’

].join(‘ && ‘)

}

}

});

// Auto increment GIT TAG and PUSH to ORIGIN

grunt.registerTask(‘tag:patch’, [‘tag’]);

grunt.registerTask(‘tag:minor’, function() {

grunt.option(‘tagType’, ‘minor’);

grunt.task.run([‘tag’]);

});

grunt.registerTask(‘tag:major’, function() {

grunt.option(‘tagType’, ‘major’);

grunt.task.run([‘tag’]);

});

grunt.registerTask(‘tag’, function() {

if (grunt.option(‘tagType’) !== ‘major’ &&

grunt.option(‘tagType’) !== ‘minor’) {

grunt.option(‘tagType’, ‘patch’);

}

var done = this.async();

exec(‘git describe –tags –abbrev=0’,

function(err, stdout, stderr) {

if (stderr) {

grunt.log.error(stderr);

} else {

gitVersion = semver.inc(

stdout.trim(),

grunt.option(‘tagType’)

);

grunt.log.ok(‘Tagging: ‘ + gitVersion +

‘ (‘ + grunt.option(‘tagType’) + ‘)’);

grunt.option(‘tag’, gitVersion);

grunt.task.run([‘shell:tag’]);

}

done();

}

);

});

};

[/sourcecode]

And the devDependencies in the package.json:

[sourcecode lang=”javascript”]

“devDependencies”: {

“grunt”: “^0.4.4”,

“load-grunt-tasks”: “^0.4.0”,

“semver”: “^2.2.1”,

“exec”: “^0.1.0”,

“grunt-shell”: “^0.6.4”

}

[/sourcecode]

You can download the complete Gruntfile.js from this gist:

https://gist.github.com/jkat98/10117306

Important Note: Some asked why I didn’t just use grunt-bump for this type of solution. The main problem I had was that grunt-bump wants to require the version number stored in the package.json file. If the version (or tag) changes outside of the package.json file then grunt-bump stops working properly. With a shared project repo like this particular one, with many old-school people not using (or refusing to use) Grunt, manual git tags are still pushed daily. The risk of the package.json file being out of sync (version # wise) was too high. This method forcefully uses the actual last git tag to increment.