# 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.

(@ load("@ytt:data", "data") -@)
(@ load("functions.lib.yml",
        "gci_label_value",
        "image_family_name")
-@)

(@ def google_variables(): -@)
GOOGLE_PROJECT=(@=data.values.google.project @)
GOOGLE_ZONE=(@=data.values.google.zone @)
(@- end @)


(@ def instance_variables(): -@)
INSTANCE_USER=build
INSTANCE_DIR=$(pwd)/instance

if [ ! -d "${INSTANCE_DIR}" ]; then
  echo "${INSTANCE_DIR} not found."
  exit 1
fi
(@- end @)

(@ def remote_functions(): -@)
(@=instance_variables() @)

SSH_OPTIONS=${SSH_OPTIONS:-"-o StrictHostKeyChecking=no -o PasswordAuthentication=no"}

ssh_key_file=${INSTANCE_DIR}/identity
if [ ! -r "${ssh_key_file}" ]; then
  echo "${ssh_key_file} not readable."
  exit 1
fi

instance_file=${INSTANCE_DIR}/instance.sh
if [ ! -r "${instance_file}" ]; then
  echo "${instance_file} not readable."
  exit 1
fi

external_ip=$(source ${instance_file} && echo -n ${networkInterfaces_accessConfigs_natIP})

function remote_shell {
  ssh ${SSH_OPTIONS} -i ${ssh_key_file} ${INSTANCE_USER}@${external_ip} "$@"
}

function remote_download {
  scp ${SSH_OPTIONS} -i ${ssh_key_file} -q -r "${INSTANCE_USER}@${external_ip}:${1}" "$2"
}

function remote_download_directory {
  ssh ${SSH_OPTIONS} -i ${ssh_key_file} ${INSTANCE_USER}@${external_ip} tar -C $(dirname ${1}) -czf - $(basename ${1}) | tar -C ${2} -zxf -
}

function remote_upload {
  scp ${SSH_OPTIONS} -i ${ssh_key_file} -q -r "$1" "${INSTANCE_USER}@${external_ip}:${2}"
}

(@- end @)

(@ def ctest_bash_task(path, timeout=300, parallel=6): -@)
set -ueo pipefail
(@= remote_functions() @)
remote_shell cmake -E chdir (@= path @) ctest -C ${CMAKE_CONFIG} -j(@= str(parallel) @) --timeout=(@= str(timeout) @) --output-on-failure --repeat until-pass:4 --schedule-random
(@- end @)

(@ def clang_tidy_bash_task(): -@)
set -ueo pipefail
export GEODE_HOME=$(pwd)/geode
log=$(mktemp)
cd build
cmake ../source -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >>${log} 2>&1 || (cat ${log}; exit 1)
cmake --build dependencies --parallel $(nproc) >>${log} 2>&1 || (cat ${log}; exit 1)
run-clang-tidy -j $(nproc) -quiet
(@- end @)

(@ def clang_format_bash_task(): -@)
set -ueo pipefail
export GEODE_HOME=$(pwd)/geode
log=$(mktemp)
cd build
cmake ../source -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >>${log} 2>&1 || (cat ${log}; exit 1)
jq -r '.[].file' compile_commands.json | sort | uniq | grep -v $(pwd) | xargs clang-format --dry-run -Werror
(@- end @)

(@ def rat_check_bash_task(): -@)
set -ueo pipefail
export GEODE_HOME=$(pwd)/geode
log=$(mktemp)
cd build
cmake ../source -DUSE_RAT=ON >>${log} 2>&1 || (cat ${log}; exit 1)
cmake --build . --target rat-check
(@- end @)

(@ def packer_build_bash_task(build): -@)
set -ueo pipefail
cd source/packer
packer build -only=googlecompute \
    -var-file=default.json \
    -var pipeline=(@= gci_label_value(data.values.pipeline.name) @) \
    -var version=(@= data.values.pipeline.version @) \
    -var pre=(@= data.values.pipeline.pre @) \
    -var owner=(@= gci_label_value(data.values.github.owner) @) \
    -var repository=(@= gci_label_value(data.values.github.repository) @) \
    -var branch=(@= gci_label_value(data.values.repository.branch) @) \
    -var image_name_prefix=(@= image_family_name(build.image_family)[0:51] @) \
    -var image_family=(@= image_family_name(build.image_family) @) \
    -var googlecompute_project=(@=  data.values.google.project @) \
    -var googlecompute_zone=(@= data.values.google.zone @) \
    (@= build.image_family @).json
