#!/usr/bin/env bash
#
# builder hook called on every git receive-pack
# NOTE: this script must be run as root (for docker access)
#
set -eo pipefail

ARGS=3

indent() {
    echo "       $@"
}

puts-step() {
    echo "-----> $@"
}

puts-step-sameline() {
    echo -n "-----> $@"
}

puts-warn() {
    echo " !     $@"
}

usage() {
    echo "Usage: $0 <user> <repo> <sha>"
}

parse-string(){
    # helper to avoid the single quote escape
    # occurred in command substitution
    local args=() idx=0 IFS=' ' c
    for c; do printf -v args[idx++] '%s ' "$c"; done
    printf "%s\n" "${args[*]}"
}

if [ $# -ne $ARGS ]; then
    usage
    exit 1
fi

USER=$1
REPO=$2
GIT_SHA=$3
SHORT_SHA=${GIT_SHA:0:8}
APP_NAME="${REPO%.*}"

cd $(dirname $0) # ensure we are in the root dir

ROOT_DIR=$(pwd)
DOCKERFILE_SHIM="$ROOT_DIR/shim.dockerfile"
REPO_DIR="${ROOT_DIR}/${REPO}"
BUILD_DIR="${REPO_DIR}/build"
CACHE_DIR="${REPO_DIR}/cache"

# define image names
IMAGE_NAME="$APP_NAME:git-$SHORT_SHA"
TMP_IMAGE="{{ .deis_registry_host }}:{{ .deis_registry_port }}/$IMAGE_NAME"

# create app directories
mkdir -p $BUILD_DIR $CACHE_DIR
# create temporary directory inside the build dir for this push
TMP_DIR=$(mktemp -d --tmpdir=$BUILD_DIR)

cd $REPO_DIR
git archive $GIT_SHA | tar -xmC $TMP_DIR

# switch to app context
cd $TMP_DIR

USING_DOCKERFILE=false

if [ -f Dockerfile ]; then
    USING_DOCKERFILE=true
fi

# pull config from controller to be used during build
URL="{{ .deis_controller_protocol }}://{{ .deis_controller_host }}:{{ .deis_controller_port }}/v1/hooks/config"
RESPONSE=$(/app/bin/get-app-config -url="$URL" -key="{{ .deis_controller_builderKey }}" -user=$USER -app=$APP_NAME)
CODE=$?

if [ $CODE -ne 0 ]; then
    puts-warn $RESPONSE
    exit 1
fi

BUILD_OPTS=()
BUILD_OPTS+='/usr/bin/docker'
BUILD_OPTS+=' run -v /etc/environment_proxy:/etc/environment_proxy'
# get application configuration
BUILD_OPTS+=$(echo $RESPONSE | /app/bin/get-app-values)

# if no Dockerfile is present, use slugbuilder to compile a heroku slug
# and write out a Dockerfile to use that slug
if [ ! -f Dockerfile ]; then
    # run in the background, we'll attach to it to retrieve logs
    BUILD_OPTS+=' -d'
    BUILD_OPTS+=' -v '
    BUILD_OPTS+=$(echo $TMP_DIR)
    BUILD_OPTS+=':/tmp/app'
    BUILD_OPTS+=' -v '
    BUILD_OPTS+=$(echo $CACHE_DIR)
    BUILD_OPTS+=':/tmp/cache:rw'
    # give slug group ownership of TMP_DIR and CACHE_DIR.
    chown -R :2000 $TMP_DIR
    chown :2000 $CACHE_DIR
    # TMP_DIR is created using mktemp, which sets permissions to 700. Since
    # we share this with the slug group, the slug group needs to be able to
    # work with it.
    chmod g+rwx $TMP_DIR

    BUILD_OPTS+=' deis/slugbuilder'

    # build the application and attach to the process
    JOB=$(eval $(parse-string "${BUILD_OPTS[@]}") )
    docker attach $JOB

    # copy out the compiled slug
    docker cp $JOB:/tmp/slug.tgz $TMP_DIR
    # copy over the Dockerfile shim to the build dir
    cp $DOCKERFILE_SHIM ./Dockerfile
fi

# force newline
echo "" >> Dockerfile
# inject builder-specific environment variables into the application environment
echo "ENV GIT_SHA $GIT_SHA" >> Dockerfile

echo
puts-step "Building Docker image"
docker build -t $TMP_IMAGE . 2>&1
puts-step "Pushing image to private registry"
docker push $TMP_IMAGE  &>/dev/null
echo

# use Procfile if provided, otherwise try default process types from ./release
if [ -f Procfile ]; then
    PROCFILE=$(cat Procfile | /app/bin/yaml2json-procfile)
elif [ -f $TMP_DIR/slug.tgz ]; then
    PROCFILE=$(tar --to-stdout -xf $TMP_DIR/slug.tgz ./.release | /app/bin/extract-types)
else
    PROCFILE="{}"
fi

puts-step "Launching... "
URL="{{ .deis_controller_protocol }}://{{ .deis_controller_host }}:{{ .deis_controller_port }}/v1/hooks/build"
DATA=$(/app/bin/generate-buildhook "$SHORT_SHA" "$USER" "$APP_NAME" "$APP_NAME" "$PROCFILE" "$USING_DOCKERFILE")
PUBLISH_RELEASE=$(echo "$DATA" | /app/bin/publish-release-controller -url=$URL -key={{ .deis_controller_builderKey }})

CODE=$?
if [ $CODE -ne 0 ]; then
    puts-warn "ERROR: Failed to launch container"
    puts-warn $PUBLISH_RELEASE
    exit 1
fi

RELEASE=$(echo $PUBLISH_RELEASE | /app/bin/extract-version)
DOMAIN=$(echo $PUBLISH_RELEASE | /app/bin/extract-domain)
indent "done, $APP_NAME:v$RELEASE deployed to Deis"
echo
indent "http://$DOMAIN"
echo
indent "To learn more, use \`deis help\` or visit http://deis.io"
echo

# cleanup
cd $REPO_DIR
git gc &>/dev/null
if [ -n "$JOB" ]; then
  docker rm -f $JOB &>/dev/null
fi
