#!/usr/bin/env bash

# Please Use Google Shell Style: https://google.github.io/styleguide/shell.xml

# ---- Start unofficial bash strict mode boilerplate
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -o errexit  # always exit on error
set -o errtrace # trap errors in functions as well
set -o pipefail # don't ignore exit codes when piping output
set -o posix    # more strict failures in subshells
# set -x          # enable debugging

IFS="$(printf "\n\t")"
# ---- End unofficial bash strict mode boilerplate

##### Usage #####
# First run:
#
# $ ./bin/docker-run.sh
#
# This will build the docker image then run a shell.
# Once the image is built, it will be run directly without a rebuild.
# If at any time you want to force a rebuild
#
# $ ./bin/docker-run.sh --build

##### How this script works #####
# This script can be dropped into a project and provide docker tooling
# to build a custom docker image for development on that project
# and get you a shell in there to do development tasks.
# It is all-in one for docker build and docker run.
#
# It is designed to have the project source code mounted into the docker image
# so that the host and the container always share the identical source code and
# project scripts. Only the development tools themselves live in the docker image.
# There is no ADD/COPY during the docker build and no docker-only volumes used.
#
# It is designed such that the user/group IDs match between the container and
# the host so there are no filesystem permission denied errors.
#
# The image it builds will by default match the name of this script's
# grandparent directory, meaning it's designed to live at
# myproject/bin/docker-run.sh
# and will tag the image as "myproject"
#
# It includes sections of Dockerfile syntax as shell variables which it will
# concatenate into a full Dockerfile sent to docker build over stdin.
# Snippets are provided for debian and alpine base images.
#
# It also mounts SSH_AUTH_SOCK so you can use git with ssh inside the
# container if you like.
#
# Bits you are intended to customize for each particular project are tagged with
# "CUSTOMIZE THIS" below.

docker_build() {
  ##### CUSTOMIZE THIS: to your base docker image #####
  from="alpine"

  # use this for debian/ubuntu, delete otherwise
  # shellcheck disable=SC2034
  user_section_debian=$(
    cat <<'EOF'
ARG USER
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN addgroup --gid ${GROUP_ID} ${USER} || true
RUN adduser --disabled-password --gid ${GROUP_ID} --uid ${USER_ID} --gecos ${USER} ${USER} || true
EOF
  )

  ##### CUSTOMIZE THIS with any additional packages you need #####
  # shellcheck disable=SC2034
  package_section_debian=$(
    cat <<'EOF'
RUN set -eux; \
  apt-get -q -y update; \
  apt-get -q -y install less git
EOF
  )

  # shellcheck disable=SC2034
  user_section_alpine=$(
    cat <<EOF
ARG USER
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN addgroup -g \${GROUP_ID} \${USER}; \
  adduser -D -G "\${USER}" -u "\${USER_ID}" -g "\${USER}" "\${USER}";
EOF
  )

  ##### CUSTOMIZE THIS with any additional packages you need #####
  # shellcheck disable=SC2034
  package_section_alpine=$(
    cat <<'EOF'
RUN set -eux; apk -v --update add bash less git;
EOF
  )

  ##### CUSTOMIZE THIS! #####
  main_section=$(
    cat <<'EOF'
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/node_modules/.bin
WORKDIR /host
USER \${USER}
EXPOSE 9999
CMD ["bash"]
EOF
  )

  echo "FROM ${from}
${user_section_alpine}
${package_section_alpine}
${main_section}" | docker build \
    --tag "$1" \
    --build-arg "USER=${USER}" \
    --build-arg "USER_ID=$(id -u)" \
    --build-arg "GROUP_ID=$(id -g)" \
    -
}

docker_run() {
  exec docker run --rm --interactive --tty \
    --attach stdin --attach stdout --attach stderr \
    --volume "${PWD}:/host" \
    --volume $SSH_AUTH_SOCK:/ssh-agent \
    --env SSH_AUTH_SOCK=/ssh-agent \
    --user "$(id -u)" \
    --publish 9999:9999 \
    "$1" "${2-bash}"
}

main() {
  cd "$(dirname "${BASH_SOURCE[0]}")/.."
  image=$(basename "${PWD}")
  case "$1" in
  --build)
    docker_build "${image}"
    ;;
  *)
    if ! docker inspect "${image}" &>/dev/null; then
      docker_build "${image}"
    fi
    docker_run "${image}"
    ;;
  esac
}

main "$@"