(@- end @)

(@ def instance_create_bash_task(pipeline_name, build_config): -@)
set -ueo pipefail
(@= google_variables() @)
(@= instance_variables() @)
ssh_key_file=${INSTANCE_DIR}/identity
ssh_pubkey_file=${ssh_key_file}.pub
ssh-keygen -m pem  -t rsa -f ${ssh_key_file} -C "${INSTANCE_USER}" -N '' <<< y
ssh_pubkey=$(cat ${ssh_pubkey_file})
ssh_keys_file=${INSTANCE_DIR}/ssh_keys_file
echo "${INSTANCE_USER}:${ssh_pubkey}" > ${ssh_keys_file}
instance_name=build-$(cat /proc/sys/kernel/random/uuid)
image_name=$(cat image/name)
time_to_live=$(( $(date +%s) + ( 4 * 60 * 60 ) ))
instance_file=${INSTANCE_DIR}/instance.sh
gcloud compute instances create ${instance_name} \
    --format='config[export](name,networkInterfaces[0].accessConfigs[0].natIP)' \
    --project=${GOOGLE_PROJECT} \
    --zone=${GOOGLE_ZONE} \
    --subnet=default \
    --machine-type=e2-standard-16 \
    --boot-disk-size=200GB \
    --boot-disk-type=pd-standard \
    --boot-disk-device-name=${instance_name} \
    --image-project=${GOOGLE_PROJECT} \
    --image=${image_name} \
    --metadata-from-file ssh-keys=${ssh_keys_file} \
    --labels=time-to-live=${time_to_live},pipeline-name=(@= pipeline_name @),build-config=(@= build_config @) \
    > ${instance_file}
(@= remote_functions() @)
SSH_OPTIONS="${SSH_OPTIONS} -o ConnectTimeout=10"
echo "Waiting for ssh on ${instance_name} to be ready."
console_file=$(mktemp)
console_next=0
while ! remote_shell echo ready 2>/dev/null ; do
  gcloud compute instances get-serial-port-output ${instance_name} \
      --start ${console_next} \
      --project=${GOOGLE_PROJECT} \
      --zone=${GOOGLE_ZONE} \
      --format='value[separator="
"](next,contents)' \
      > ${console_file}
  tmp_next=$(head -n 1 ${console_file})
  if (( tmp_next != console_next )); then
    console_next=${tmp_next}
    sed '1d;s/\x1b\[[0-9;]*[JH]//g' ${console_file}
  fi
done
(@- end @)

(@ def instance_delete_bash_task(): -@)
set -ueo pipefail
(@= google_variables() @)
(@= instance_variables() @)

instance_file=${INSTANCE_DIR}/instance.sh
instance_name=$(source ${instance_file} && echo -n ${name})

gcloud compute instances delete ${instance_name} \
    --project=${GOOGLE_PROJECT} \
    --zone=${GOOGLE_ZONE} \
    --delete-disks=all \
    --quiet
(@- end @)

(@ def download_build_bash_task(): -@)
set -ueo pipefail
(@= remote_functions() @)
remote_download_directory build .
(@- end @)

(@ def build_bash_task(): -@)
set -ueo pipefail
(@= remote_functions() @)
pushd source
git_url=$(git remote get-url origin)
git_rev=$(git rev-parse HEAD)
popd
version=$(cat version/number)
builddate=$(date "+%Y-%m-%d")
remote_shell cmake -E make_directory build
remote_shell cmake -E time cmake -E chdir build cmake ../source ${CMAKE_CONFIGURE_FLAGS} \
    -DCMAKE_BUILD_TYPE=${CMAKE_CONFIG} \
    -DGEODE_ROOT=../geode \
    -DPRODUCT_VERSION=${version} \
    -DPRODUCT_BUILDDATE=${builddate} \
    -DPRODUCT_SOURCE_REVISION=${git_rev} \
    -DPRODUCT_SOURCE_REPOSITORY=${git_url}
