Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
00774e9
Update README.md
flyerhzm Feb 9, 2014
c51dbd1
write a default npmrc
zeke Feb 28, 2014
4486ec5
add a status message about npmrc
zeke Feb 28, 2014
97a5856
Merge pull request #98 from heroku/npm-cert-bug
zeke Feb 28, 2014
61540c1
Merge pull request #12 from xinminlabs/master
mbuchetics Mar 4, 2014
2168fa2
Merge branch 'master' of https://github.com/heroku/heroku-buildpack-n…
mbuchetics Mar 4, 2014
33d25ce
Removed nomnom, added bower, reduced slug size
nknj May 13, 2014
11dc06c
Added .tmp to bulk removal
nknj May 13, 2014
ae2e951
Updated Readme
nknj May 13, 2014
f8cefdc
Increased Readme clarity
nknj May 13, 2014
a8678fb
Escape a few stars in readme
nknj May 13, 2014
97e5985
Spellings
nknj May 13, 2014
883d4d3
Improved clarity in Readme.md
nknj May 13, 2014
b2b21ae
Readme clarity improved
nknj May 13, 2014
928450f
New version of node requires morgan
nknj May 14, 2014
9f7767b
Added $GRUNT_VERBOSE config var for grunt --verbose
nknj May 14, 2014
c6a0c52
prune devDependencies after build
gvelo May 31, 2014
8703ba1
Merge pull request #19 from gvelo/master
mbuchetics Jun 13, 2014
af5ede9
Remove README instructions to enable deprecated heroku labs user-env-…
skeller88 Aug 18, 2014
bd26f21
fixed merge conflict
nknj Aug 30, 2014
a1206ce
no need to delete devDependecies (new parent version prunes for prod)…
nknj Aug 30, 2014
aafa80c
updated readme and fixed a typo in compile
nknj Aug 30, 2014
b13285d
call npm prune again - first time doesnt delete all - npm bug?
nknj Aug 30, 2014
0289b5e
Don't double run grunt heroku
jagregory Sep 24, 2014
72f2139
Merge pull request #1 from jagregory/master
nknj Sep 24, 2014
0a22d90
reverting to older version of sass
nknj Oct 23, 2014
23aee5e
set NODE_ENV to development to allow download dev dependencies
gvelo Dec 19, 2014
99aeced
Merge pull request #22 from gvelo/master
mbuchetics Dec 19, 2014
13b320c
Merge pull request #20 from skeller88/master
mbuchetics Dec 19, 2014
11637b4
unset GIT_DIR to allow of use of commit SHAs in bower.json
coreyjonoliver Dec 30, 2014
0588ecd
Merge pull request #23 from coreyjonoliver/master
mbuchetics Jan 1, 2015
857021a
adding another gruntfile name
Edo78 Feb 5, 2015
1ba4e82
Merge pull request #25 from Edo78/patch-1
mbuchetics Feb 6, 2015
db20026
fixed #2
nknj Mar 11, 2015
8de0e82
removed GRUNT_VERBOSE env var and added custom GRUNT_FLAGS instead
nknj Apr 14, 2015
4efc583
synced with parent fork
nknj May 4, 2015
777d7b1
upgraded gem home for cedar-14 compatibility
nknj May 4, 2015
af5030b
Now compatible for both cedar and cedar-14
nknj May 4, 2015
55872cf
for PR to stephanmelzer
nknj May 4, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 64 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,106 +1,87 @@
Heroku buildpack: Node.js with grunt support
============================================
Heroku buildpack: Yo Angular
============================

