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

ARGS=3

get_app_name() {
    echo $1 | awk -F"." '{print $1}'
}

get_git_sha() {
    repo=$1
    branch=$2
    branch_file="${repo}/${branch}"
    cat $branch_file
}

indent() {
    echo "       $@"
}

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

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

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

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

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

USER=$1
REPO=$2
BRANCH=$3
APP_NAME=$(get_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"

# get git sha of branch
GIT_SHA=$(get_git_sha $REPO_DIR $BRANCH)
SHORT_SHA=${GIT_SHA:0:8}

# 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
# extract git branch
git archive $BRANCH | tar -xmC $TMP_DIR

# switch to app context
cd $TMP_DIR

if [ -f Dockerfile ]; then
    DOCKERFILE=$(cat Dockerfile)
fi

if [ -f Procfile ]; then
    PROCFILE=$(cat Procfile)
fi

# pull config from controller to be used during build
URL="{{ .deis_controller_protocol }}://{{ .deis_controller_host }}:{{ .deis_controller_port }}/v1/hooks/config"
RESPONSE=$(curl -s -XPOST \
     -H "Content-Type: application/json" \
     -H "X-Deis-Builder-Auth: {{ .deis_controller_builderKey }}" \
     --data "{\"receive_user\":\"$USER\",\"receive_repo\":\"$APP_NAME\"}" \
     $URL)

# massage response for the environment variables in the form "-e HELLO=world"
CONFIG=$(echo $RESPONSE | jq -r ".values|to_entries|map(\"-e \(.key)=\(.value|tostring)\")|.[]" | sed -e 's/^"//' -e 's/"$//' 2> /dev/null || echo $RESPONSE)

if [ "$CONFIG" == "$RESPONSE" ];
then
    puts-warn "failed retrieving config from controller"
    puts-warn $RESPONSE
    exit 1
fi

# 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

    # build option string to send to slugbuilder
    BUILD_OPTS=("$CONFIG")

    # run in the background, we'll attach to it to retrieve logs
    BUILD_OPTS+=" -d"
    BUILD_OPTS+=" -v $TMP_DIR:/tmp/app"
    BUILD_OPTS+=" -v $CACHE_DIR:/tmp/cache:rw"
    # give non-root slugbuilder user R/W perms for docker volumes
    chown -R 2000:2000 $TMP_DIR $CACHE_DIR

    if [ -f /buildpacks ]; then
        BUILD_OPTS+=" -v /buildpacks:/tmp/buildpacks:rw"
        # give non-root slugbuilder user R/W perms for docker volumes
        chown -R 2000:2000 /buildpacks
    fi

    # build the application and attach to the process
    JOB=$(docker run $BUILD_OPTS deis/slugbuilder)
    docker attach $JOB

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

# 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

if [ -f $TMP_DIR/slug.tgz ]; then
    RELEASE_INFO=$(tar --to-stdout -xf $TMP_DIR/slug.tgz ./.release | python -c 'import sys,yaml,json;print json.dumps(yaml.safe_load(sys.stdin).get("default_process_types", {}))')
else
    RELEASE_INFO="{}"
fi

if [ -f $TMP_DIR/Procfile ]; then
    # update release info with data from the Procfile
    RELEASE_INFO=$(echo $RELEASE_INFO | python -c "import sys,json,os,yaml;obj=json.load(sys.stdin);procfile=open('Procfile').read();obj.update(yaml.safe_load(procfile));print json.dumps(obj)")
fi

if [ -f $TMP_DIR/Dockerfile ]; then
    DOCKERFILE=$(cat $TMP_DIR/Dockerfile)
fi

# safely escape double quotes
RELEASE_INFO=$(echo $RELEASE_INFO | sed -e 's/\"/\\\"/g')
DOCKERFILE=$(echo $DOCKERFILE | sed -e 's/\"/\\\"/g' -e 's/ \\/ /g')

puts-step "Launching... "
URL="{{ .deis_controller_protocol }}://{{ .deis_controller_host }}:{{ .deis_controller_port }}/v1/hooks/build"
DATA="{\"sha\":\"$SHORT_SHA\",\"receive_user\":\"$USER\",\"receive_repo\":\"$APP_NAME\",\"image\":\"$APP_NAME\",\"procfile\":\"$RELEASE_INFO\",\"dockerfile\":\"$DOCKERFILE\"}"

# notify the controller that the push was successful
RESPONSE=$(curl -s -XPOST \
     -H "Content-Type: application/json" \
     -H "X-Deis-Builder-Auth: {{ .deis_controller_builderKey }}" \
     --data "$DATA" \
     $URL)

RELEASE=$(echo $RESPONSE | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["release"]["version"]' 2> /dev/null || echo $RESPONSE)

if [ "$RELEASE" == "$RESPONSE" ];
then
    puts-warn "ERROR: Failed to launch container"
    puts-warn $RESPONSE
    exit 1
fi

DOMAIN=$(echo $RESPONSE | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["domains"][0]')
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
docker rm -f $JOB &>/dev/null
docker rmi -f $TMP_IMAGE &>/dev/null
