So you’ve got a cool node app written in Coffeescript and you want to deploy it to Heroku?
Seems like it should be easy these days, right?
Not so much. Well, let’s be honest, we’re in the future, you don’t have to manually manage servers, etc. (Thank you Heroku!) but deploying a Coffeescript app is still no small task as there are a bunch of gotchas. Hopefully this will help.
The following instructions assume you have the following rough structure to your application:
/ |-- package.json |-- Procfile +-- src |-- app.coffee
The general idea here is that your app source .coffee files will live in the
/src directory and when they’re pushed to Heroku, they’ll magically
be compiled into the
/target directory as js files.
This has the nice fringe benefit as it means this compilation from coffee to js happens just once with each push.
So after pushing your app will look like this on the Heroku servers:
/ |-- package.json |-- Procfile +-- src | |-- app.coffee +-- target |-- app.js
target/app.js is the compiled version of
Heroku has things called Buildpacks which are a bundle of scripts to be run when code is pushed to Heroku. They have official Buildpacks for Node, Rails, and the other most common frameworks, but none for Coffeescript as of the time of this writing.
To add this buildpack to a new node app you can use the following command to create the app:
$ heroku create --buildpack https://github.com/aergonaut/heroku-buildpack-coffeescript.git
You can also add it to an existing app:
$ heroku config:set BUILDPACK_URL=https://github.com/aergonaut/heroku-buildpack-coffeescript.git
You will have to make sure your Procfile is properly configured for this as well.
It should contain the following:
web: node target/app.js
Note how it’s hitting
target/app.js which will be the compiled copy of your app.
Even though you may have an empty target directory on your local machine, it will get populated with the compiled app code on push to Heroku.
During development, you can run your code locally without compilation if you’d like:
$ coffee src/app.coffee
If you want to do this with Foreman it’s easy to create a Procfile.dev and tell Foreman to use it:
$ web: coffee src/app.coffee
Then to run it:
$ foreman start -f Procfile.dev
You can also compile the code manually, just as the Buildpack will on a push to Heroku:
$ coffee --compile --bare --output target src
My favorite approach is to have all of this happen automagically, with some
linting as well using Grunt Grunt to watch for changes
and compile everything in the
/src directory into the
First, if you haven’t already, install the Grunt dependencies:
$ npm install grunt grunt-cli grunt-contrib-coffee grunt-coffeelint --save-dev
Then create or edit your Gruntfile:
module.exports = (grunt) -> grunt.initConfig coffee: compile: files: [ expand: true cwd: 'src' src: '**/*.coffee' dest: 'target' ext: '.js' ] coffeelint: app: 'src/**/*.coffee' watch: files: ['Gruntfile.coffee', 'src/**/*.coffee'] tasks: ['coffeelint', 'coffee'] grunt.loadNpmTasks 'grunt-coffeelint' grunt.loadNpmTasks 'grunt-contrib-coffee' grunt.loadNpmTasks 'grunt-contrib-watch' grunt.registerTask 'default', ['watch']
Then run it:
Not only will that watch the directory for changes and compile it whenever a file is written, but it will also lint the files! Then the app can be run:
$ node target/app.js
Or, since this is just the way it will look when pushed to Heroku, you can use Foreman with the normal Procfile to run it:
$ foreman start
Note how under the coffee task I have
files: [ expand: true cwd: 'src' src: '**/*.coffee' dest: 'target' ext: '.js' ]
This basically instructs Grunt to recurse through every subdirectory of
find each .coffee file, and compile it to create a .js doppelganger under
target/config/db.js and so on.
If you’re going to do this local auto-compilation, I suggest adding
your .gitignore file. No need for that to get committed to your repo!
Of course this is all well and good, but until you actually push it to Heroku it isn’t doing you much good!
$ git push heroku master
You should see something like:
Counting objects: 11, done. Delta compression using up to 8 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (9/9), 883 bytes, done. Total 9 (delta 3), reused 0 (delta 0) -----> Fetching custom git buildpack... done -----> Coffeescript app detected -----> Resolving engine versions Using Node.js version: 0.8.25 Using npm version: 1.1.65 -----> Fetching Node.js binaries -----> Vendoring node into slug -----> Installing dependencies with npm ... Dependencies installed -----> Compiling coffee source Source compiled to target -----> Building runtime environment -----> Discovering process types Procfile declares types -> web
Note: I replaced all the npm dependency installation as … above for brevity
Note how it says
Coffeescript app detected
This is due to the Buildpack referenced above. If it finds
.coffee files in the
src directory, it recognizes it as a Coffeescript app.
After it has installed the dependencies, including the coffee-script module, it
will compile the coffee source from
target/ and start your app.