Getting oomph out of Grunt

By James Hiscock

One year ago

I presented a talk about the Census Explorer we made for SBS

python extract-jade.py
# Manual copy pasting
./combine.sh
uglify js/combined.js > js/minified.js
# zip up and email the files to the client

Evolution

Over time our build processes has evolved.

  • New technologies
  • New requirements
  • Laziness

Javascript Build

We used AMD for a long time

r.js -o build.js
r.js -o build-ie.js

And recently swapped to browserify

browserify main.js -t coffeeify -o bundle.js

Pushing to production

Changes almost project to project.

  • Uploading manually to S3
  • Uploading manually to FTP
  • git pull on server
  • git aws.push, git push heroku master, etc.

What's the problem

We waste time doing repetitive tasks that a computer could do faster and better. Also context-switching when projects are different.


  • Building
  • Pushing new versions to production
  • Development tasks
  • Project specific tasks

Source: XKCD 1205.

Solutions

Don't be afraid, there are solutions.

  • Shell scripts
  • make
  • package.json scripts: {}
  • or...

One project

{
    "scripts": {
      "start": "beefy src/app/bootstrap.coffee:bundle.js --live -- -t coffeeify -t simple-jadeify -t ractify",
      "bundle": "browserify src/app/bootstrap.coffee -t coffeeify -t simple-jadeify -t ractify > bundle.js",
      "start-server": "POLL=10000 node app/server.js",
      "app": "mkdir -p dist/{app,data}; npm run bundle && npm run stylus && (rm dist/app/app.nw||true) && npm run zip && npm run copier",
      "copier": "cp ./vendor/nw/* ./dist/app/ && npm run osx-copy && cp -r ./app/data/ ./dist/data/ && cp ./{app,dist}/start.bat && cp ./{app,dist}/start.sh",
      "osx-copy": "cp -r ./vendor/nw-osx.app ./dist/app/ && cp ./dist/app/app.nw ./dist/app/nw-osx.app/Contents/Resources/app.nw",
      "zip": "(find node_modules src app | zip dist/app/app.nw -@) && zip dist/app/app.nw *.js *.json *.html *.css",
      "osx": "open dist/app/app.nw --args $(pwd)/dist/app/app.nw $(pwd)/dist/data",
      "lite": "POLL=60000 node app/server.js",
      "stylus": "stylus -o . src/styl/main.styl"
    }
}

Grunt!

What is Grunt?

Self-described task runner.

You define tasks that you want to automate.

Use the command line tool to run those tasks.

grunt watch
grunt compass:dev
grunt browserify:dev
grunt dev
grunt test
grunt publish

Installing

http://gruntjs.com has an excellent guide on the specifics of how to install.

Using Grunt

  • Make a file Gruntfile.js (or .coffee) in your package root.
  • Add a package.json if you haven't already.
  • npm install grunt --save-dev
  • Add the following code to your gruntfile:
module.exports = function(grunt) {
    grunt.initConfig({})
    
    grunt.registerTask('my-first-task', function() {
        console.log('Hello, world!')
    })
}
$ grunt my-first-task
Hello, world!

Using Grunt continued

module.exports = function(grunt) {
    grunt.initConfig({
        jade: {
            compile: {
                files: {
                    'index.html': 'index.jade'
                }
            }
        }
    })
    
    grunt.loadNpmTasks('grunt-contrib-jade');
}
$ grunt jade:compile

Demo time