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

function build::common::ensure_tar() {
  if [[ -n "${TAR:-}" ]]; then
    return
  fi

  # Find gnu tar if it is available, bomb out if not.
  TAR=tar
  if which gtar &>/dev/null; then
      TAR=gtar
  elif which gnutar &>/dev/null; then
      TAR=gnutar
  fi
  if ! "${TAR}" --version | grep -q GNU; then
    echo "  !!! Cannot find GNU tar. Build on Linux or install GNU tar"
    echo "      on Mac OS X (brew install gnu-tar)."
    return 1
  fi
}

# Build a release tarball.  $1 is the output tar name.  $2 is the base directory
# of the files to be packaged.  This assumes that ${2}/kubernetes is what is
# being packaged.
function build::common::create_tarball() {
  build::common::ensure_tar

  local -r tarfile=$1
  local -r stagingdir=$2
  local -r repository=$3

  "${TAR}" czf "${tarfile}" -C "${stagingdir}" $repository --owner=0 --group=0
}

# Generate shasum of tarballs. $1 is the directory of the tarballs.
function build::common::generate_shasum() {

  local -r tarpath=$1

  echo "Writing artifact hashes to shasum files..."

  if [ ! -d "$tarpath" ]; then
    echo "  Unable to find tar directory $tarpath"
    exit 1
  fi

  cd $tarpath
  for file in $(find . -name '*.tar.gz'); do
    filepath=$(basename $file)
    sha256sum "$filepath" > "$file.sha256"
    sha512sum "$filepath" > "$file.sha512"
  done
  cd -
}

function build::common::upload_artifacts() {
  local -r artifactspath=$1
  local -r artifactsbucket=$2
  local -r projectpath=$3
  local -r buildidentifier=$4
  local -r githash=$5
  local -r latestpath=$6

  echo "$githash" >> "$artifactspath"/githash

  # Upload artifacts to s3
  # 1. To proper path on s3 with buildId-githash
  # 2. Latest path to indicate the latest build, with --delete option to delete stale files in the dest path
  aws s3 sync "$artifactspath" "$artifactsbucket"/"$projectpath"/"$buildidentifier"-"$githash"/artifacts --acl public-read
  aws s3 sync "$artifactspath" "$artifactsbucket"/"$projectpath"/"$latestpath" --delete --acl public-read
}

function build::gather_licenses() {
  local -r golang_version=$1
  # Setting this variable to local so that it always uses the local
  # bundled toolchain regardless of the version specified in go.mod file
  # It was introduced in this commit from Go1.21 onwards:
  # https://github.com/golang/go/commit/83dfe5cf62234427eae04131dc6e4551fd283463
  # Upstream issue:- https://github.com/google/go-licenses/issues/244
  export GOTOOLCHAIN=local

  build::common::use_go_version $golang_version
  if ! command -v go-licenses &> /dev/null
  then
    echo "go-licenses not found"
    exit
  fi

  local -r outputdir=$2
  local -r patterns=$3


  # Force deps to only be pulled form vendor directories
  # this is important in a couple cases where license files
  # have to be manually created
  export GOFLAGS=-mod=vendor
  # force platform to be linux to ensure all deps are picked up
  export GOOS=linux
  export GOARCH=amd64

  mkdir -p "${outputdir}/attribution"
  # attribution file generated uses the output go-deps and go-license to gather the neccessary
  # data about each dependency to generate the amazon approved attribution.txt files
  # go-deps is needed for module versions
  # go-licenses are all the dependencies found from the module(s) that were passed in via patterns
  go list -deps=true -json ./... | jq -s ''  > "${outputdir}/attribution/go-deps.json"

  go-licenses save --force $patterns --save_path="${outputdir}/LICENSES"

  # go-licenses can be a bit noisy with its output and lot of it can be confusing
  # the following messags are safe to ignore since we do not need the license url for our process
  NOISY_MESSAGES="cannot determine URL for|Error discovering URL|unsupported package host"
  go-licenses csv $patterns > "${outputdir}/attribution/go-license.csv" 2>  >(grep -vE "$NOISY_MESSAGES" >&2)

  if cat "${outputdir}/attribution/go-license.csv" | grep -q "^vendor\/golang.org\/x"; then
      echo " go-licenses created a file with a std golang package (golang.org/x/*)"
      echo " prefixed with vendor/.  This most likely will result in an error"
      echo " when generating the attribution file and is probably due to"
      echo " to a version mismatch between the current version of go "
      echo " and the version of go that was used to build go-licenses"
      exit 1
  fi

  if cat "${outputdir}/attribution/go-license.csv" | grep -e ",LGPL-" -e ",GPL-"; then
    echo " one of the dependencies is licensed as LGPL or GPL"
    echo " which is prohibited at Amazon"
    echo " please look into removing the dependency"
  fi


  # go-license is pretty eager to copy src for certain license types
  # when it does, it applies strange permissions to the copied files
  # which makes deleting them later awkward
  # this behavior may change in the future with the following PR
  # https://github.com/google/go-licenses/pull/28
  chmod -R 777 "${outputdir}/LICENSES"

  # most of the packages show up the go-license.csv file as the module name
  # from the go.mod file, storing that away since the source dirs usually get deleted
  MODULE_NAME=$(go mod edit -json | jq -r '.Module.Path')
  echo $MODULE_NAME > ${outputdir}/attribution/root-module.txt
}

