#!/usr/bin/env bash
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
#
# Licensed 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.

set -e
set -o pipefail
set -x

GOLANG_GIT_URL="https://github.com/golang/go.git"

# Arg 1:  absolute path to the Golang minor version directory under this repo's projects/golang/go
# Arg 2:  absolute path to the root directory of the locally cloned Golang repo
function apply_cve_patches {
  local version_dir=$1
  local golang_dir=$2

  echo "Applying *CVE* patches in $version_dir to $golang_dir..."
  if apply_patches "$version_dir" "$golang_dir" "true"; then
    echo "All *CVE* patches succeeded!"
    echo "HEAD is at the last successful *CVE* patch."
    return 0
  fi
  echo "A *CVE* patch failed to apply"
  echo "HEAD is at the last successful *CVE* patch."
  return 1
}

# Arg 1:  absolute path to the Golang minor version directory under this repo's projects/golang/go
# Arg 2:  absolute path to the root directory of the locally cloned Golang repo
function apply_other_patches {
  local version_dir=$1
  local golang_dir=$2

  echo "Assuming CVE patches, if they exist, are already applied in $golang_dir"

  echo "Applying *other* patches in $version_dir to $golang_dir..."
  if apply_patches "$version_dir" "$golang_dir" "false"; then
    echo "All *other* patches succeeded!"
    echo "HEAD is at the last successful *other* patch."
    return 0
  fi

  echo "An *other* patch failed to apply"
  echo "HEAD is at the last successful *other* patch."
  return 1
}

# Arg 1:  absolute path to the Golang minor version directory under this repo's projects/golang/go
# Arg 2:  absolute path to the root directory of the locally cloned Golang repo
# Arg 3:  must be "true" if the applied patches are CVE patches. All other values indicate that the
#         patches are not CVE patches.
function apply_patches {
  local version_dir=$1
  local golang_dir=$2
  local is_apply_for_cve_patches=$3

  local patches

  pushd "$golang_dir"

  if [[ -n "$(ls "$version_dir")" ]]; then
    patches=$(_get_patches "${version_dir}" "${is_apply_for_cve_patches}")
    echo "Patches to apply: ${patches}"
    for file in ${patches}; do
      if git am <"$file"; then
        echo "Applying succeeded: $file"
      else
        echo "Applying failed: $file"
        git am --skip
        popd
        return 1
      fi
    done
  else
    echo "Nothing to apply in $version_dir"
  fi

  popd
}

# Arg 1:  absolute path to the Golang minor version directory under this repo's projects/golang/go
# Arg 2:  should be "true" if the applied patches are CVE patches. All other values indicate that
#         the patches are not CVE patches.
#
# Returns:  names of patch files
function _get_patches {
  local version_dir=$1
  local is_apply_for_cve_patches_string=$2

  local is_apply_for_cve_patches=false
  if [ "$is_apply_for_cve_patches_string" = "true" ]; then
    is_apply_for_cve_patches=true
  fi

  local file_name_regex
  file_name_regex="^.*[0-9]{4}-$(get_eks_go_git_tag "$version_dir")-.*\.patch$"
  local is_match

  declare -a patches

  for file in "${version_dir}"/patches/*.patch; do
    is_match=false
    if [[ $file =~ $file_name_regex ]]; then
      is_match=true
    fi

    if [ $is_match = $is_apply_for_cve_patches ]; then
      patches+=("$file")
    fi
  done
  echo "${patches[@]}"
}

# Arg 1:  absolute path to the root directory of where the Golang repo is locally cloned or will be
#         locally cloned if it is not already
function clone_golang {
  local golang_dir=$1
  if [ ! "$(ls -A "$golang_dir")" ]; then
    echo "Golang repo expected at $golang_dir but was not found. Cloning..."
    git clone "$GOLANG_GIT_URL" "$golang_dir" --origin upstream
  fi
}

# Arg 1:  absolute path to the Golang minor version directory under this repo's projects/golang/go
# Arg 2:  absolute path to the root directory of the locally cloned Golang repo
function checkout_golang_at_git_tag {
  local version_dir=$1
  local golang_dir=$2

  pushd "$golang_dir"
  if ! git diff-index --quiet HEAD --; then
    echo "Local $golang_dir repository is in a dirty state. Stash, commit, or reset in-progress work."
    popd
    return 1
  fi

  if ! git config remote.upstream.url >/dev/null; then
    git remote add upstream "$GOLANG_GIT_URL"
  fi
  git fetch upstream --tags -f

  local git_tag
  git_tag="$(get_golang_git_tag "${version_dir}")"

  echo "Checking out $git_tag in $golang_dir"
  git checkout tags/"$git_tag"
  echo "$git_tag checked out!"
  popd
}

# Arg 1:  absolute path to the Golang minor version directory under this repo's projects/golang/go
#
# Returns:  EKS Go git tag, e.g. go-1.17.13-eks
function get_eks_go_git_tag {
  local version_dir=$1

  local golang_git_tag
  golang_git_tag=$(get_golang_git_tag "$version_dir")
  echo "${golang_git_tag//go/go-}-eks"
}

# Arg 1:  absolute path to the Golang minor version directory under this repo's projects/golang/go
#
# Returns:  EKS Go git tag, e.g. go1.17.13
function get_golang_git_tag {
  local version_dir=$1

  local version

  pushd "$version_dir" >/dev/null
  version="$(cat GIT_TAG)"
  popd >/dev/null
  echo "$version"
}