remote_shell cmake -E time cmake --build build --config ${CMAKE_CONFIG} -- ${CMAKE_BUILD_FLAGS}
remote_shell cmake -E time cmake --build build --config ${CMAKE_CONFIG} --target docs -- ${CMAKE_BUILD_FLAGS}
remote_shell cmake -E time cmake -E chdir build cpack -C ${CMAKE_CONFIG} -G "${CPACK_GENERATORS}" | tee cpack.out
packages=$(awk '/^CPack: - package: / {print $4}' cpack.out)
for package in ${packages}; do
  remote_download ${package} package/
done
checksums=$(awk '/^CPack: - checksum file: / {print $5}' cpack.out)
for checksum in ${checksums}; do
  remote_download ${checksum} package/
done
(@- end @)

(@ def upload_source_bash_task(): -@)
set -ueo pipefail
(@= remote_functions() @)
remote_upload source .
(@- end @)


(@ def upload_geode_bash_task(): -@)
set -ueo pipefail
(@= remote_functions() @)
geode_version=$(cat geode-latest/version)
geode_name="apache-geode-${geode_version}"
geode_artifact="${geode_name}.tgz"
remote_upload geode-latest/${geode_artifact} .
remote_shell cmake -E tar xvf ${geode_artifact}
remote_shell cmake -E rename ${geode_name} geode
(@- end @)

(@ def extract_geode_bash_task(): -@)
set -ueo pipefail
if (cmp -s geode-latest/version geode/version); then
 exit 0
fi
geode_version=$(cat geode-latest/version)
geode_name="apache-geode-${geode_version}"
geode_artifact="${geode_name}.tgz"
tar xvf geode-latest/${geode_artifact} --directory=geode --strip-components=1
unzip -p geode/lib/geode-core-${geode_version}.jar org/apache/geode/internal/GemFireVersion.properties
cp geode-latest/version geode/version
(@- end @)

(@ def is_source_from_pr_testable_bash_task(): -@)
# Cribbed shamelessly from geode/ci/scripts/shared_utilities.sh
is_source_from_pr_testable() {
  if [[ $# -ne 3 ]]; then
    >&2 echo "Invalid args. Try ${0} \"<repo_path>\" \"<list of exclusion dirs>\""
    exit 1
  fi
  local repo_dir="${1}"
  if [[ ! -d "${repo_dir}" ]]; then
    # If the repo_dir does not exist, assume call from non-PR
    return 0;
  fi

  pushd "${repo_dir}" 2>&1 >> /dev/null
    local base_dir=$(git rev-parse --show-toplevel)
    local github_pr_dir="${base_dir}/.git/resource"
    pushd ${base_dir} 2>&1 >> /dev/null
      local return_code=0
      if [ -d "${github_pr_dir}" ]; then
        # Modify this path list with directories to exclude
        local exclude_dirs="${2}"
        for d in $(echo ${exclude_dirs}); do
          local exclude_pathspec="${exclude_pathspec} :(exclude,glob)${d}/**"
        done
        local exclude_files="${3}"
        for f in "${exclude_files}"; do
          local exclude_pathspec="${exclude_pathspec} :(exclude,glob)${f}"
        done
        pushd ${base_dir} &> /dev/null
          local files=$(git diff --name-only $(cat "${github_pr_dir}/base_sha") $(cat "${github_pr_dir}/head_sha") -- . $(echo ${exclude_pathspec}))
        popd &> /dev/null
        if [[ -z "${files}" ]]; then
          >&2 echo "Code changes are from CI only"
          return_code=1
        else
          >&2 echo "real code change here!"
        fi
      else
        >&2 echo "repo is not from a PR"
      fi
    popd 2>&1 >> /dev/null
  popd 2>&1 >> /dev/null
  return ${return_code}
}

exclude_dirs="ci packer docker tools"
exclude_files="*/*.md"
is_source_from_pr_testable "source" "${exclude_dirs}" "${exclude_files}" && exit 0 || exit 1

(@- end @)
