#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/apps/functions"
source "$PLUGIN_AVAILABLE_PATH/config/functions"

git_build_app_repo() {
  declare desc="builds local git app repo for app"
  verify_app_name "$1"
  local APP="$1"; local REV="$2"

  # clean up after ourselves
  local GIT_BUILD_APP_REPO_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku_git.XXXX")
  trap 'rm -rf "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null' RETURN INT TERM EXIT


  # git clone - this method creates a new git repository and adds the primary
  #             repo as a remote, then does a fetch depth=1 to avoid cloning
  #             the entire repo
  local TMP_TAG="dokku/$REV"
  chmod 755 "$GIT_BUILD_APP_REPO_TMP_WORK_DIR"
  unset GIT_DIR GIT_WORK_TREE
  pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null
  [[ ! -d "$DOKKU_ROOT/$APP" ]] && apps_create "$APP"
  GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
  GIT_DIR="$DOKKU_ROOT/$APP" git tag "$TMP_TAG" "$REV" &> /dev/null
  git init &> /dev/null
  git config advice.detachedHead false
  git remote add origin "$DOKKU_ROOT/$APP" &> /dev/null
  git fetch --depth=1 origin "refs/tags/$TMP_TAG" &> /dev/null
  git reset --hard FETCH_HEAD &> /dev/null
  git submodule update --init --recursive &> /dev/null
  GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
  find -name .git -prune -exec rm -rf {} \; > /dev/null

  if [[ -f Dockerfile ]] && [[ "$([[ -f .env ]] && grep -q BUILDPACK_URL .env; echo $?)" != "0" ]] && [[ ! -f ".buildpacks" ]] && [[ -z $(config_get "$APP" BUILDPACK_URL || true) ]]; then
    plugn trigger pre-receive-app "$APP" "dockerfile" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV"
    dokku receive "$APP" "dockerfile" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" | sed -u "s/^/"$'\e[1G'"/"
  else
    plugn trigger pre-receive-app "$APP" "herokuish" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV"
    dokku receive "$APP" "herokuish" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" | sed -u "s/^/"$'\e[1G'"/"
  fi
}

git_hook_cmd() {
  declare desc="kick off receive-app trigger from git prereceive hook"
  local cmd="git-hook"
  local APP="$2"

  local oldrev newrev refname
  while read -r oldrev newrev refname; do
    # Only run this script for the master branch. You can remove this
    # if block if you wish to run it for others as well.
    if [[ $refname = "refs/heads/master" ]]; then
      # broken out into plugin so we might support other methods to receive an app
      # shellcheck disable=SC2086
      plugn trigger receive-app $APP $newrev
    else
      if [[ $(find "$PLUGIN_PATH"/enabled/*/receive-branch 2>/dev/null | wc -l) != 0 ]]; then
        # shellcheck disable=SC2086
        plugn trigger receive-branch $APP $newrev $refname
      else
        echo $'\e[1G\e[K'"-----> WARNING: deploy did not complete, you must push to master."
        echo $'\e[1G\e[K'"-----> for example, try 'git push <dokku> ${refname/refs\/heads\/}:master'"
      fi
    fi
  done
}

git_upload_pack_cmd() {
  declare desc="executes git-upload-pack"
  local cmd="git-upload-pack"
  local APP="$(echo "$2" | perl -pe 's/(?<!\\)'\''//g' | sed 's/\\'\''/'\''/g')"
  plugn trigger git-pre-pull "$APP"
  cat | git-upload-pack "$DOKKU_ROOT/$APP"
  plugn trigger git-post-pull "$APP"
}

git_build_cmd() {
  declare desc="lock git-build"
  local cmd="git-build"
  local APP="$2"; local APP_BUILD_LOCK="$DOKKU_ROOT/$APP/.build.lock"
  local APP_BUILD_LOCK_MSG="$APP is currently being deployed or locked. Waiting..."
  [[ $(flock -n "$APP_BUILD_LOCK" true &>/dev/null ; echo $?) -ne 0 ]] && echo "$APP_BUILD_LOCK_MSG"

  shift 1
  flock -o "$APP_BUILD_LOCK" dokku git-build-locked "$@"
}

git_build_locked_cmd() {
  declare desc="setup and call git_build_app_repo"
  local cmd="git-build-locked"
  local APP="$2"
  if [[ $# -ge 3 ]]; then
    local REF="$3"
  else
    local REF=$(< "$DOKKU_ROOT/$APP/refs/heads/master")
  fi
  # shellcheck disable=SC2086
  git_build_app_repo $APP $REF
}

git_glob_cmd() {
  declare desc="catch-all for any other git-* commands"
  local cmd="git-*"
  local APP="$(echo "$2" | perl -pe 's/(?<!\\)'\''//g' | sed 's/\\'\''/'\''/g' | sed 's/^\///g')"
  local APP_PATH=$DOKKU_ROOT/$APP

  if [[ $1 == "git-receive-pack" && ! -d "$APP_PATH/refs" ]]; then
    git init --bare "$APP_PATH" > /dev/null
    local PRERECEIVE_HOOK="$APP_PATH/hooks/pre-receive"
    cat > "$PRERECEIVE_HOOK" <<EOF
#!/usr/bin/env bash
set -e; set -o pipefail;

cat | DOKKU_ROOT="$DOKKU_ROOT" dokku git-hook $APP
EOF
    chmod +x "$PRERECEIVE_HOOK"
  fi

  if [[ $1 == "git-receive-pack" ]]; then
    local args="$1 '$APP_PATH'"
  else
    local args=$*
  fi
  git-shell -c "$args"
}

case "$1" in
  git-hook)
    git_hook_cmd "$@"
    ;;

  git-upload-pack)
    git_upload_pack_cmd "$@"
    ;;

  git-build)
    git_build_cmd "$@"
    ;;

  git-build-locked)
    git_build_locked_cmd "$@"
    ;;

  git-*)
    git_glob_cmd "$@"
    ;;

  help | git:help)
    echo -n ""
    ;;

  *)
    exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
    ;;

esac
