#!/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
set -o nounset    # fail on unset variables
unset GIT_DIR     # Avoid GIT_DIR leak from previous build steps

### Configure directories

BUILD_DIR=${1:-}
CACHE_DIR=${2:-}
ENV_DIR=${3:-}
BP_DIR=$(cd $(dirname ${0:-}); cd ..; pwd)

mkdir -p "$BUILD_DIR/.heroku/node/"
cd $BUILD_DIR
export PATH="$BUILD_DIR/.heroku/node/bin":$PATH

LOG_FILE='/tmp/node-build-log.txt'
echo "" > "$LOG_FILE"

### Load dependencies

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

### Handle errors

handle_failure() {
  header "Build failed"
  warn_untracked_dependencies "$LOG_FILE"
  warn_angular_resolution "$LOG_FILE"
  failure_message | output "$LOG_FILE"
}
trap 'handle_failure' ERR

### Check initial state

[ -e "$BUILD_DIR/node_modules" ] && PREBUILD=true || PREBUILD=false

### Failures that should be caught immediately

fail_invalid_package_json "$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"
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")

  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)}"
  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`"
  else
    warn_node_engine "$node_engine"
    install_nodejs "$node_engine" "$BUILD_DIR/.heroku/node"
    install_npm "$npm_engine" "$BUILD_DIR/.heroku/node"
  fi
  warn_old_npm
}

header "Installing binaries"
install_bins | output "$LOG_FILE"

restore_cache() {
  local cache_status="$(get_cache_status)"

  if [ "$cache_status" == "valid" ]; then
    local cache_directories=$(get_cache_directories)
    if [ "$cache_directories" == "" ]; then
      echo "Loading 1 from cacheDirectories (default):"
      restore_cache_directories "$BUILD_DIR" "$CACHE_DIR" "node_modules"
    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 ($cache_status)"
  fi
}

header "Restoring cache"
restore_cache | output "$LOG_FILE"

build_dependencies() {
  if $PREBUILD; then
    echo "Prebuild detected (node_modules already exists)"
    rebuild_node_modules "$BUILD_DIR"
  else
    install_node_modules "$BUILD_DIR"
  fi
}

header "Building dependencies"
build_dependencies | output "$LOG_FILE"

cache_build() {
  local cache_directories=$(get_cache_directories)

  echo "Clearing previous node cache"
  clear_cache
  if [ "$cache_directories" == "" ]; then
    echo "Saving 1 cacheDirectories (default):"
    save_cache_directories "$BUILD_DIR" "$CACHE_DIR" "node_modules"
  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() {
  cd $BUILD_DIR
  (npm ls --depth=0 | tail -n +2 || true) 2>/dev/null
}

header "Build succeeded!"
summarize_build | output "$LOG_FILE"
