How to deploy node app that uses grunt to heroku
I'm using grunt and also grunt plugins like grunt-contrib-copy
, grunt-contrib-mincss
(that listed as npm dependencies for my application).
Also I don't commit npm_modules
folder and public
folder, where all generated files are. And I can't figure out how to build my app (I have grunt build
command) after deploy and setup my server (it's already looking for public
folder).
I saw some stuff like grunt-heroku-deploy
, but it seems me a bad idea to commit before upload. Maybe there are some gentle decisions... Any thoughts?
Solution 1:
npm has a support for a postinstall
step (among many others) that might be just what you're looking for.
The node.js heroku buildpack runs this command when you push to heroku to resolve build dependencies:
$ npm install --production
https://devcenter.heroku.com/articles/nodejs-support#build-behavior
If you take a look at the npm documentation, you can setup a series of scripts to run either before or after anyone runs npm install
for your package. It's configured in the scripts
property of package.json
. The scripts
property allows to run custom scripts (including grunt
) when certain things happen in a package's lifecycle.
For example, to echo some text and run the grunt
command whenever anyone (including Heroku) runs npm install
, add this to your package.json
:
{
...
"scripts": {
"postinstall": "echo postinstall time; ./node_modules/grunt-cli/bin/grunt <your task name>"
},
...
}
https://npmjs.org/doc/scripts.html
Important caveats:
- You might have to change the path to the grunt binary in the
postinstall
script, check the error output if thegrunt
command doesn't execute. -
grunt
andgrunt-cli
must be listed as adependency
in yourpackage.json
so it gets installed by Heroku. Listing them underdevDependencies
is not sufficient since Heroku won't install those. Also, note that Heroku won't install it as a global package so to execute it on Heroku you're going to have to use a relative path (as it is configured above).
If this doesn't work (you'll probably need to fiddle with the relative paths a bit), then you might want to consider writing your own custom buildpack for Heroku.
Update
As of 0.4, the grunt
package no longer contains the grunt
binary, which is now part of the grunt-cli
package. The answer has been updated to reflect this.
Solution 2:
This looks like it will largely be solved when the Heroku Platorm API slug
and release
features make it into the mainline. At that point, you can build your code locally (or on a ci server), package it up and send it to heroku via an API call and release it from there.
This is still in the beta period and was only announced on December 19, 2013.
https://devcenter.heroku.com/articles/platform-api-deploying-slugs
I was never super happy with how many people seemed ok with checking in your generated code into git or the NPM postinstall hook. :(
Plus from a philosophical stance, doing a build during a release is simply another potential failure point.
Just for fun: Since that's not finalized yet, here's a bash script I threw together you can use for the time being to build your code on a deployment branch, commit it, deploy it to heroku and then remove the deployment branch. (I really am not a fan of bash deployment scripts, so I'm really looking forward to the platform API additions)
#!/bin/bash
set -e
# Delete current deploy branch
git branch -D deploy
# Create new deploy branch based on master
git checkout -b deploy
# Grunt comands to build our site
grunt build:production
# the dist/ directory is in my .gitignore, so forcibly add it
git add -f dist/
git commit -m "Deploying to Heroku"
# Push it up to heroku, the -f ensures that heroku won't complain
git push heroku -f deploy:master
# Switch it back to master
git checkout master
Solution 3:
Grunt (et al.) is a build tool, not (really) something you should be packaging up and running on production. A different approach would be to use Grunt to prepare your project locally (or better on a CI server) before only pushing the built files to Heroku. As already mentioned Heroku will do an npm install
on your app after its pushed which should be enough on its own to finally prepare your app.
I have it set up so that the Grunt derived/built Heroku app lives in a totally separate Git repo to my main app source code repo. So that when I do a grunt deploy
it optimises and copies the relevant files to the Heroku repo, tidies it up (git add -A
etc.) and then git push heroku master
(or whatever).
It seems like a cleaner separation of concerns if your live servers are only responsible for running a pre-built app package.
YMMV of course, and the accepted answer above is totally valid too ... especially on a well understood and stable live environment like Heroku.
Solution 4:
Heroku buildpack works fine for me. Great stuff.