#!/bin/bash

set -e

ARGS=$@

VERSION=
DO_PUSH=
DO_BUILD=1
DO_DEBUG=
TARGET_ENV=

# The default registry to push to
REGISTRY=docker.io

usage() {
    cat << EOF
USAGE: pymdocker [--debug] [--no-build] [--push] --version N

Package a PyMacaron microservice into a Docker image and pushes that image to a
container repository. Must be run from the root of a PyMacaron directory. The
user running pymdocker must have logged in to the docker reporitofy with for
example 'docker login' or 'gcloud auth configure-docker'. The name of the
project and of the docker repository are extracted from the project's
pym-config.

If the project contains the file 'Dockerfile.extra', its content will be added
into the docker file used to generate the docker image.

The ID of the generated Docker image is saved in the file
'.pym/last_docker_image_id'.

OPTIONS:
  --env ENV     Build using configuration from 'pym-config.ENV.yaml' (optional)
  --version N   The version of the docker image to generate (mandatory)
  --no-build    Don't build the image
  --push        Push the generated docker image to he docker repository specified
  --debug       Run with extra verbosity
  --registry R  Override the default registry to push to (docker.io)
  --help        Show this help

EXAMPLES:
  # Build and push version 0.0.1 to google registry
  pymdocker --version 0.0.1 --push --registry gcr.io

  # Build 0.0.1 but don't push
  pymdocker --version 0.0.1

  # Push version 0.0.1, without (re)building it
  pymdocker --version 0.0.1 --no-build --push

EOF
}


parse_args() {
    while [ "$1" != "" ]; do
        case $1 in
            "--env")          shift; export TARGET_ENV=$1;;
            "--version")      shift; export VERSION=$1;;
            "--debug")        export DO_DEBUG=1; set -x;;
            "--no-build")     export DO_BUILD=;;
            "--push")         export DO_PUSH=1;;
            "--registry")     shift; REGISTRY=$1;;
            "-h" | "--help")  usage; exit 0;;
            *)                echo "Unknown argument '$1' (-h for help)"; exit 0;;
        esac
        shift
    done
}

parse_args $ARGS

if [ -z "$VERSION" ]; then
    echo "ERROR: please set an image version with --version"
    exit 1
fi

PYMCONFIG_ARGS=""
if [ ! -z "$TARGET_ENV" ]; then
    PYMCONFIG_ARGS="--env $TARGET_ENV"
fi

# Check that pymconfig exists
pymconfig $PYMCONFIG_ARGS

#
# Load configuration
#

PROJECT_NAME=$(pymconfig $PYMCONFIG_ARGS --name)
DOCKER_BASE=$(pymconfig $PYMCONFIG_ARGS --docker-base)
DOCKER_ROOT_REPO=$(pymconfig $PYMCONFIG_ARGS --docker-repo)
DOCKER_REPO=$DOCKER_ROOT_REPO/$PROJECT_NAME

#
# Check that we can run safely
#

check_git_clean() {
    IS_DIRTY_CLONE=$(git status --short --porcelain | wc -l)
    if [ "$IS_DIRTY_CLONE" -gt 0 ]; then
        ROOTDIR=$(git rev-parse --show-toplevel)
        ROOTDIR=$(basename $ROOTDIR)
        echo "ERROR: project $ROOTDIR is not clean! Commit and re-run."
        exit 1
    fi
}

check_git_clean

for LINK in $(pymconfig $PYMCONFIG_ARGS --include-links)
do
    check_git_clean $LINK
done

# Defensive check: are we in the git clone's dir?
# NOTE: on osx, a tmp dir may actually be located under /private
ROOTDIR=$(git rev-parse --show-toplevel | sed -e 's|^\/private||')
HERE=$(pwd | sed -e 's|^\/private||')
if [ "$HERE" != "$ROOTDIR" ]; then
    echo "ERROR: current dir is not the clone's root directory"
    exit 1
fi

DOCKERFILE_TEMPLATE=$(pymdockerpath)/Dockerfile.template
if [ ! -e "$DOCKERFILE_TEMPLATE" ]; then
    echo "ERROR: cannot find $DOCKERFILE_TEMPLATE"
    exit 1
