#!/bin/sh

# Given a file of warnings, reports only those that are in the diff for the
# current pull request.  Works for Azure Pipelines, CircleCI, GitHub Actions,
# and Travis CI.  Exit status is non-zero if any warnings are output.
# (On CircleCI, it depends on environment variable CIRCLE_COMPARE_URL;
# see documentation of `ci-info` script for details.)

# Example use:
#
#   if [ -d /tmp/$USER/plume-scripts ] ; then
#     git -C /tmp/$USER/plume-scripts pull -q > /dev/null 2>&1
#   else
#     mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/plume-scripts.git
#   fi
#   (command-that-issues-warnings > /tmp/warnings.txt 2>&1) || true
#   /tmp/$USER/plume-scripts/ci-lint-diff /tmp/warnings.txt
#
# If you get a warning that /tmp/diff is empty, here are two possible reasons:
#  * Your branch is identical to the base/upstream branch.  In this case, the pull
#    request is pointless.
#  * Your clone is shallow and does not contain all the commits.  You can fix that
#    by pulling upstream.
#
# Requires the `jq` program to be installed, when used in Azure Pipelines jobs.

DEBUG=""

if [ "$#" -eq 0 ]; then
  echo "Usage: $0 [--debug] WARNINGS-FILE" >&2
  exit 2
fi
if [ "$1" = "--debug" ]; then
  DEBUG="--debug"
  shift
fi
if [ "$#" -ne 1 ]; then
  echo "Usage: $0 [--debug] WARNINGS-FILE" >&2
  exit 2
fi
if ! [ -f "$1" ]; then
  echo "File $1 does not exist" >&2
  exit 2
fi
WARNINGSFILE=$1

set -e

if [ "$DEBUG" = "--debug" ]; then
  env | sort
  git log --graph | head --lines=10000
fi

SCRIPTDIR="$(
  cd "$(dirname "$0")"
  pwd -P
)"

eval "$("${SCRIPTDIR}"/ci-info)"

# Ensure the commits mentioned in $CI_COMMIT_RANGE are available.
if ! git cat-file -e "${CI_COMMIT_RANGE_START}^{commit}"; then
  CI_LINT_DIFF_ERROR="Commit CI_COMMIT_RANGE_START=$CI_COMMIT_RANGE_START not found; clone more deeply."
fi
if ! git cat-file -e "${CI_COMMIT_RANGE_END}^{commit}"; then
  CI_LINT_DIFF_ERROR="Commit CI_COMMIT_RANGE_END=$CI_COMMIT_RANGE_END not found; clone more deeply."
fi

set +e
diff_file=$(mktemp /tmp/diff-XXXXXX.txt)
echo "git diff $CI_COMMIT_RANGE > $diff_file"
git --no-pager diff --exit-code "$CI_COMMIT_RANGE" > "$diff_file" 2>&1
git_status=$?
if [ $git_status -gt 1 ]; then
  CI_LINT_DIFF_ERROR="git diff exited with status $git_status"
  cat "$diff_file"
  git --no-pager diff --exit-code "$CI_COMMIT_RANGE"
  git --no-pager branch -a
  git --no-pager diff "$CI_COMMIT_RANGE"
elif [ ! -r "$diff_file" ]; then
  CI_LINT_DIFF_ERROR="$diff_file does not exist"
fi
if [ ! -s "$diff_file" ]; then
  CI_LINT_DIFF_ERROR="ERROR: Empty diff for $CI_COMMIT_RANGE.
Try pulling base branch (often main or master) into compare branch (often your feature branch).
Other causes of an empty diff include:
  * The commit range is a no-op, such as a commit and its revert.
  * HEAD is a merge commit and the merge took all its changes from its second argument.
  * Your compare branch does not differ from your base branch.
"
fi

if [ -n "$CI_LINT_DIFF_ERROR" ]; then
  echo
  echo "*****"
  echo "$CI_LINT_DIFF_ERROR"
  echo "*****"
  echo
  echo "This command will print diagnostic information, then exit."
  echo

  # This is not very effective because environment variables were set by
  # the previous invocation of ci-info.
  echo "${SCRIPTDIR}"/ci-info --debug
  "${SCRIPTDIR}"/ci-info --debug
  echo end of "${SCRIPTDIR}"/ci-info --debug
  echo

  echo git rev-parse \$CI_COMMIT_RANGE_START
  echo git rev-parse "$CI_COMMIT_RANGE_START"
  git rev-parse "$CI_COMMIT_RANGE_START"
  echo git rev-parse \$CI_COMMIT_RANGE_END
  echo git rev-parse "$CI_COMMIT_RANGE_END"
  git rev-parse "$CI_COMMIT_RANGE_END"
  echo git rev-parse remotes/origin/\$CI_COMMIT_RANGE_START
  echo git rev-parse "remotes/origin/$CI_COMMIT_RANGE_START"
  git rev-parse "remotes/origin/$CI_COMMIT_RANGE_START" || true
  echo git rev-parse remotes/origin/\$CI_COMMIT_RANGE_END
  echo git rev-parse "remotes/origin/$CI_COMMIT_RANGE_END"
  git rev-parse "remotes/origin/$CI_COMMIT_RANGE_END" || true
  echo
  echo git fetch
  git fetch
  echo
  echo git rev-parse "$CI_COMMIT_RANGE_START"
  git rev-parse "$CI_COMMIT_RANGE_START"
  echo git rev-parse "$CI_COMMIT_RANGE_END"
  git rev-parse "$CI_COMMIT_RANGE_END"
  echo git rev-parse "remotes/origin/$CI_COMMIT_RANGE_START"
  git rev-parse "remotes/origin/$CI_COMMIT_RANGE_START" || true
  echo git rev-parse "remotes/origin/$CI_COMMIT_RANGE_END"
  git rev-parse "remotes/origin/$CI_COMMIT_RANGE_END" || true
  echo
  echo "Branches:"
  git --no-pager branch -a
  echo
  echo "git remote show origin"
  git remote show origin
  echo
  echo "Re-execution of diff command:"
  echo git --no-pager diff --exit-code "$CI_COMMIT_RANGE"
  git --no-pager diff --exit-code "$CI_COMMIT_RANGE"
  echo
  echo "Second re-execution of diff command:"
  echo "git --no-pager diff --exit-code $CI_COMMIT_RANGE > /tmp/tmp.tmp && cat /tmp/tmp.tmp"
  git --no-pager diff --exit-code "$CI_COMMIT_RANGE" > /tmp/tmp.tmp && cat /tmp/tmp.tmp
  echo
  echo "Endpoints of commit range ($CI_COMMIT_RANGE_START and $CI_COMMIT_RANGE_END):"
  git --no-pager show "$CI_COMMIT_RANGE_START"
  git --no-pager show "$CI_COMMIT_RANGE_END"
  echo
  echo "Current directory:"
  pwd
  echo
  echo "Contents of /tmp:"
  ls -l /tmp/
  echo
  echo "Environment:"
  env | sort
  echo
  echo "*****"
  echo "$CI_LINT_DIFF_ERROR"
  echo "*****"
  exit 2
fi

if [ "$DEBUG" = "--debug" ]; then
  echo "warnings file $WARNINGSFILE:"
  cat "$WARNINGSFILE"
  echo "end of warnings file $WARNINGSFILE."
  echo "$diff_file file:"
  cat "$diff_file"
  echo "end of $diff_file file."
fi

set -e

"${SCRIPTDIR}"/lint-diff.py $DEBUG --guess-strip "$diff_file" "$WARNINGSFILE"