Supported Grunt versions: 0.3 and 0.4.
See the Grunt [migration guide](https://github.com/gruntjs/grunt/wiki/Upgrading-from-0.3-to-0.4) if you are upgrading from 0.3.
Parent Repository
-----------------

This is a fork of [Heroku's official Node.js buildpack](https://github.com/heroku/heroku-buildpack-nodejs) with added [Grunt](http://gruntjs.com/) support.
Using this buildpack you do not need to commit the results of your Grunt tasks (e.g. minification and concatination of files), keeping your repository clean.
Refer to the parent repo's [readme](https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt/blob/master/README.md) **First**.

After all the default Node.js and npm build tasks have finished, the buildpack checks if a Gruntfile (`Gruntfile.js`, `Gruntfile.coffee`or `grunt.js`) exists and executes the `heroku` task by running `grunt heroku`. For details about grunt and how to define tasks, check out the [offical documentation](http://gruntjs.com/getting-started). You must add grunt to the npm dependencies in your `package.json` file.
If no Gruntfile exists, the buildpacks simply skips the grunt step and executes like the standard Node.js buildpack.


How it Works
------------

Here's an overview of what this buildpack does:

- Uses the [semver.io](https://semver.io) webservice to find the latest version of node that satisfies the [engines.node semver range](https://npmjs.org/doc/json.html#engines) in your package.json.
- Allows any recent version of node to be used, including [pre-release versions](https://semver.io/node.json).
- Uses an [S3 caching proxy](https://github.com/heroku/s3pository#readme) of nodejs.org for faster downloads of the node binary.
- Discourages use of dangerous semver ranges like `*` and `>0.10`.
- Uses the version of `npm` that comes bundled with `node`.
- Puts `node` and `npm` on the `PATH` so they can be executed with [heroku run](https://devcenter.heroku.com/articles/one-off-dynos#an-example-one-off-dyno).
- Caches the `node_modules` directory across builds for fast deploys.
- Doesn't use the cache if `node_modules` is checked into version control.
- Runs `npm rebuild` if `node_modules` is checked into version control.
- Always runs `npm install` to ensure [npm script hooks](https://npmjs.org/doc/misc/npm-scripts.html) are executed.
- Always runs `npm prune` after restoring cached modules to ensure cleanup of unused dependencies.
- Runs `grunt` if a Gruntfile (`Gruntfile.js`, `Gruntfile.coffee`or `grunt.js`) is found.

For more technical details, see the [heavily-commented compile script](https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt/blob/master/bin/compile).

Usage
-----
Additional Features
-------------------

Create a new app with this buildpack:
- Installs bower apps from a `bower.json` file in the root directory
- Installs compass
- Deletes a few common files that are not need for production by Angular apps to reduce the slug size

heroku create myapp --buildpack https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt.git
### Current files being deleted to reduce slug size:

Or add this buildpack to your current app:
- .bowerrc
- .editorconfig
- .git/
- .gitattributes
- .gitignore
- .jshintrc
- .travis.yml
- .tmp/
- .sass-cache/
- Gruntfile.js
- app/
- bower_components/
- bower.json
- karma-e2e.conf.js
- karma.conf.js
- package.json
- test/

heroku config:add BUILDPACK_URL=https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt.git
How to make your angular app work with this buildpack
-----------------------------------------------------

Set the `NODE_ENV` environment variable (e.g. `development` or `production`):
This buildpack uses a task in your `Gruntfile` to build the contents of your `app` folder into an optimized `dist` folder. This is the same thing the command `grunt` does in your `yo angular` app. All of this takes place on the heroku server, so all you have to do is push your app's source code to heroku with the standard `.gitignore` file . There is no need for you to commit the `dist` folder or any other pre-built files like a lot of the other online guides suggest. This buildpack installs all the `node_modules`, `bower_components` and uses `grunt` to build your app.

heroku config:set NODE_ENV=production
### 4 Step Guide

Create your Node.js app and add a Gruntfile named `Gruntfile.js` (or `Gruntfile.coffee` if you want to use CoffeeScript, or `grunt.js` if you are using Grunt 0.3) with a `heroku` task:
#### Step 1: Set up your `node.js` web server and add a `Procfile` command

grunt.registerTask('heroku:development', 'clean less mincss');

or
I use `express`, `gzippo` and `morgan` with the `Procfile` command `web: node web.js`. But you can use anything. Here is a sample of my `web.js` file:

grunt.registerTask('heroku:production', 'clean less mincss uglify');
```js
'use strict';

Don't forget to add grunt to your dependencies in `package.json`. If your grunt tasks depend on other pre-defined tasks make sure to add these dependencies as well:
var gzippo = require('gzippo');
var express = require('express');
var logger = require('morgan');
var nodeApp = express();

"dependencies": {
...
"grunt": "*",
"grunt-contrib": "*",
"less": "*"
}
nodeApp.use(logger('dev'));
nodeApp.use(gzippo.staticGzip('' + __dirname + '/dist'));
nodeApp.listen(process.env.PORT || 5000);
```

Push to heroku
#### Step 2: Add a heroku task to your Gruntfile

git push heroku master
...
-----> Heroku receiving push
-----> Fetching custom buildpack... done
-----> Node.js app detected
-----> Resolving engine versions
Using Node.js version: 0.8.2
Using npm version: 1.1.41
-----> Fetching Node.js binaries
-----> Vendoring node into slug
-----> Installing dependencies with npm
...
Dependencies installed
-----> Building runtime environment
-----> Found gruntfile, running grunt heroku task
Running "heroku" task
...
-----> Discovering process types
```js
grunt.registerTask('heroku', ['build']);
```

Debugging
---------
This is the task that will be run by heroku on the heroku server. I have just included the `build` task here (referring to the standard `build` task created by the `yo angular`) but you could add the `newer:jshint` and the `test` task too.

npm can be run with a verbose flag to help debugging if something fails when installing the dependencies.
You can also set the environment variable `NODE_ENV` on your heroku server if you want the buildpack to call a `heroku:$NODE_ENV` task. In that case, here is how the task will look like in your `Gruntfile`:

* if the `VERBOSE` environment variable is set, npm is always run with verbose logging.
* if `BUILDPACK_RETRY_VERBOSE` is set, npm is relaunched in verbose mode if npm failed.
```js
grunt.registerTask('heroku', function (target) {
// use the target to do whatever, for example:
grunt.task.run('build:' + target);
});
```

Thanks to [mackwic](https://github.com/mackwic) for these extensions.
#### Step 3: Set this buildpack as your heroku app's buildpack

Further Information
-------------------
```
heroku config:set BUILDPACK_URL=https://github.com/nknj/heroku-buildpack-yo-angular.git
```

For more information about using Node.js and buildpacks on Heroku, see these Dev Center articles:
#### Step 4: Deploy

- [Heroku Node.js Support](https://devcenter.heroku.com/articles/nodejs-support)
- [Getting Started with Node.js on Heroku](https://devcenter.heroku.com/articles/nodejs)
- [Buildpacks](https://devcenter.heroku.com/articles/buildpacks)
- [Buildpack API](https://devcenter.heroku.com/articles/buildpack-api)
- [Grunt: a task-based command line build tool for JavaScript projects](http://gruntjs.com/)
```
git push heroku master
```
90 changes: 76 additions & 14 deletions bin/compile
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,43 @@ PATH=$PATH:$build_dir/vendor/node/bin
# Run subsequent node/npm commands from the build path
cd $build_dir

is_gruntfile_present= [ -f $build_dir/grunt.js ] || [ -f $build_dir/Gruntfile.js ] || [ -f $build_dir/Gruntfile.coffee ];
if $is_gruntfile_present; then
status "Found Gruntfile"
status "Augmenting package.json with grunt and grunt-cli"
# Do this because we want to cache it with other node_modules. Otherwise it will be pruned.
# It doesn't matter if grunt and/or grunt-cli is already there
awk '{ if ( $0 ~ /"dependencies".*/ ) {
printf "%s\n%s\n%s\n", $0, "\"grunt-cli\": \"*\",", "\"grunt\": \"*\",";
} else {
print $0;
}
}' package.json > tmp-package.json;
cp tmp-package.json package.json;
rm tmp-package.json;
else
status "No Gruntfile (grunt.js, Gruntfile.js, Gruntfile.coffee) found"
fi


# If node_modules directory is checked into source control then
# rebuild any native deps. Otherwise, restore from the build cache.
if test -d $build_dir/node_modules; then
status "Found existing node_modules directory; skipping cache"
status "Rebuilding any native dependencies"
npm rebuild 2>&1 | indent
elif test -d $cache_dir/node_modules; then
elif test -d $cache_dir/node/node_modules; then
status "Restoring node_modules directory from cache"
cp -r $cache_dir/node_modules $build_dir/
cp -r $cache_dir/node/node_modules $build_dir/

status "Pruning cached dependencies not specified in package.json"
npm prune 2>&1 | indent

if test -f $cache_dir/node/.heroku/node-version && [ $(cat $cache_dir/node/.heroku/node-version) != "$node_version" ]; then
status "Node version changed since last build; rebuilding dependencies"
npm rebuild 2>&1 | indent
fi

fi

# Scope config var availability only to `npm install`
Expand All @@ -81,10 +106,26 @@ fi
npm install --userconfig $build_dir/.npmrc --production 2>&1 | indent
)

status "Caching node_modules directory for future builds"
rm -rf $cache_dir/node_modules
mkdir -p $cache_dir
test -d $build_dir/node_modules && cp -r $build_dir/node_modules $cache_dir/
# Persist goodies like node-version in the slug
mkdir -p $build_dir/.heroku

# Save resolved node version in the slug for later reference
echo $node_version > $build_dir/.heroku/node-version

# Purge node-related cached content, being careful not to purge the top-level
# cache, for the sake of heroku-buildpack-multi apps.
rm -rf $cache_dir/node_modules # (for apps still on the older caching strategy)
rm -rf $cache_dir/node
mkdir -p $cache_dir/node

# If app has a node_modules directory, cache it.
if test -d $build_dir/node_modules; then
status "Caching node_modules directory for future builds"
cp -r $build_dir/node_modules $cache_dir/node
fi

# Copy goodies to the cache
cp -r $build_dir/.heroku $cache_dir/node

status "Cleaning up node-gyp and npm artifacts"
rm -rf "$build_dir/.node-gyp"
Expand All @@ -105,27 +146,48 @@ if [ ! -e $build_dir/Procfile ]; then
fi
fi

# Update the PATH
# install compass
status "Installing Compass"
if [ "$STACK" == "cedar-14" ]; then
export GEM_HOME=$cache_dir/ruby/.gem/ruby/2.2.0
else
export GEM_HOME=$cache_dir/ruby/.gem/ruby/1.9.1
fi
PATH="$GEM_HOME/bin:$PATH"
if test -d $cache_dir/ruby/.gem; then
status "Restoring ruby gems directory from cache"
cp -r $cache_dir/ruby/.gem $build_dir
HOME=$build_dir gem update compass --user-install --no-rdoc --no-ri
else
HOME=$build_dir gem install compass --user-install --no-rdoc --no-ri
fi

# cache ruby gems compass
rm -rf $cache_dir/ruby
mkdir -p $cache_dir/ruby

# If app has a gems directory, cache it.
if test -d $build_dir/.gem; then
status "Caching ruby gems directory for future builds"
cp -r $build_dir/.gem $cache_dir/ruby
fi

status "Building runtime environment"
mkdir -p $build_dir/.profile.d
echo "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" > $build_dir/.profile.d/nodejs.sh
echo "export PATH=\"\$HOME/.gem/ruby/1.9.1/bin:\$PATH\"" > $build_dir/.profile.d/ruby.sh

# Check and run Grunt
(
if [ -f $build_dir/grunt.js ] || [ -f $build_dir/Gruntfile.js ] || [ -f $build_dir/Gruntfile.coffee ]; then
if $is_gruntfile_present; then
# get the env vars
if [ -d "$env_dir" ]; then
status "Exporting config vars to environment"
export_env_dir $env_dir
fi

# make sure that grunt and grunt-cli are installed locally
npm install grunt-cli
npm install grunt
echo "-----> Found Gruntfile, running grunt heroku:$NODE_ENV task"
status "Running grunt heroku:$NODE_ENV task"
$build_dir/node_modules/.bin/grunt heroku:$NODE_ENV
else
echo "-----> No Gruntfile (grunt.js, Gruntfile.js, Gruntfile.coffee) found"
fi
)

Expand Down