As part of our recent work on backbone.hypermedia at Linn I’ve spent some time familiarising myself with Grunt. With very little effort - we built a workflow which enables us to publish to npm, NuGet and Bower with a single command. In this post I describe how we arrived at this solution in the hope that it may prove useful to others.

This post assumes you are familiar with the basics of Grunt and npm. If not, you should probably read this and this first.

backbone.hypermedia

The library we are publishing is backbone.hypermedia - a Backbone plugin which provides facilities for interacting with a hypermedia API.

The library is fairly small, and has a handful of dependencies:

  • Backbone
  • jQuery
  • underscore

Publishing to NuGet

NuGet is a package manager for .NET. At Linn we develop our HTTP services using C# - so publishing to NuGet makes sense because we already manage other dependencies through NuGet.

grunt-nuget is a Grunt plugin which provides tasks for creating and publishing a package to NuGet. Firstly we install the plugin as a development dependency:

npm install grunt-nuget --save-dev

Then we load the tasks in Gruntfile.js:

grunt.loadNpmTasks('grunt-nuget');

And configure them as follows:

grunt.initConfig({
	pkg: grunt.file.readJSON('package.json'),

	nugetpack: {
    	dist: {
        	src: 'nuget/backbone.hypermedia.nuspec',
            dest: 'nuget/',
            options: {
            	version: '<%= pkg.version %>'
            }
        }
    },

	nugetpush: {
    	dist: {
        	src: 'nuget/*.nupkg'
        }
    }

grunt.file.readJSON loads the contents of package.json into a variable pkg which we subsequently refer to within a Grunt template to pass the version to the nugetpack task. The nugetpack task generates a .nupkg file from a .nuspec manifest. You can see the contents of our .nuspec file on GitHub.

Once the package file has been created, nugetpush takes care of publishing the package to the NuGet repository. Prior to executing this task for the first time you must declare your NuGet API key by executing:

grunt nugetkey --key=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

You only need to do this once.

We then defined an alias in Gruntfile.js to perform both tasks in sequence:

grunt.registerTask('publish', ['nugetpack', 'nugetpush']);

All credit for implementing the NuGet publishing goes to Henry Wilson.

Publishing to npm

npm is a package manager for Node.js. I initially introduced npm as a means of implementing CI through Travis CI. However once I became familiar with Grunt, the ease at which we could automate other tasks became clear.

Once we had automated publishing to NuGet, we felt that it made sense to publish the package to npm as well, since this is a JavaScript library and we were already using npm for our own development dependencies.

With an appropriately populated package.json, publishing to npm is as simple as executing:

npm publish .

So at this point, our workflow for releasing a new version was:

grunt publish
npm publish .

But we were keen to wrap the two steps together to make it super simple, and we also wanted to automate the cleanup of old .nupkg files.

My initial plan was to use grunt-shell to execute the commands one at a time. Then I discovered grunt-release which automates various steps, including publishing to npm, and automatically incrementing the version number in package.json. This was appealing as I had already forgotten to increment the version at least once when attempting to publish a new release.

As before, we install the plugin:

npm install grunt-release --save-dev

Load the tasks:

grunt.loadNpmTasks('grunt-release');

And configure them:

release: {
	options: {
    	bump: false,
        commitMessage: 'Release <%= version %>'
	}
}

You might be asking yourself why I’m setting bump to false here. Well, it turns out that unfortunately grunt-release does not provide a facility (that I’m aware of) to reload the pkg variable after the version has been bumped. Since we rely on passing this version to our nugetpack task, this presents a problem - as the version would be incorrect. I’ve opened an issue to suggest adding this feature.

I then found grunt-bump, which is a similar plugin which does support reloading config variables, but does not support publishing to npm (also suggested). The solution I came up with was to use grunt-bump to increment the version, and grunt-release to publish to npm. This way the pkg variable is updated and the nugetpack task works as intended.

First install grunt-bump:

npm install grunt-bump --save-dev

Then load the task into Gruntfile.js:

grunt.loadNpmTasks('grunt-bump');

And configure it to bump the version and reload the pkg variable:

bump: {
	options: {
                updateConfigs: ['pkg'],
                commit: false,
                createTag: false,
                push: false
            }
        }

We use grunt-contrib-clean to clean up the old .nupkg files.

grunt-bump supports three different task targets to facilitate bumping the major, minor or patch release numbers. To make use of this, we enhanced our publish task alias as follows:

grunt.registerTask('publish', ['publish:patch']);
grunt.registerTask('publish:patch', ['clean', 'bump:patch', 'release', 'nugetpack', 'nugetpush']);
grunt.registerTask('publish:minor', ['clean', 'bump:minor', 'release', 'nugetpack', 'nugetpush']);
grunt.registerTask('publish:major', ['clean', 'bump:major', 'release', 'nugetpack', 'nugetpush']);

So now we can do:

grunt publish

To publish a patch release to NuGet and npm. This is short-hand for grunt publish:patch.

grunt publish:minor

As before, but bumps minor version.

grunt publish:major

As before, but bumps major version.

In addition to publishing the packages, grunt-release also commits the updated package.json and does a git push.

Publishing to Bower

Bower is yet another package manager, specifically aimed at managing packages for front-end development. You can manage dependencies for front-end development using npm, but npm’s nested dependency tree isn’t always appropriate for this. In constrast, Bower requires a flat dependency tree.

Bower is somewhat different to NuGet and npm in that there is no package repository as such. Instead, a Bower package is simply an alias which points to a git endpoint.

You can install Bower globally using:

npm install -g bower

You must then add a bower.json manifest to the root of your repo. You can create one interactively using bower init. Bower will pick up versions from semver git tags on your repo. Conveniently grunt-release already takes care of creating such tags as well as pushing them. So with the git tags in place, publishing to Bower is simply a case of executing a one off command to register the package alias:

bower register <my-package-name> <git-endpoint>

And that’s it. So now our workflow for releasing to three different package managers simultaneously is as simple as typing grunt publish.