#!/usr/bin/env bash

source "$(dirname $0)/../dokku_common"

dokku_restore_timestamps() {
  perl - "$1" <<"EOF"
my $commit = $ARGV[0];

$commit = 'HEAD' unless $commit;

# git a list of access times and files
my @logbook = `git whatchanged --pretty=%at $commit`;

my %seen;
my $timestamp;
my $filename;
foreach (@logbook) {
    next if /^$/; # skip emtpy lines
    if (/^:/) {
        chomp ($filename = (split /\t/)[1]);
        next if $seen{$filename};
        utime($timestamp, $timestamp, $filename);
        $seen{$filename} = 1;
    } else {
        chomp ($timestamp = $_);
    }
}
EOF
}

dokku_build_app_from_git() {
    verify_app_name "$1"
    verify_max_args "2" "$@"
    REV="$2"

    # delete directory on return
    TMP_WORK_DIR="$(mktemp -d)"
    trap "popd > /dev/null; rm -rf \"$TMP_WORK_DIR\"" RETURN

    # clone git repository
    chmod 755 "$TMP_WORK_DIR"
    unset GIT_DIR GIT_WORK_TREE
    pushd "$TMP_WORK_DIR" > /dev/null
    git clone "$DOKKU_ROOT/$APP" "$TMP_WORK_DIR" > /dev/null
    git config advice.detachedHead false
    git checkout "$REV" > /dev/null
    git submodule update --init --recursive > /dev/null

    REV="$(git rev-parse HEAD)"

    # Build using either Dockerfile
    if [[ -f "Dockerfile" ]]; then
      info "Building $APP using Dockerfile..."
      dokku_restore_timestamps "$REV"
      find -name .git -prune -exec rm -rf {} \; > /dev/null
      if [[ -f "$APP_DIR/FORCE_CLEAN_CACHE" ]]; then
        docker build --no-cache=true -t "$IMAGE" .
        # Mark as cleaned-up
        rm -f "$APP_DIR/FORCE_CLEAN_CACHE"
      else
        docker build -t "$IMAGE" .
      fi
      pluginhook post-build "$APP" "$REV" "dockerfile"
    else
      # or buildstep (heroku-style)
      info "Building $APP using buildstep..."

      # Allow to override buildstep image with DOKKU_BUILDSTEP_IMAGE variable set
      DOKKU_BUILDSTEP_IMAGE="$(config_get DOKKU_BUILDSTEP_IMAGE || true)"
      if [[ -n "$DOKKU_BUILDSTEP_IMAGE" ]]; then
        info "Using custom buildstep image: $DOKKU_BUILDSTEP_IMAGE"
      else
        DOKKU_BUILDSTEP_IMAGE="$BUILDSTEP_IMAGE"
      fi

      find -name .git -prune -exec rm -rf {} \; > /dev/null
      tar c . | docker run -i -a stdin "$BUILDSTEP_IMAGE" /bin/bash \
                        -c "mkdir -p /app && tar -xC /app" | commit_image "$IMAGE"

      pluginhook pre-build "$APP" "$REV" "buildstep"
      DOCKER_ARGS="$(: | pluginhook docker-args "$APP" build)"
      if [[ -f "$APP_DIR/FORCE_CLEAN_CACHE" ]]; then
        remove_container "$APP_CACHE_NAME"
        # Mark as cleaned-up
        rm -f "$APP_DIR/FORCE_CLEAN_CACHE"
      fi
      # start data container for cache
      docker run -d -v "/cache" --name="$APP_CACHE_NAME" \
                busybox:latest true 1>/dev/null 2>/dev/null || true
      # link volumes from data container
      docker run -d --volumes-from="$APP_CACHE_NAME" \
                  $DOCKER_ARGS \
                  "$IMAGE" \
                  /build/builder | attach_and_commit_image "$IMAGE"

      pluginhook post-build "$APP" "$REV" "buildstep"
    fi

    tag_image "$IMAGE" "$IMAGE_GENERIC:build"

    dokku release:locked "$APP"
    dokku deploy:locked "$APP"
}

case "$1" in
  rebuild)
    verify_app_name "$2"
    verify_max_args 3 "$@"

    if ! flock -n "$APP_BUILD_LOCK" true; then
      info2 "$APP is currently being deployed or locked. We will wait for other process to finish."
    fi

    shift 1

    flock "$APP_BUILD_LOCK" dokku rebuild:locked "$@"
    ;;

  rebuild:locked)
    verify_app_name "$2"
    verify_max_args 3 "$@"

    if [[ $# -ge 3 ]]; then
      REF="$3"
    elif [[ -f "$APP_DIR/refs/heads/master" ]]; then
      REF="$(cat "$APP_DIR/refs/heads/master")"
    else
      verbose "$APP not yet deployed"
      exit 1
    fi

    dokku_build_app_from_git "$APP" "$REF"
    dokku cleanup
    ;;

  rebuild:force)
    verify_app_name "$2"
    verify_max_args 3 "$@"

    touch "$APP_DIR/FORCE_CLEAN_CACHE"

    shift
    dokku rebuild "$@"
    ;;

  rebuild:all)
    verify_max_args 1 "$@"

    for app in $(ls -d $DOKKU_ROOT/*/ 2>/dev/null); do
      if [[ -f "$app/refs/heads/master" ]]; then
        dokku rebuild "$(basename "$app")"
      fi
    done
    ;;

  rebuild:all:force)
    verify_max_args 1 "$@"

    for app in $(ls -d $DOKKU_ROOT/*/ 2>/dev/null); do
      touch "$app/FORCE_CLEAN_CACHE"
    done

    dokku rebuild:all
    ;;

  help)
    cat && cat<<EOF
    rebuild <app> [ref]                             Rebuild an app (using optional ref)
    rebuild:force <app> [ref]                       Remove all caches and rebuild an app (using optional ref)
    rebuild:all                                     Rebuild all apps
    rebuild:all:force                               Remove all caches and rebuild all apps
EOF
  ;;

  *)
    exit $DOKKU_NOT_IMPLEMENTED_EXIT
    ;;
esac
