Commit 1dfe0f61 authored by Hunter Loftis's avatar Hunter Loftis

refactor to clean up compile

parent cc4cf452
...@@ -15,9 +15,10 @@ heroku_dir=$build_dir/.heroku ...@@ -15,9 +15,10 @@ heroku_dir=$build_dir/.heroku
mkdir -p $heroku_dir/node mkdir -p $heroku_dir/node
warnings=$(mktemp) warnings=$(mktemp)
# Load some convenience functions like status(), echo(), and indent() # Load dependencies
source $bp_dir/bin/common.sh source $bp_dir/lib/common.sh
source $bp_dir/bin/warnings.sh source $bp_dir/lib/build.sh
source $bp_dir/lib/warnings.sh
# Avoid GIT_DIR leak from previous build steps # Avoid GIT_DIR leak from previous build steps
unset GIT_DIR unset GIT_DIR
...@@ -32,44 +33,15 @@ export_env_dir $env_dir ...@@ -32,44 +33,15 @@ export_env_dir $env_dir
####### Determine current state ####### Determine current state
# What's the requested semver range for node? node_engine=$(read_json "$build_dir/package.json" ".engines.node")
node_engine=$(package_json ".engines.node")
node_previous=$(file_contents "$cache_dir/node/node-version") node_previous=$(file_contents "$cache_dir/node/node-version")
npm_engine=$(read_json "$build_dir/package.json" ".engines.npm")
# What's the requested semver range for npm?
npm_engine=$(package_json ".engines.npm")
npm_previous=$(file_contents "$cache_dir/node/npm-version") npm_previous=$(file_contents "$cache_dir/node/npm-version")
start_method=$(get_start_method "$build_dir")
modules_source=$(get_modules_source "$build_dir")
modules_cached=$(get_modules_cached "$cache_dir")
# How does this app start? show_current_state
if test -f $build_dir/Procfile; then start_method="Procfile"
elif [[ $(package_json ".scripts.start") != "" ]]; then start_method="npm start"
elif [ -f $build_dir/server.js ]; then start_method="server.js"
else start_method=""
fi
# What's the source-of-truth for node_modules?
if test -d $build_dir/node_modules; then modules_source="prebuilt"
elif test -f $build_dir/npm-shrinkwrap.json; then modules_source="npm-shrinkwrap.json"
elif test -f $build_dir/package.json; then modules_source="package.json"
else modules_source=""
fi
# What does our cache look like?
test -d $cache_dir/node/node_modules && modules_cached=true || modules_cached=false
####### Provide debugging info and feedback
echo ""
info "Node engine: ${node_engine:-unspecified}"
info "Npm engine: ${npm_engine:-unspecified}"
info "Start mechanism: ${start_method:-none}"
info "node_modules source: ${modules_source:-none}"
info "node_modules cached: $modules_cached"
echo ""
printenv | grep ^NPM_CONFIG_ | indent
info "NODE_MODULES_CACHE=$NODE_MODULES_CACHE"
warn_node_engine "$node_engine" warn_node_engine "$node_engine"
warn_node_modules "$modules_source" warn_node_modules "$modules_source"
...@@ -77,145 +49,54 @@ warn_node_modules "$modules_source" ...@@ -77,145 +49,54 @@ warn_node_modules "$modules_source"
####### Vendor in binaries ####### Vendor in binaries
head "Installing binaries" head "Installing binaries"
install_node
# Resolve non-specific node versions using semver.herokuapp.com install_npm
if ! [[ "$node_engine" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
info "Resolving node version ${node_engine:-(latest stable)} via semver.io..."
node_engine=$(curl --silent --get --data-urlencode "range=${node_engine}" https://semver.herokuapp.com/node/resolve)
fi
# Download node from Heroku's S3 mirror of nodejs.org/dist
info "Downloading and installing node $node_engine..."
node_url="http://s3pository.heroku.com/node/v$node_engine/node-v$node_engine-linux-x64.tar.gz"
curl $node_url -s -o - | tar xzf - -C /tmp
# Move node (and npm) into .heroku/node and make them executable
mv /tmp/node-v$node_engine-linux-x64/* $heroku_dir/node
chmod +x $heroku_dir/node/bin/*
PATH=$heroku_dir/node/bin:$PATH
# Optionally bootstrap a different npm version
if [ "$npm_engine" != "" ]; then
if ! [[ "$npm_engine" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
info "Resolving npm version ${npm_engine} via semver.io..."
npm_engine=$(curl --silent --get --data-urlencode "range=${npm_engine}" https://semver.herokuapp.com/npm/resolve)
fi
if [[ `npm --version` == "$npm_engine" ]]; then
info "npm `npm --version` already installed with node"
else
info "Downloading and installing npm $npm_engine (replacing version `npm --version`)..."
npm install --quiet -g npm@$npm_engine 2>&1 >/dev/null | indent
fi
fi
warn_old_npm `npm --version`
# Run subsequent commands from the build directory
cd $build_dir
####### Build the project's dependencies ####### Build the project's dependencies
head "Building dependencies" head "Building dependencies"
cd $build_dir
# Did we bust the cache?
if ! $modules_cached; then
use_cache=false
elif ! $NODE_MODULES_CACHE; then
info "Cache disabled with NODE_MODULES_CACHE"
use_cache=false
elif [ "$node_previous" != "" ] && [ "$node_engine" != "$node_previous" ]; then
info "Node version changed ($node_previous => $node_engine); invalidating cache"
use_cache=false
elif [ "$npm_previous" != "" ] && [ "$npm_engine" != "$npm_previous" ]; then
info "Npm version changed ($npm_previous => $npm_engine); invalidating cache"
use_cache=false
else
use_cache=true
fi
if [ "$modules_source" == "" ]; then if [ "$modules_source" == "" ]; then
info "Skipping dependencies (no source for node_modules)" info "Skipping dependencies (no source for node_modules)"
elif [ $modules_source == "prebuilt" ]; then elif [ "$modules_source" == "prebuilt" ]; then
info "Rebuilding any native modules for this architecture" info "Rebuilding any native modules for this architecture"
npm rebuild 2>&1 | indent npm rebuild 2>&1 | indent
info "Installing any new modules" info "Installing any new modules"
npm install --quiet --userconfig $build_dir/.npmrc 2>&1 | indent npm install --quiet --userconfig $build_dir/.npmrc 2>&1 | indent
elif $use_cache; then
info "Restoring node modules from cache"
cp -r $cache_dir/node/node_modules $build_dir/
info "Pruning unused dependencies"
npm prune 2>&1 | indent
info "Installing any new modules"
npm install --quiet --userconfig $build_dir/.npmrc 2>&1 | indent
else else
info "Installing node modules" cache_status=$(get_cache_status)
touch $build_dir/.npmrc
npm install --quiet --userconfig $build_dir/.npmrc 2>&1 | indent if [ "$cache_status" == "valid" ]; then
info "Restoring node modules from cache"
cp -r $cache_dir/node/node_modules $build_dir/
info "Pruning unused dependencies"
npm prune 2>&1 | indent
info "Installing any new modules"
npm install --quiet --userconfig $build_dir/.npmrc 2>&1 | indent
else
info "$cache_status"
info "Installing node modules"
touch $build_dir/.npmrc
npm install --quiet --userconfig $build_dir/.npmrc 2>&1 | indent
fi
fi fi
####### Create a Procfile if possible ####### Create a Procfile if possible
head "Checking startup method" head "Checking startup method"
ensure_procfile "$start_method" "$build_dir"
if [ "$start_method" == "Procfile" ]; then
info "Found Procfile"
elif test -f $build_dir/Procfile; then
info "Procfile created during build"
elif [ "$start_method" == "npm start" ]; then
info "No Procfile; Adding 'web: npm start' to new Procfile"
echo "web: npm start" > $build_dir/Procfile
elif [ "$start_method" == "server.js" ]; then
info "No Procfile; Adding 'web: node server.js' to new Procfile"
echo "web: node server.js" > $build_dir/Procfile
fi
warn_start "$start_method" warn_start "$start_method"
####### Create the runtime environment (profile.d) ####### Finalize the build
head "Finalizing build" head "Finalizing build"
write_profile
# Runtime & Multi-buildpack exports write_export
info "Creating runtime environment" clean_npm
mkdir -p $build_dir/.profile.d clean_cache
echo "export PATH=\"\$HOME/.heroku/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\"" > $build_dir/.profile.d/nodejs.sh create_cache
echo "export NODE_HOME=\"\$HOME/.heroku/node\"" >> $build_dir/.profile.d/nodejs.sh build_succeeded
info "Exporting binary paths"
echo "export PATH=\"$build_dir/.heroku/node/bin:$build_dir/node_modules/.bin:\$PATH\"" > $bp_dir/export
echo "export NODE_HOME=\"$build_dir/.heroku/node\"" >> $bp_dir/export
####### Clean up
info "Cleaning up build artifacts"
# Clean up after npm
rm -rf "$build_dir/.node-gyp"
rm -rf "$build_dir/.npm"
# Clear the cache
rm -rf $cache_dir/node_modules # (for apps still on the older caching strategy)
rm -rf $cache_dir/node
####### Build successful! Store results in cache
# Create the cache
mkdir -p $cache_dir/node
echo $node_engine > $cache_dir/node/node-version
echo $npm_engine > $cache_dir/node/npm-version
if test -d $build_dir/node_modules; then
info "Caching node_modules for future builds"
cp -r $build_dir/node_modules $cache_dir/node
fi
# Show the final dependency tree
head "Build successful!"
(npm ls --depth=0 || true) 2>/dev/null | indent
# Show any relevant warnings
cat $warnings | indent
...@@ -116,7 +116,7 @@ testInvalidDependency() { ...@@ -116,7 +116,7 @@ testInvalidDependency() {
testNodeModulesCached() { testNodeModulesCached() {
cache=$(mktmpdir) cache=$(mktmpdir)
compile "caching" $cache compile "caching" $cache
assertCaptured "Caching node_modules for future builds" assertCaptured "Caching results for future builds"
assertEquals "1" "$(ls -1 $cache/ | grep node | wc -l)" assertEquals "1" "$(ls -1 $cache/ | grep node | wc -l)"
assertEquals "1" "$(ls -1 $cache/node | grep node_modules | wc -l)" assertEquals "1" "$(ls -1 $cache/node | grep node_modules | wc -l)"
} }
...@@ -125,7 +125,7 @@ testBuildWithCache() { ...@@ -125,7 +125,7 @@ testBuildWithCache() {
cache=$(mktmpdir) cache=$(mktmpdir)
compile "stable-node" $cache compile "stable-node" $cache
assertCaptured "node_modules cached: false" assertCaptured "node_modules cached: false"
assertCaptured "Caching node_modules for future builds" assertCaptured "Caching results for future builds"
assertCapturedSuccess assertCapturedSuccess
compile "stable-node" $cache compile "stable-node" $cache
......
build_failed() {
head "Build failed"
echo ""
cat $warnings | indent
info "We're sorry this build is failing! If you can't find the issue in application code,"
info "please submit a ticket so we can help: https://help.heroku.com/"
info "You can also try reverting to our legacy Node.js buildpack:"
info "heroku config:set BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-nodejs#v63"
info ""
info "Love,"
info "Heroku"
}
build_succeeded() {
head "Build succeeded!"
echo ""
(npm ls --depth=0 || true) 2>/dev/null | indent
cat $warnings | indent
}
get_start_method() {
local build_dir=$1
if test -f $build_dir/Procfile; then
echo "Procfile"
elif [[ $(read_json "$build_dir/package.json" ".scripts.start") != "" ]]; then
echo "npm start"
elif test -f $build_dir/server.js; then
echo "server.js"
else
echo ""
fi
}
get_modules_source() {
local build_dir=$1
if test -d $build_dir/node_modules; then
echo "prebuilt"
elif test -f $build_dir/npm-shrinkwrap.json; then
echo "npm-shrinkwrap.json"
elif test -f $build_dir/package.json; then
echo "package.json"
else
echo ""
fi
}
get_modules_cached() {
local cache_dir=$1
if test -d $cache_dir/node/node_modules; then
echo "true"
else
echo "false"
fi
}
show_current_state() {
echo ""
info "Node engine: ${node_engine:-unspecified}"
info "Npm engine: ${npm_engine:-unspecified}"
info "Start mechanism: ${start_method:-none}"
info "node_modules source: ${modules_source:-none}"
info "node_modules cached: $modules_cached"
echo ""
printenv | grep ^NPM_CONFIG_ | indent
info "NODE_MODULES_CACHE=$NODE_MODULES_CACHE"
}
install_node() {
# Resolve non-specific node versions using semver.herokuapp.com
if ! [[ "$node_engine" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
info "Resolving node version ${node_engine:-(latest stable)} via semver.io..."
node_engine=$(curl --silent --get --data-urlencode "range=${node_engine}" https://semver.herokuapp.com/node/resolve)
fi
# Download node from Heroku's S3 mirror of nodejs.org/dist
info "Downloading and installing node $node_engine..."
node_url="http://s3pository.heroku.com/node/v$node_engine/node-v$node_engine-linux-x64.tar.gz"
curl $node_url -s -o - | tar xzf - -C /tmp
# Move node (and npm) into .heroku/node and make them executable
mv /tmp/node-v$node_engine-linux-x64/* $heroku_dir/node
chmod +x $heroku_dir/node/bin/*
PATH=$heroku_dir/node/bin:$PATH
}
install_npm() {
# Optionally bootstrap a different npm version
if [ "$npm_engine" != "" ]; then
if ! [[ "$npm_engine" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
info "Resolving npm version ${npm_engine} via semver.io..."
npm_engine=$(curl --silent --get --data-urlencode "range=${npm_engine}" https://semver.herokuapp.com/npm/resolve)
fi
if [[ `npm --version` == "$npm_engine" ]]; then
info "npm `npm --version` already installed with node"
else
info "Downloading and installing npm $npm_engine (replacing version `npm --version`)..."
npm install --quiet -g npm@$npm_engine 2>&1 >/dev/null | indent
fi
warn_old_npm `npm --version`
fi
}
get_cache_status() {
# Did we bust the cache?
if ! $modules_cached; then
echo "No cache available"
elif ! $NODE_MODULES_CACHE; then
echo "Cache disabled with NODE_MODULES_CACHE"
elif [ "$node_previous" != "" ] && [ "$node_engine" != "$node_previous" ]; then
echo "Node version changed ($node_previous => $node_engine); invalidating cache"
elif [ "$npm_previous" != "" ] && [ "$npm_engine" != "$npm_previous" ]; then
echo "Npm version changed ($npm_previous => $npm_engine); invalidating cache"
else
echo "valid"
fi
}
ensure_procfile() {
local start_method=$1
local build_dir=$2
if [ "$start_method" == "Procfile" ]; then
info "Found Procfile"
elif test -f $build_dir/Procfile; then
info "Procfile created during build"
elif [ "$start_method" == "npm start" ]; then
info "No Procfile; Adding 'web: npm start' to new Procfile"
echo "web: npm start" > $build_dir/Procfile
elif [ "$start_method" == "server.js" ]; then
info "No Procfile; Adding 'web: node server.js' to new Procfile"
echo "web: node server.js" > $build_dir/Procfile
fi
}
write_profile() {
info "Creating runtime environment"
mkdir -p $build_dir/.profile.d
echo "export PATH=\"\$HOME/.heroku/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\"" > $build_dir/.profile.d/nodejs.sh
echo "export NODE_HOME=\"\$HOME/.heroku/node\"" >> $build_dir/.profile.d/nodejs.sh
}
write_export() {
info "Exporting binary paths"
echo "export PATH=\"$build_dir/.heroku/node/bin:$build_dir/node_modules/.bin:\$PATH\"" > $bp_dir/export
echo "export NODE_HOME=\"$build_dir/.heroku/node\"" >> $bp_dir/export
}
clean_npm() {
info "Cleaning npm artifacts"
rm -rf "$build_dir/.node-gyp"
rm -rf "$build_dir/.npm"
}
clean_cache() {
info "Cleaning previous cache"
rm -rf "$cache_dir/node_modules" # (for apps still on the older caching strategy)
rm -rf "$cache_dir/node"
}
create_cache() {
info "Caching results for future builds"
mkdir -p $cache_dir/node
echo $node_engine > $cache_dir/node/node-version
echo $npm_engine > $cache_dir/node/npm-version
if test -d $build_dir/node_modules; then
cp -r $build_dir/node_modules $cache_dir/node
fi
}
...@@ -14,26 +14,13 @@ info() { ...@@ -14,26 +14,13 @@ info() {
} }
warning() { warning() {
tip=$1 local tip=$1
url=$2 local url=$2
echo "WARNING: $tip" >> $warnings echo "WARNING: $tip" >> $warnings
echo "${url:-https://devcenter.heroku.com/articles/nodejs-support}" >> $warnings echo "${url:-https://devcenter.heroku.com/articles/nodejs-support}" >> $warnings
echo "" >> $warnings echo "" >> $warnings
} }
build_failed() {
head "Build failed"
echo ""
cat $warnings | indent
info "We're sorry this build is failing! If you can't find the issue in application code,"
info "please submit a ticket so we can help: https://help.heroku.com/"
info "You can also try reverting to our legacy Node.js buildpack:"
info "heroku config:set BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-nodejs#v63"
info ""
info "Love,"
info "Heroku"
}
file_contents() { file_contents() {
if test -f $1; then if test -f $1; then
echo "$(cat $1)" echo "$(cat $1)"
...@@ -42,11 +29,15 @@ file_contents() { ...@@ -42,11 +29,15 @@ file_contents() {
fi fi
} }
package_json() { read_json() {
if test -f $build_dir/package.json; then local file=$1
local result="$(cat $build_dir/package.json | $bp_dir/vendor/jq -r $1)" local node=$2
if [ "$result" == "null" ]; then echo "" if test -f $file; then
else echo "$result" local result="$(cat $file | $bp_dir/vendor/jq -r $node)"
if [ "$result" == "null" ]; then
echo ""
else
echo "$result"
fi fi
else else
echo "" echo ""
......
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository" : {
"type" : "git",
"url" : "http://github.com/example/example.git"
},
"dependencies": {
"hashish": "*"
},
"engines": {
"node": "0.10.x",
"npm": "1.1.65"
},
"scripts": {
"start": "node server.js"
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment