#!/usr/bin/env bash

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

trap cleanup EXIT

# Source in utils.
SELF=$(cd $(dirname $0) && pwd)
. "$SELF/release-util.sh"

# Print usage and exit.
function exit_with_usage {
  cat << EOF
Usage: release-build.sh <build|publish-snapshot|publish-release>
Creates build deliverables from a tag/commit.
Arguments:
 build             Create binary packages and commit to dist.apache.org/repos/dist/dev/hbase/
 publish-snapshot  Publish snapshot release to Apache snapshots
 publish-release   Publish a release to Apache release repo

All other inputs are environment variables:
 GIT_REF - Release tag or commit to build from
 PACKAGE_VERSION - Release identifier in top level package directory (e.g. 2.1.2RC1)
 VERSION - (optional) Version of project being built (e.g. 2.1.2)
 ASF_USERNAME - Username of ASF committer account
 ASF_PASSWORD - Password of ASF committer account
 GPG_KEY - GPG key used to sign release artifacts
 GPG_PASSPHRASE - Passphrase for GPG key
 PROJECT - The project to build. No default.

Set REPO environment to full path to repo to use
to avoid re-downloading dependencies on each run.

For example:
 $ PROJECT="hbase-operator-tools" ASF_USERNAME=NAME ASF_PASSWORD=PASSWORD GPG_PASSPHRASE=PASSWORD GPG_KEY=stack@apache.org ./release-build.sh build
EOF
  exit 1
}

set -e

function cleanup {
  rm ${tmp_settings} &> /dev/null || true
}

