#!/usr/bin/env bash

LOG=/tmp/deploy.log
CONFIG=./deploy.conf
TEST=1
REF=
ENV=

#
# Abort with <msg>
#

abort() {
  echo
  echo "  $@" 1>&2
  echo
  exit 1
}

#
# Log <msg>.
#

log() {
  echo "  ○ $@"
}

#
# Set configuration file <path>.
#

set_config_path() {
  test -f $1 || abort invalid --config path
  CONFIG=$1
}

#
# Check if config <section> exists.
#

config_section() {
  grep "^\[$1" $CONFIG &> /dev/null
}

#
# Get config value by <key>.
#

config_get() {
  local key=$1
  test -n "$key" \
    && grep "^\[$ENV" -A 20 $CONFIG \
    | grep "^$key" \
    | head -n 1 \
    | cut -d ' ' -f 2-999
}

#
# Require environment arg.
#

require_env() {
  config_section $ENV || abort "[$ENV] config section not defined"
  test -z "$ENV" && abort "<env> required"
}

#
# Return the ssh command to run.
#

ssh_command() {
  local url="`config_get user`@`config_get host`"
  local key="`config_get key`"
  local forward_agent="`config_get forward-agent`"
  local port="`config_get port`"
  local needs_tty="`config_get needs_tty`"

  test -n "$forward_agent" && local agent="-A"
  test -n "$key" && local identity="-i $key"
  test -n "$port" && local port="-p $port"
  test -n "$needs_tty" && local tty="-t"
  echo "ssh $tty $agent $port $identity $url"
}

#
# Run the given remote <cmd>.
#

run() {
  local shell="`ssh_command`"
  echo $shell "\"$@\"" >> $LOG
  $shell $@
}

#
# Execute hook <name> relative to the path configured.
#

hook() {
  test -n "$1" || abort hook name required
  local hook=$1
  local path=`config_get path`
  local cmd=`config_get $hook`
  if test -n "$cmd"; then
    log "executing $hook \`$cmd\`"

    run "cd $path; \
      $cmd 2>&1 | tee -a $LOG; \
      exit \${PIPESTATUS[0]}"
    test $? -eq 0
  else
    log hook $hook
  fi
}

#
# Deploy [ref].
#

deploy() {
  local ref=$1
  local path=`config_get path`
  log deploying

  hook pre-deploy || abort pre-deploy hook failed

  # fetch source
  log fetching updates
  run "cd $path && git fetch --all"
  test $? -eq 0 || abort fetch failed

  # latest tag
  if test -z "$ref"; then
    log fetching latest tag
    ref=`run "cd $path && git for-each-ref refs/tags \
      --sort=-*authordate \
      --format='%(refname)' \
      --count=1 | cut -d '/' -f 3"`
    test $? -eq 0 || abort failed to determine latest tag
  fi

  # reset HEAD
  log resetting HEAD to $ref
  run "cd $path && git reset --hard $ref"
  test $? -eq 0 || abort git reset failed

  hook post-deploy || abort post-deploy hook failed

  # done
  log successfully deployed $ref
}

# parse argv

while test $# -ne 0; do
  arg=$1; shift
  case $arg in
    -c|--config) set_config_path $1; shift ;;
    -C|--chdir) log cd $1; cd $1; shift ;;
    *)
      if test -z "$ENV"; then
        ENV=$arg;
      else
        REF="$REF $arg";
      fi
      ;;
  esac
done

require_env

# deploy
deploy "${REF:-`config_get ref`}"