fi

EXTRA_DOCKERFILE='./Dockerfile.extra'

if ! [[ "$REGISTRY" =~ ^(docker.io|gcr.io)$ ]]; then
    echo "ERROR: don't know how to push to registry $REGISTRY"
    exit 1
fi

#
# All set! Let's build!
#

if [ ! -z "$DO_BUILD" ]; then
    echo "=> Building Docker image for project $PROJECT_NAME, version $VERSION"

    echo "=> Taring source with git archive"
    mkdir -p docker
    git archive -o docker/macaron.tar HEAD

    # Remove the test folders
    # NOTE: on osx, you need to 'brew install gnu-tar' for tar to support --delete
    tar -f docker/macaron.tar --delete test
    tar -f docker/macaron.tar --delete testaccept

    if [ -d 'apis' ]; then
        tar -rf docker/macaron.tar apis/*.yaml
    fi

    for LINK in $(pymconfig $PYMCONFIG_ARGS --include-links)
    do
        tar --dereference \
            -rf docker/macaron.tar \
            --exclude='*.pyc' \
            --exclude='*__pycache__*' \
            $LINK
    done

    echo "=> Docker image will contain files:"
    echo ""
    tar -tvf docker/macaron.tar
    echo ""

    echo "=> Generating docker/Dockerfile"
    cat $DOCKERFILE_TEMPLATE \
        | sed -e "s|<VERSION>|$VERSION|" \
        > docker/Dockerfile

    if [ ! -z "$DOCKER_BASE" ]; then
        echo "=> Using custom Docker base image $DOCKER_BASE"
        sed -i "/FROM /c\FROM ${DOCKER_BASE}" docker/Dockerfile
    fi

    if [ -f $EXTRA_DOCKERFILE ]; then
        echo "=> Adding Dockerfile.extra to docker/Dockerfile"
        sed -i -e '/<EXTRA_DOCKERFILE>/r Dockerfile.extra' docker/Dockerfile > docker/Dockerfile.tmp
    fi

    # Remove image if it already exists
    # TODO: make the cleanup optional, so it can be skipped if no changes to project files
    IMAGE_ID=$(docker images --quiet ${DOCKER_REPO}:${VERSION})
    if [ ! -z "$IMAGE_ID" ]; then
        echo "=> Deleting cached image"
        docker rmi -f $IMAGE_ID
    fi

    echo "=> Building docker image"
    docker buildx build --platform linux/amd64 -t ${DOCKER_REPO}:${VERSION} -t gcr.io/${DOCKER_REPO}:${VERSION} --rm docker

    if [ ! -e '.pym' ]; then
        mkdir .pym
    fi

    FILE_IMAGE_ID=.pym/last_docker_image_id
    echo "=> Saving image version to $FILE_IMAGE_ID"
    echo "${DOCKER_REPO}:${VERSION}" > $FILE_IMAGE_ID

    FILE_VERSION=.pym/last_version
    echo "=> Saving version to $FILE_VERSION"
    echo $VERSION > $FILE_VERSION

    echo "=> Cleaning up"
    rm docker/Dockerfile
else
    echo "=> Not building docker image"
fi

if [ ! -z "$DO_PUSH" ]; then
    # NOTE: for this to work, the running user must have logged in into the
    # docker hub using 'docker login'

    if [[ "$REGISTRY" =~ ^(docker.io)$ ]]; then
        echo "=> Checking if 'docker login' has been run"
        set +e
        IS_LOGGEDIN=$(docker info | grep Username)
        if [ -z "$DO_DEBUG" ]; then
            set -e
        fi
        if [ -z "$IS_LOGGEDIN" ]; then
            echo "ERROR: please login to docker hub with 'docker login'"
            exit 1
        fi
    fi

    echo "=> Pushing to registry ${DOCKER_REPO}:${VERSION}"
    docker push $REGISTRY/${DOCKER_REPO}:${VERSION}

else
    echo "=> Not pushing docker image to repository"
fi

echo "=> Done!"