if [ $# -eq 0 ]; then
  exit_with_usage
fi

if [[ $@ == *"help"* ]]; then
  exit_with_usage
fi

# Read in the ASF password.
if [[ -z "$ASF_PASSWORD" ]]; then
  echo 'The environment variable ASF_PASSWORD is not set. Enter the password.'
  echo
  stty -echo && printf "ASF password: " && read ASF_PASSWORD && printf '\n' && stty echo
fi

# Read in the GPG passphrase
if [[ -z "$GPG_PASSPHRASE" ]]; then
  echo 'The environment variable GPG_PASSPHRASE is not set. Enter the passphrase to'
  echo 'unlock the GPG signing key that will be used to sign the release!'
  echo
  stty -echo && printf "GPG passphrase: " && read GPG_PASSPHRASE && printf '\n' && stty echo
  export GPG_PASSPHRASE
  export GPG_TTY=$(tty)
fi

for env in ASF_USERNAME GPG_PASSPHRASE GPG_KEY; do
  if [ -z "${!env}" ]; then
    echo "ERROR: $env must be set to run this script"
    exit_with_usage
  fi
done

export LC_ALL=C.UTF-8
export LANG=C.UTF-8

# Commit ref to checkout when building
GIT_REF=${GIT_REF:-master}

RELEASE_STAGING_LOCATION="https://dist.apache.org/repos/dist/dev/hbase"

GPG="gpg --pinentry-mode loopback -u $GPG_KEY --no-tty --batch"
NEXUS_ROOT=https://repository.apache.org/service/local/staging
NEXUS_PROFILE=8e226b97c0c82 # Profile for project staging uploads via INFRA-17900 Need nexus "staging profile id" for the hbase project
BASE_DIR=$(pwd)

init_java
init_mvn
init_python
# Print out subset of perl version.
perl --version | grep 'This is'

rm -rf ${PROJECT}
ASF_REPO="${ASF_REPO:-https://gitbox.apache.org/repos/asf/${PROJECT}.git}"
git clone "$ASF_REPO"
cd ${PROJECT}
git checkout $GIT_REF
git_hash=`git rev-parse --short HEAD`
echo "Checked out ${PROJECT} git hash $git_hash"

if [ -z "$VERSION" ]; then
  # Run $MVN in a separate command so that 'set -e' does the right thing.
  TMP=$(mktemp)
  $MVN help:evaluate -Dexpression=project.version > $TMP
  VERSION=$(cat $TMP | grep -v INFO | grep -v WARNING | grep -v Download)
  rm $TMP
fi

# Profiles for publishing snapshots and release to Maven Central
PUBLISH_PROFILES="-Papache-release -Prelease"

# This is a band-aid fix to avoid the failure of Maven nightly snapshot in some Jenkins
# machines by explicitly calling /usr/sbin/lsof. Please see SPARK-22377 and the discussion
# in its pull request.
LSOF=lsof
if ! hash $LSOF 2>/dev/null; then
  LSOF=/usr/sbin/lsof
fi

if [ -z "$PACKAGE_VERSION" ]; then
  PACKAGE_VERSION="${VERSION}-$(date +%Y_%m_%d_%H_%M)-${git_hash}"
fi

DEST_DIR_NAME="$PACKAGE_VERSION"

git clean -d -f -x
cd ..

tmp_repo="${REPO:-`pwd`/$(mktemp -d hbase-repo-XXXXX)}"
# Reexamine. Not sure this working. Pass as arg? That don't seem to work either!
tmp_settings="/${tmp_repo}/tmp-settings.xml"
echo "<settings><servers>" > $tmp_settings
echo "<server><id>apache.snapshots.https</id><username>$ASF_USERNAME</username>" >> $tmp_settings
echo "<password>$ASF_PASSWORD</password></server>" >> $tmp_settings
echo "<server><id>apache-release</id><username>$ASF_USERNAME</username>" >> $tmp_settings
echo "<password>$ASF_PASSWORD</password></server>" >> $tmp_settings
echo "</servers>" >> $tmp_settings
echo "</settings>" >> $tmp_settings
export tmp_settings

if [[ "$1" == "build" ]]; then
  # Source and binary tarballs
  echo "Packaging release source tarballs"
  make_src_release "${PROJECT}" "${VERSION}"

  # Add timestamps to mvn logs.
  MAVEN_OPTS="-Dorg.slf4j.simpleLogger.showDateTime=true -Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss ${MAVEN_OPTS}"

  echo "`date -u +'%Y-%m-%dT%H:%M:%SZ'` Building binary dist"
  make_binary_release "${PROJECT}" "${VERSION}"
  echo "`date -u +'%Y-%m-%dT%H:%M:%SZ'` Done building binary distribution"

  if ! is_dry_run; then
    svn co --depth=empty $RELEASE_STAGING_LOCATION svn-hbase
    rm -rf "svn-hbase/${DEST_DIR_NAME}"
    mkdir -p "svn-hbase/${DEST_DIR_NAME}"

    echo "Copying release tarballs"
    cp ${PROJECT}-*.tar.* "svn-hbase/${DEST_DIR_NAME}/"
    cp ${PROJECT}/CHANGES.md "svn-hbase/${DEST_DIR_NAME}/"
    cp ${PROJECT}/RELEASENOTES.md "svn-hbase/${DEST_DIR_NAME}/"
    shopt -s nocasematch
    # Generate api report only if project is hbase for now.
    if [ "${PROJECT}" == "hbase" ]; then
      # This script usually reports an errcode along w/ the report.
      generate_api_report ./${PROJECT} "${API_DIFF_TAG}" "${PACKAGE_VERSION}" || true
      cp api*.html "svn-hbase/${DEST_DIR_NAME}/"
    fi
    shopt -u nocasematch

    svn add "svn-hbase/${DEST_DIR_NAME}"

    cd svn-hbase
    svn ci --username $ASF_USERNAME --password "$ASF_PASSWORD" -m"Apache ${PROJECT} $PACKAGE_VERSION" --no-auth-cache
    cd ..
    rm -rf svn-hbase
  fi

  exit 0
fi

if [[ "$1" == "publish-snapshot" ]]; then
  cd ${PROJECT}
  # Publish ${PROJECT} to Maven release repo
  echo "Deploying ${PROJECT} SNAPSHOT at '$GIT_REF' ($git_hash)"
  echo "Publish version is $VERSION"
  if [[ ! $VERSION == *"SNAPSHOT"* ]]; then
    echo "ERROR: Snapshots must have a version containing SNAPSHOT"
    echo "ERROR: You gave version '$VERSION'"
    exit 1
  fi
  # Coerce the requested version
  $MVN versions:set -DnewVersion=$VERSION
  $MVN --settings $tmp_settings -DskipTests $PUBLISH_PROFILES deploy
  cd ..
  exit 0
fi

if [[ "$1" == "publish-release" ]]; then
  cd ${PROJECT}
  # Get list of modules from parent pom but filter out 'assembly' modules.
  # Used below in a few places.
  modules=`sed -n 's/<module>\(.*\)<.*$/\1/p' pom.xml | grep -v '-assembly' |  tr '\n' ' '`
  # Get the second groupId in the pom. This is the groupId for these artifacts.
  groupid=`sed -n 's/<groupId>\(.*\)<.*$/\1/p' pom.xml | tr '\n' ' '| awk '{print $2}'`
  # Convert groupid to a dir path for use below reaching into repo for jars.
  groupid_as_dir=`echo $groupid | sed -n 's/\./\//gp'`
  pwd
  # Publish ${PROJECT} to Maven release repo
  echo "Publishing ${PROJECT} checkout at '$GIT_REF' ($git_hash)"
  echo "Publish version is $VERSION"
  # Coerce the requested version
  $MVN versions:set -DnewVersion=$VERSION
  MAVEN_OPTS="${MAVEN_OPTS}" ${MVN} --settings $tmp_settings \
    clean install -DskipTests \
    -Dcheckstyle.skip=true ${PUBLISH_PROFILES} \
    -Dmaven.repo.local=${tmp_repo}
  pushd $tmp_repo/$groupid_as_dir
  # Remove any extra files generated during install
  # Remove extaneous files from module subdirs
  find $modules -type f | grep -v \.jar | grep -v \.pom | xargs rm

  # Using Nexus API documented here:
  # https://support.sonatype.com/entries/39720203-Uploading-to-a-Staging-Repository-via-REST-API
  if ! is_dry_run; then
    echo "Creating Nexus staging repository"
    repo_request="<promoteRequest><data><description>Apache ${PROJECT} $VERSION (commit $git_hash)</description></data></promoteRequest>"
    out=$(curl -X POST -d "$repo_request" -u $ASF_USERNAME:$ASF_PASSWORD \
      -H "Content-Type:application/xml" -v \
      $NEXUS_ROOT/profiles/$NEXUS_PROFILE/start)
    staged_repo_id=$(echo $out | sed -e "s/.*\(orgapachehbase-[0-9]\{4\}\).*/\1/")
    echo "Created Nexus staging repository: $staged_repo_id"
  fi

  # this must have .asc, and .sha1 - it really doesn't like anything else there
  for file in $(find $modules -type f)
  do
    if [[ "$file" == *.asc ]]; then
      continue
    fi
    if [ ! -f $file.asc ]; then
      echo $GPG_PASSPHRASE | $GPG --passphrase-fd 0 --output $file.asc \
        --detach-sig --armour $file;
    fi
    if [ $(command -v md5)  ]; then
      # Available on OS X; -q to keep only hash
      md5 -q $file > $file.md5
    else
      # Available on Linux; cut to keep only hash
      md5sum $file | cut -f1 -d' ' > $file.md5
    fi
    if [ $(command -v sha1sum)  ]; then
      sha1sum $file | cut -f1 -d' ' > $file.sha1
    else
      shasum $file | cut -f1 -d' ' > $file.sha1
    fi
  done

  if ! is_dry_run; then
    nexus_upload=$NEXUS_ROOT/deployByRepositoryId/$staged_repo_id
    echo "Uplading files to $nexus_upload"
    for file in $(find $modules -type f)
    do
      # strip leading ./
      file_short=$(echo $file | sed -e "s/\.\///")
      dest_url="$nexus_upload/$groupid_as_dir/$file_short"
      echo "  Uploading $file to $dest_url"
      curl -u "$ASF_USERNAME:$ASF_PASSWORD" --upload-file $file_short $dest_url
    done

    echo "Closing nexus staging repository"
    repo_request="<promoteRequest><data><stagedRepositoryId>$staged_repo_id</stagedRepositoryId><description>Apache ${PROJECT} $VERSION (commit $git_hash)</description></data></promoteRequest>"
    out=$(curl -X POST -d "$repo_request" -u $ASF_USERNAME:$ASF_PASSWORD \
      -H "Content-Type:application/xml" -v \
      $NEXUS_ROOT/profiles/$NEXUS_PROFILE/finish)
    echo "Closed Nexus staging repository: $staged_repo_id"
  fi

  popd
  rm -rf $tmp_repo
  cd ..
  # Dump out email to send.
  eval "echo \"$(< ../vote.tmpl)\"" |tee vote.txt
  exit 0
fi

cd ..
rm -rf ${PROJECT}
echo "ERROR: expects to be called with 'install', 'publish-release' or 'publish-snapshot'"