function build::generate_attribution(){
  local -r golang_verson=$1
  local -r output_directory=${2:-"_output"}
  local -r attribution_file=${3:-"ATTRIBUTION.txt"}

  local -r root_module_name=$(cat ${output_directory}/attribution/root-module.txt)
  local -r go_path=$(build::common::get_go_path $golang_verson)
  local -r golang_version_tag=$($go_path/go version | grep -o "go[0-9].* ")

  if cat "${output_directory}/attribution/go-license.csv" | grep -e ",LGPL-" -e ",GPL-"; then
    echo " one of the dependencies is licensed as LGPL or GPL"
    echo " which is prohibited at Amazon"
    echo " please look into removing the dependency"
    exit 1
  fi

  generate-attribution $root_module_name "./" $golang_version_tag $output_directory
  cp -f "${output_directory}/attribution/ATTRIBUTION.txt" $attribution_file
}

function build::common::remove_go_path() {
  # This is the path where the specific go binary versions reside in our builder-base image
  export PATH=$(echo "$PATH" | sed -e "s/^\/go\/go[0-9\.]*\/bin://")

  # This is the path that will most likely be correct if running locally
  local -r quoted_go_path=$(build::common::re_quote $GOPATH)
  export PATH=$(echo "$PATH" | sed -e "s/^$quoted_go_path\/go[0-9\.]*\/bin://")
}

function build::common::get_go_path() {
  local -r version=$1

  # This is the path where the specific go binary versions reside in our builder-base image
  local -r gorootbinarypath="/go/go${version}/bin"
  # This is the path that will most likely be correct if running locally
  local -r gopathbinarypath="$GOPATH/go${version}/bin"
  if [ -d "$gorootbinarypath" ]; then
    echo $gorootbinarypath
  elif [ -d "$gopathbinarypath" ]; then
    echo $gopathbinarypath
  else
    # not in builder-base, probably running in dev environment
    # return default go installation
    local -r which_go=$(which go)
    echo "$(dirname $which_go)"
  fi
}

function build::common::use_go_version() {
  local -r version=$1

  local -r gobinarypath=$(build::common::get_go_path $version)
  echo "Adding $gobinarypath to PATH"
  # Adding to the beginning of PATH to allow for builds on specific version if it exists
  export PATH=${gobinarypath}:$PATH
}

function build::common::re_quote() {
    local -r to_escape=$1
    sed 's/[][()\.^$\/?*+]/\\&/g' <<< "$to_escape"
}

# This function returns all release branches of EKS Anywhere from GitHub.
function build::common::get_release_branches() {
  echo "$(git ls-remote https://github.com/aws/eks-anywhere.git | grep -oE 'refs/heads/release-[0-9]+\.[0-9]+' | xargs -I_ echo _ | cut -d/ -f3)"
}

# This function returns the two latest release branches of EKS Anywhere,
# that is, release branches for versions N and N-1 in that order.
function build::common::get_latest_release_branches() {
  local -r release_branches=("$@")

  latest_minor=0
  for branch in "${release_branches[@]}"; do
    minor_version=${branch##release-0.}
    if [ $minor_version -gt $latest_minor ] && [ "$(build::common::get_latest_release_for_release_branch $branch)" != "" ]; then
      latest_minor=$minor_version
    fi
  done

  echo "release-0.${latest_minor} release-0.$(($latest_minor-1))"
}

# This function returns the latest release version for the given release branch
# by parsing the EKS Anywhere releases manifest.
function build::common::get_latest_release_for_release_branch() {
  release_branch=$1

  minor_version="v${release_branch##release-}"
  latest_release=$(curl --silent "https://anywhere-assets.eks.amazonaws.com/releases/eks-a/manifest.yaml" | yq ".spec.releases[].version" | grep $minor_version | tail -1)

  echo $latest_release
}
