Monday, February 24, 2014

Development Grunt Tasks in a Heroku Production Environment

We ran into a bit of a conflict between Gruntfile.js and package.json. The package.json file defines the npm packages that are used by the application and by Grunt. However, some of the grunt tasks only make sense to run in a development or test environment, but not in a production environment. Our package.json file was able to make this distinction but our Gruntfile.js wasn't.

Looking at the code, our package.json had the dev packages declared inside devDependencies:

                // In package.json
                ...
                "devDependencies": {
                    "jasmine-node": "1.5.X",
                    "grunt-contrib-jshint": "~0.6.0",
                    ...
                }

But, our Gruntfile.js didn't distinguish between a dev or a production environment. It just declared tasks that always had to be loaded:

                // In Gruntfile.js
                ...
                grunt.loadNpmTasks('grunt-contrib-jshint');
                grunt.loadNpmTasks('grunt-jasmine-node');
                ...

This setup failed when deploying to production because Grunt was trying to load tasks whose modules were not installed because npm doesn't install dev dependencies in production.

We came up with a quick fix to this issue by reading the .env file. Since we are using Heroku, our dev boxes have the .env file, and they define the environment they are running:

                // In .env
                ...
                NODE_ENV=development
                ...

Thus, we wrote some code in Gruntfile.js to read the .env file:

                // In Gruntfile.js
                ...
                function isDevelopmentEnvironment() {
                    return grunt.file.exists('.env') && 
                           grunt.file.read('.env').indexOf('NODE_ENV=development') !== -1;
                }

                if (isDevelopmentEnvironment()) {
                    grunt.loadNpmTasks('grunt-contrib-jshint');
                    grunt.loadNpmTasks('grunt-jasmine-node');
                    ...
                }

And that did the trick, we can now deploy to production and the development Grunt tasks will only load in a development environment.

2 comments:

  1. Great idea! I actually have been doing a lot of Heroku deployment work and I like this solution. The prod dependencies and devDep were a big issue. One other good solution is to use https://www.npmjs.org/package/load-grunt-tasks

    Its nice because you dont have to specify any npm tasks in your grunt file. It will find all modules with grunt- and load them automatically. The only downside is if you use non grunt- prefixed packages.

    ReplyDelete
    Replies
    1. Yes, I think that module would be a good complement to our solution. Thanks!

      Delete