Created
December 3, 2014 23:31
-
-
Save hunterloftis/c19c833e5cc105b4f5fb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
####### Configure environment | |
set -e # fail fast | |
set -o pipefail # don't ignore exit codes when piping output | |
# set -x # enable debugging | |
# Configure directories | |
build_dir=$1 | |
cache_dir=$2 | |
env_dir=$3 | |
bp_dir=$(cd $(dirname $0); cd ..; pwd) | |
heroku_dir=$build_dir/.heroku | |
# Load some convenience functions like status(), echo(), and indent() | |
source $bp_dir/bin/common.sh | |
# Avoid GIT_DIR leak from previous build steps | |
status "Resetting git environment" | |
unset GIT_DIR | |
# Load config vars into environment | |
if [ -d "$env_dir" ]; then | |
status "Exporting config vars to environment" | |
export_env_dir $env_dir | |
fi | |
####### Determine current state | |
# What was the last released node version? | |
if test -f $cache_dir/node/node-version; then | |
previous_node_version=$(cat $cache_dir/node/node-version) | |
else | |
previous_node_version="" | |
fi | |
# Is there a requested semver range for node? | |
semver_range=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .engines.node) | |
node_version="" | |
if [ "$semver_range" == "null" ]; then | |
semver_range="" | |
has_semver=false | |
has_specific_version=false | |
else | |
has_semver=true | |
if [[ "semver_range" =~ "^\d+\.\d+\.\d+$" ]]; then | |
has_specific_version=true | |
node_version=semver_range | |
else | |
has_specific_version=false | |
fi | |
fi | |
# Start by assuming we can't start | |
start_method="" | |
# Does server.js exist? | |
if [ -f $build_dir/server.js ]; then | |
has_server=true | |
start_method="server.js" | |
else | |
has_server=false | |
fi | |
# Can we use npm start (default = node server.js)? | |
npm_start=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .scripts.start) | |
if [ "$npm_start" != "null" ]; then | |
has_start=true | |
start_method="scripts.start" | |
else | |
has_start=false | |
fi | |
# Is a Procfile provided? | |
if test -d $build_dir/Procfile; then | |
has_procfile=true | |
start_method="Procfile" | |
else | |
has_procfile=false | |
fi | |
# By default, we'll build node_modules from package.json | |
node_modules_source="package.json" | |
# Do we have cached node_modules? | |
elif test -d $cache_dir/node/node_modules; then | |
node_modules_cached=true | |
else | |
node_modules_cached=false | |
fi | |
# Does node_modules dir already exist? | |
if test -d $build_dir/node_modules; then | |
node_modules_source="prebuilt" | |
fi | |
####### Provide debugging info and feedback | |
status "Node Buildpack v63" | |
status "Previous node version: ${previous_node_version:none}" | indent | |
status "Requested node version: ${semver_range:missing}" | indent | |
status "App startup method: $start_method" | indent | |
status "node_modules source: $node_modules_source" | indent | |
status "Have node_modules cache: $node_modules_cached" | indent | |
# Abort of starting is impossible | |
if [ "$start_method" == "" ]; then | |
status "Unable to start app: you can fix this by creating any of the following:" | |
status "- A Procfile" | indent | |
status "- A start script in package.json" | indent | |
status "- server.js in your project's root" | indent | |
error "Can't start" | |
fi | |
# Warn about dangerous engines.node ranges | |
if [ "$semver_range" == "" ]; then | |
protip "You should specify a node version in package.json - will query for latest stable." | |
elif [ "$semver_range" == "*" ]; then | |
protip "Avoid using semver ranges like '*' in engines.node" | |
elif [ ${semver_range:0:1} == ">" ]; then | |
protip "Avoid using semver ranges starting with '>' in engines.node" | |
fi | |
# Warn about checked-in node_modules | |
if [ "$node_modules_source" == "prebuilt" ]; then | |
protip "Avoid checking node_modules into source control (see NPM FAQ)" | |
fi | |
####### Vendor in binaries | |
# Resolve non-specific node versions using semver.io | |
if [ ! $has_specific_version ]; then | |
status "Resolving node version $semver_range with semver.io..." | |
node_version=$(curl --silent --get --data-urlencode "range=${semver_range}" https://semver.io/node/resolve) | |
fi | |
# Is this a new version of node? | |
if [ "$previous_node_version" != "" ] && [ "$node_version" != "$previous_node_version" ]; then | |
new_node_version=true | |
else | |
new_node_version=false | |
fi | |
# Create home for node | |
mkdir -p $heroku_dir/node | |
mkdir -p $heroku_dir/temp | |
# Download node from Heroku's S3 mirror of nodejs.org/dist | |
status "Downloading and installing node $node_version..." | |
node_url="http://s3pository.heroku.com/node/v$node_version/node-v$node_version-linux-x64.tar.gz" | |
curl $node_url -s -o - | tar xzf - -C $heroku_dir/temp | |
# Move node (and npm) into .heroku/node and make them executable | |
mv $temp_dir/node-v$node_version-linux-x64 $heroku_dir/node | |
chmod +x $heroku_dir/node/bin/* | |
PATH=$heroku_dir/node/bin:$PATH | |
# Run subsequent commands from the build directory | |
cd $build_dir | |
####### Build the project's dependencies | |
if [ "$node_modules_source" == "prebuilt" ]; then | |
status "Found existing node_modules directory; skipping cache and install" | |
status "Rebuilding any native dependencies" | |
npm rebuild 2>&1 | indent | |
else | |
if $new_node_version; then | |
status "Changing node versions, $previous_node_version => $node_version; skipping cache" | |
npm rebuild 2>&1 | indent | |
elif $node_modules_cached then | |
status "Restoring node_modules directory from cache" | |
cp -r $cache_dir/node/node_modules $build_dir/ | |
status "Pruning cached dependencies not specified in package.json" | |
npm prune 2>&1 | indent | |
fi | |
status "Installing dependencies" | |
npm install --userconfig $build_dir/.npmrc 2>&1 | indent # Make npm output to STDOUT instead of its default STDERR | |
status "Updating dependencies" | |
npm update | |
status "Deduping dependencies" | |
npm dedupe | |
fi | |
####### Ensure a Procfile | |
if [ ! $has_procfile ]; then | |
status "No Procfile found; Adding 'web: npm start' to new Procfile" | |
echo "web: npm start" > $build_dir/Procfile | |
fi | |
####### Create the runtime environment (profile.d) | |
status "Building runtime environment" | |
mkdir -p $build_dir/.profile.d | |
# Add binary locations to the PATH | |
echo "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" > $build_dir/.profile.d/nodejs.sh | |
# Default to NODE_ENV=production | |
echo "export NODE_ENV=${NODE_ENV:production}" >> $build_dir/.profile.d/nodejs.sh | |
####### Clean up | |
status "Cleaning up build artifacts" | |
# Clear temporary files | |
rm -rf $heroku_dir/temp | |
# Pick up after npm | |
rm -rf "$build_dir/.node-gyp" | |
rm -rf "$build_dir/.npm" | |
# Clear and recreate the cache | |
rm -rf $cache_dir/node | |
mkdir -p $cache_dir/node | |
####### Build successful! Store results in cache | |
echo $node_version > $cache_dir/node/node-version | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment