Permalink
executable file 220 lines (175 sloc) 6.15 KB
#!/usr/bin/env bash
# bin/compile <build-dir> <cache-dir> <env-dir>
### Configure environment
set -o errexit # always exit on error
set -o pipefail # don't ignore exit codes when piping output
unset GIT_DIR # Avoid GIT_DIR leak from previous build steps
### Constants
DEFAULT_CACHE="node_modules bower_components"
BPLOG_PREFIX="buildpack.nodejs"
### Configure directories
BUILD_DIR=${1:-}
CACHE_DIR=${2:-}
ENV_DIR=${3:-}
BP_DIR=$(cd $(dirname ${0:-}); cd ..; pwd)
STDLIB_FILE=$(mktemp -t stdlib.XXXXX)
BUILDPACK_LOG_FILE=${BUILDPACK_LOG_FILE:-/dev/null}
### Load dependencies
curl --silent --retry 5 --retry-max-time 15 'https://lang-common.s3.amazonaws.com/buildpack-stdlib/v2/stdlib.sh' > "$STDLIB_FILE"
source "$STDLIB_FILE"
source $BP_DIR/lib/output.sh
source $BP_DIR/lib/json.sh
source $BP_DIR/lib/failure.sh
source $BP_DIR/lib/environment.sh
source $BP_DIR/lib/binaries.sh
source $BP_DIR/lib/cache.sh
source $BP_DIR/lib/dependencies.sh
export PATH="$BUILD_DIR/.heroku/node/bin:$BUILD_DIR/.heroku/yarn/bin":$PATH
LOG_FILE=$(mktemp -t node-build-log.XXXXX)
echo "" > "$LOG_FILE"
### Handle errors
handle_failure() {
header "Build failed"
warn_untracked_dependencies "$LOG_FILE"
warn_angular_resolution "$LOG_FILE"
warn_missing_devdeps "$LOG_FILE"
warn_econnreset "$LOG_FILE"
warn_young_yarn "$LOG_FILE"
failure_message | output "$LOG_FILE"
}
trap 'handle_failure' ERR
### Check initial state
[ -e "$BUILD_DIR/node_modules" ] && PREBUILD=true || PREBUILD=false
[ -f "$BUILD_DIR/yarn.lock" ] && YARN=true || YARN=false
[ -f "$BUILD_DIR/package-lock.json" ] && NPM_LOCK=true || NPM_LOCK=false
### Failures that should be caught immediately
fail_dot_heroku "$BUILD_DIR"
fail_dot_heroku_node "$BUILD_DIR"
fail_invalid_package_json "$BUILD_DIR"
fail_yarn_and_npm_lockfiles "$BUILD_DIR"
warn_prebuilt_modules "$BUILD_DIR"
warn_missing_package_json "$BUILD_DIR"
### Compile
create_env() {
write_profile "$BP_DIR" "$BUILD_DIR"
write_export "$BP_DIR" "$BUILD_DIR"
export_env_dir "$ENV_DIR"
create_default_env
}
header "Creating runtime environment"
mkdir -p "$BUILD_DIR/.heroku/node/"
cd $BUILD_DIR
create_env # can't pipe the whole thing because piping causes subshells, preventing exports
list_node_config | output "$LOG_FILE"
install_bins() {
local node_engine=$(read_json "$BUILD_DIR/package.json" ".engines.node")
local iojs_engine=$(read_json "$BUILD_DIR/package.json" ".engines.iojs")
local npm_engine=$(read_json "$BUILD_DIR/package.json" ".engines.npm")
local yarn_engine=$(read_json "$BUILD_DIR/package.json" ".engines.yarn")
if [ -n "$iojs_engine" ]; then
echo "engines.iojs (package.json): $iojs_engine (iojs)"
else
echo "engines.node (package.json): ${node_engine:-unspecified}"
fi
echo "engines.npm (package.json): ${npm_engine:-unspecified (use default)}"
if $YARN; then
echo "engines.yarn (package.json): ${yarn_engine:-unspecified (use default)}"
fi
echo ""
if [ -n "$iojs_engine" ]; then
warn_node_engine "$iojs_engine"
install_iojs "$iojs_engine" "$BUILD_DIR/.heroku/node"
echo "Using bundled npm version for iojs compatibility: `npm --version`"
mcount "version.iojs.$(node --version)"
else
warn_node_engine "$node_engine"
install_nodejs "$node_engine" "$BUILD_DIR/.heroku/node"
install_npm "$npm_engine" "$BUILD_DIR/.heroku/node" $NPM_LOCK
mcount "version.node.$(node --version)"
fi
# Download yarn if there is a yarn.lock file or if the user
# has specified a version of yarn under "engines". We'll still
# only install using yarn if there is a yarn.lock file
if $YARN || [ -n "$yarn_engine" ]; then
install_yarn "$BUILD_DIR/.heroku/yarn" "$yarn_engine"
fi
if $YARN; then
mcount "version.yarn.$(yarn --version)"
else
mcount "version.npm.$(npm --version)"
fi
warn_old_npm
}
header "Installing binaries"
install_bins | output "$LOG_FILE"
restore_cache() {
local cache_status="$(get_cache_status)"
if $YARN; then
if [ -e "$BUILD_DIR/node_modules" ]; then
warn "node_modules checked into source control" "https://blog.heroku.com/node-habits-2016#9-only-git-the-important-bits"
rm -rf "$BUILD_DIR/node_modules"
fi
fi
if [ "$cache_status" == "valid" ]; then
local cache_directories=$(get_cache_directories)
if [ "$cache_directories" == "" ]; then
echo "Loading 2 from cacheDirectories (default):"
restore_cache_directories "$BUILD_DIR" "$CACHE_DIR" "$DEFAULT_CACHE"
else
echo "Loading $(echo $cache_directories | wc -w | xargs) from cacheDirectories (package.json):"
restore_cache_directories "$BUILD_DIR" "$CACHE_DIR" $cache_directories
fi
else
echo "Skipping cache restore ($cache_status)"
fi
mcount "cache.$cache_status"
}
header "Restoring cache"
restore_cache | output "$LOG_FILE"
build_dependencies() {
run_if_present 'heroku-prebuild'
local cache_status="$(get_cache_status)"
local start=$(nowms)
if $YARN; then
yarn_node_modules "$BUILD_DIR"
elif $PREBUILD; then
echo "Prebuild detected (node_modules already exists)"
npm_rebuild "$BUILD_DIR"
else
npm_node_modules "$BUILD_DIR"
fi
mtime "modules.time.cache.$cache_status" "${start}"
run_if_present 'heroku-postbuild'
# TODO: run_if_present 'build'
log_build_scripts
}
header "Building dependencies"
build_dependencies | output "$LOG_FILE"
cache_build() {
local cache_directories=$(get_cache_directories)
echo "Clearing previous node cache"
clear_cache
if ! ${NODE_MODULES_CACHE:-true}; then
echo "Skipping cache save (disabled by config)"
elif [ "$cache_directories" == "" ]; then
echo "Saving 2 cacheDirectories (default):"
save_cache_directories "$BUILD_DIR" "$CACHE_DIR" "$DEFAULT_CACHE"
else
echo "Saving $(echo $cache_directories | wc -w | xargs) cacheDirectories (package.json):"
save_cache_directories "$BUILD_DIR" "$CACHE_DIR" $cache_directories
fi
save_signature
}
header "Caching build"
cache_build | output "$LOG_FILE"
summarize_build() {
if $NODE_VERBOSE; then
list_dependencies "$BUILD_DIR"
fi
mmeasure 'modules.size' "$(measure_size)"
}
header "Build succeeded!"
mcount "compile"
summarize_build | output "$LOG_FILE"
warn_no_start "$LOG_FILE"
warn_unmet_dep "$LOG_FILE"