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.