#!/bin/sh

# Determines the start and end commits for a CI job.
# Works under Azure Pipelines, CircleCI, GitHub Actions, and Travis CI.

# Typical use:
#
#   if [ -d /tmp/plume-scripts ] ; then
#     git -C /tmp/plume-scripts pull -q > /dev/null 2>&1
#   else
#     mkdir -p /tmp && git -C /tmp clone --depth=1 -q https://github.com/plume-lib/plume-scripts.git
#   fi
#   eval "$(/tmp/plume-scripts/git-changes DEFAULT-ORGANIZATION)"
#
# Alternatively, to use a specific version of this script rather than the latest:
#
#   if [ ! -d /tmp/plume-scripts ] ; then
#     mkdir -p /tmp/$USER
#     git -C /tmp/$USER clone --revision=e768754ffafad4a9f1e26c5305591d4061d890b4 --depth=1 -q https://github.com/plume-lib/plume-scripts.git
#   fi
#   eval "$(/tmp/plume-scripts/git-changes DEFAULT-ORGANIZATION)"

# Either snippet of code sets these variables:
# CI_COMMIT_RANGE: An argument to `git diff` with the range of commit IDs.
#    (Don't use it with `git log`, which interprets its argument differently.)
# CI_COMMIT_RANGE_START: The start element of CI_COMMIT_RANGE, a commit ID/SHA.
# CI_COMMIT_RANGE_END: The end element of CI_COMMIT_RANGE, a commit ID/SHA.
#
# If called not under CI, or under CI but not in the main or toplevel
# clone, then gets information from the local repository.

# Requires the `jq` program and either `curl` or `wget`.
#
# If environment variable GITHUB_PAT or GH_TOKEN is set when this script is
# called, it is used as GitHub Personal Access Token when making GitHub API
# calls.  This can avoid "403 rate limit exceeded" failures.

# PROBLEM: The directory from which git-changes is running must override environment
# variables, because a CI job may itself call "git clone" and then later call
# git-changes from that clone.  Idea: check the directory name.

# SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)"
SCRIPT_NAME="$(basename -- "$0")"

DEBUG=""
DEFAULT_ORGANIZATION=""
while [ "$#" -gt 0 ]; do
  case $1 in
    --verbose)
      VERBOSE="--verbose"
      ;;
    --debug)
      DEBUG="--debug"
      VERBOSE="--verbose"
      ;;
    *)
      if [ -n "${DEFAULT_ORGANIZATION}" ]; then
        usage="Usage: ${SCRIPT_NAME} [--verbose] [--debug] [DEFAULT-ORGANIZATION]"
        echo "echo \"$usage\";"
        echo "$usage" >&2
        echo "exit 2"
        exit 2
      else
        DEFAULT_ORGANIZATION="$1"
      fi
      ;;
  esac
  shift
done

if [ "$DEBUG" = "--debug" ]; then
  # `env` only prints EXPORTED variables, so `set` is usually more useful.
  echo "echo ${SCRIPT_NAME} is running in $(pwd)"
  echo
  echo 'echo ---------------- start of env'
  env -0 | sort -z | tr '\0' '\n' | sed -e 's/^/echo /'
  echo 'echo ---------------- end of env'
  echo
  echo 'echo ---------------- start of set'
  set | sed -e 's/^/echo /'
  echo 'echo ---------------- end of set'
  echo
  echo 'echo ---------------- start of git status'
  git --no-pager status | sed -e 's/^/echo /'
  echo 'echo ---------------- end of git status'
  echo
  echo 'echo ---------------- start of all branches'
  git --no-pager branch -a | sed -e 's/^/echo /'
  echo 'echo ---------------- end of all branches'
  echo
  echo 'echo ---------------- start of all remote branches'
  git --no-pager branch -r | sed -e 's/^/echo /'
  echo 'echo ---------------- end of all branches'
  echo
  echo 'echo ---------------- start of 1000 lines of git log'
  git --no-pager log --graph | head --lines=1000 | sed -e 's/^/echo /'
  echo 'echo ---------------- end of 1000 lines of git log'
  echo
fi

### Variables

# "GITHUB_PAT" is a GitHub Personal Access Token.
# TODO: Should this be "Authorization: Bearer <YOUR-TOKEN>" instead?  See https://docs.github.com/en/rest/pulls/pulls#get-a-pull-request
if [ -n "$GITHUB_PAT" ]; then
  auth_token="$GITHUB_PAT"
elif [ -n "$GH_TOKEN" ]; then
  auth_token="$GH_TOKEN"
else
  auth_token=""
fi

### Prerequisites

# TODO: Try to not access the GitHub REST API, because it can get throttled.
if [ -n "$(command -v curl 2> /dev/null)" ]; then
  http_get_tool="curl"
elif [ -n "$(command -v wget 2> /dev/null)" ]; then
  http_get_tool="wget"
else
  echo "echo Neither curl nor wget is installed"
  exit 2
fi

# get_url URL: fetch URL to standard output, sending an Authorization header
# if $auth_token is set.  The header is passed as a single argument rather than
# being word-split out of a command string; that avoids mangling the header and
# leaking the token into stray connection attempts.
get_url() {
  if [ "$http_get_tool" = "curl" ]; then
    if [ -n "$auth_token" ]; then
      curl -s --header "Authorization: token $auth_token" "$1"
    else
      curl -s "$1"
    fi
  else
    if [ -n "$auth_token" ]; then
      wget -q -O - --header="Authorization: token $auth_token" "$1"
    else
      wget -q -O - "$1"
    fi
  fi
}

if [ -z "$(command -v jq 2> /dev/null)" ]; then
  echo "echo jq is not installed"
  exit 2
fi

### Functions

# Returns either `git rev-parse HEAD^1` if HEAD^1 exists, or `git rev-parse HEAD`.
# HEAD^1 does not exist if the clone was created with `git clone --depth=1`.
head1() {
  if git rev-parse --verify --quiet HEAD^1 > /dev/null 2>&1; then
    git rev-parse HEAD^1
  else
    git rev-parse HEAD
  fi
}

### Organization

## Continuous integration services
if [ "$TRAVIS" = "true" ]; then
  CI_ORGANIZATION=${TRAVIS_PULL_REQUEST_SLUG%/*}
  if [ -z "$CI_ORGANIZATION" ]; then
    CI_ORGANIZATION=${TRAVIS_REPO_SLUG%/*}
  fi
elif [ -n "$AZURE_HTTP_USER_AGENT" ]; then
  # TODO: Can I implement this without a GitHub API request?
  if [ "$BUILD_REASON" = "PullRequest" ]; then
    url="https://api.github.com/repos/${BUILD_REPOSITORY_NAME}/pulls/${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}"
    pull_json="$(get_url "${url}" 2>&1 | tr -d '[:cntrl:]')"
    SLUG=$(printf '%s' "${pull_json}" | jq .head.label | sed 's/"//g')
    if [ -z "$SLUG" ]; then
      echo "echo ${SCRIPT_NAME} error: bad output for ${url}"
      if [ -n "$auth_token" ]; then
        echo "echo auth_token is set."
      fi
      echo "echo ---- start of wget output ----"
      # shellcheck disable=SC2001
      printf '%s' "${pull_json}" | sed -e 's/^/echo /'
      echo "echo ---- end of wget output ----"
      exit 2
    fi
    CI_ORGANIZATION=${SLUG%:*}
  else
    CI_ORGANIZATION=${BUILD_REPOSITORY_NAME%/*}
    # CI_REPO=${BUILD_REPOSITORY_NAME##*/}
  fi
elif [ -n "$CIRCLE_PR_USERNAME" ]; then
  CI_ORGANIZATION="$CIRCLE_PR_USERNAME"
elif [ -n "$GITHUB_HEAD_REF" ]; then
  # GitHub Actions pull request

  # TODO: Can I implement this without a GitHub API request?
  # Is GITHUB_ACTOR equal to CI_ORGANIZATION, maybe??  (Need to observe beyond mernst.)
  GITHUB_PR_NUMBER=${GITHUB_REF_NAME%/merge}
  url="https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${GITHUB_PR_NUMBER}"
  pull_json="$(get_url "${url}" 2>&1 | tr -d '[:cntrl:]')"
  CI_ORGANIZATION=$(printf '%s' "${pull_json}" | jq -r '.head.repo.owner.login')
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo GitHub Actions pull request, GITHUB_HEAD_REF=${GITHUB_HEAD_REF}"
    echo "echo GITHUB_PR_NUMBER=${GITHUB_PR_NUMBER}"
    echo "echo CI_ORGANIZATION=${CI_ORGANIZATION}"
  fi
fi

## Git clone
if [ -z "$CI_ORGANIZATION" ]; then
  URL=$(git config --get remote.origin.url)
  # This removes the leading part of two possible forms for a URL:
  #   https://github.com/mernst/annotation-tools
  #   git@github.com:mernst/annotation-tools.git
  SLUG=${URL#https://github.com/}
  SLUG=${SLUG#git@github.com:}
  CI_ORGANIZATION=${SLUG%/*}
  # TODO: Maybe add a sanity check here.
fi

## Default
if [ -z "$CI_ORGANIZATION" ]; then
  CI_ORGANIZATION="${DEFAULT_ORGANIZATION}"
fi

if [ "$DEBUG" = "--debug" ]; then
  echo "echo CI_ORGANIZATION=${CI_ORGANIZATION}"
fi

### Other information (besides organization)

## Both of these commands for DEFAULT_BRANCH_NAME seem to work.
# DEFAULT_BRANCH_NAME=$(git ls-remote --symref "$(git config --get remote.origin.url)" HEAD | awk '/^ref:/ {sub(/refs\/heads\//, "", $2); print $2}')
DEFAULT_BRANCH_NAME=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)
if [ "$DEBUG" = "--debug" ]; then
  echo "echo remote's DEFAULT_BRANCH_NAME=$DEFAULT_BRANCH_NAME;"
  if [ -n "${URL}" ]; then
    echo "HEAD of ${URL} = $(git ls-remote --symref "${URL}" HEAD)" 2>&1 | sed -e 's/^/echo /'
  else
    echo "echo URL is empty (is set only for non-CI runs);"
  fi
  echo "echo current branch: $(git branch --show-current);"
  echo "echo current repo: $(git config --get remote.origin.url);"
fi

if [ -n "$SYSTEM_PULLREQUEST_TARGETBRANCH" ]; then
  ## Azure Pipelines pull request
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo Azure Pipelines pull request $SYSTEM_PULLREQUEST_TARGETBRANCH;"
  fi
  # WARNING!  $BUILD_SOURCEBRANCHNAME is just a name, not a full ref path.  For example,
  # $BUILD_SOURCEBRANCHNAME may be "merge" when $BUILD_SOURCEBRANCH is "refs/pull/2971/merge".
  CI_BRANCH=$SYSTEM_PULLREQUEST_SOURCEBRANCH
  # For CI_COMMIT_RANGE_START:  HEAD = $BUILD_SOURCEVERSION is a commit created by Azure; it isn't
  # in the repo.  Its first child HEAD^1 is the target branch (e.g., master); use that because
  # $SYSTEM_PULLREQUEST_TARGETBRANCH is not fetched into this repo.
  CI_COMMIT_RANGE_START=$(head1)
  CI_COMMIT_RANGE_END=$SYSTEM_PULLREQUEST_SOURCECOMMITID
  CI_COMMIT_RANGE=${CI_COMMIT_RANGE_START}...${CI_COMMIT_RANGE_END}
elif [ -n "$BUILD_SOURCEBRANCH" ]; then
  # Azure Pipelines build for a branch (possibly master).
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo Azure Pipelines build for a branch $BUILD_SOURCEBRANCH;"
    echo "echo SYSTEM_PULLREQUEST_TARGETBRANCH=$SYSTEM_PULLREQUEST_TARGETBRANCH is not set but BUILD_SOURCEBRANCH=$BUILD_SOURCEBRANCH;"
  fi
  # In Azure Pipelines:  BUILD_SOURCEBRANCH is a full ref path like "refs/heads/mybranch";
  # BUILD_SOURCEBRANCHNAME is a short name like "mybranch"; $BUILD_SOURCEVERSION is a commit ID.
  # Some clients such as `git-clone-related` MUST be given a branch name, not a commit ID.
  CI_BRANCH=$BUILD_SOURCEBRANCHNAME
  if [ "$BUILD_SOURCEBRANCHNAME" = "$DEFAULT_BRANCH_NAME" ]; then
    DEFAULT_CI_COMMIT_RANGE_START=$(head1)

    # If a build fails, we would like to continue failing subsequent
    # builds until the problem is fixed.  Using `git rev-parse HEAD^1`
    # does not do that, because there could have been multiple pushes
    # since the last successful CI job.
    # Pass argument to ci-last-success.py, because in a pull request
    # we are only interested in the last success on the master branch,
    # not on the feature branch!  The feature branch might have many
    # succeeding jobs and then ci-last-success.py will return just
    # part of the pull request's diffs.

    ## # ci-last-success.py currently doesn't work because api.github.com returns "state: pending".
    # # TODO: wrap all this in a ci-last-commit.sh shell script.
    # if pip3 list --format columns | tail -n +1 | grep -q '^requests ' ; then
    #   # echo "echo \"Python requests package is already installed\";"
    #   :
    # else
    #   (sudo pip3 install requests || pip3 install --user requests || true) > /dev/null 2>&1
    # fi
    # if pip3 list --format columns | tail -n +1 | grep -q '^requests ' ; then
    #   CI_COMMIT_RANGE_START=$(${SCRIPT_DIR}/ci-last-success.py ${CI_ORGANIZATION} ${CI_REPO} ${DEFAULT_CI_COMMIT_RANGE_START})
    #   if [ -z "$CI_COMMIT_RANGE_START" ] ; then
    #     echo "echo \"WARNING: ci-last-success.py script failed; just considering last commit.\";"
    #     CI_COMMIT_RANGE_START=${DEFAULT_CI_COMMIT_RANGE_START}
    #   elif ! git cat-file -e ${CI_COMMIT_RANGE_START}^{commit}; then
    #     # The commit is not in the repository.  Maybe it is older than the
    #     # commits pulled into this repository.  This can happen when
    #     # api.github.com incorrectly returns "state: pending" for successful
    #     # jobs.
    #     echo "echo \"WARNING: ci-last-success.py script returned commit not in repository; using last commit.\";"
    #     CI_COMMIT_RANGE_START=${DEFAULT_CI_COMMIT_RANGE_START}
    #   else
    #     echo "echo \"ci-last-success.py ${CI_ORGANIZATION} ${CI_REPO} succeeded with $CI_COMMIT_RANGE_START.\";"
    #   fi
    # else
    #   echo "echo \"WARNING: Could not run ci-last-success.py script; just considering last commit.\";"
    #   CI_COMMIT_RANGE_START=${DEFAULT_CI_COMMIT_RANGE_START}
    # fi

    ## Use this because ci-last-success.py currently doesn't work.
    CI_COMMIT_RANGE_START=${DEFAULT_CI_COMMIT_RANGE_START}
  else
    git fetch origin "$DEFAULT_BRANCH_NAME"
    CI_COMMIT_RANGE_START=$(git rev-parse "origin/$DEFAULT_BRANCH_NAME")
  fi
  CI_COMMIT_RANGE_END=$BUILD_SOURCEVERSION
  CI_COMMIT_RANGE=${CI_COMMIT_RANGE_START}...${CI_COMMIT_RANGE_END}
elif [ "$TRAVIS" = "true" ]; then
  ## Travis CI
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo Travis CI $TRAVIS;"
  fi
  CI_BRANCH=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}
  if [ "$VERBOSE" = "--verbose" ]; then
    echo "echo TRAVIS_PULL_REQUEST_BRANCH=$TRAVIS_PULL_REQUEST_BRANCH;"
    echo "echo TRAVIS_BRANCH=$TRAVIS_BRANCH;"
    echo "echo CI_BRANCH=$CI_BRANCH;"
  fi
  # $TRAVIS_COMMIT_RANGE is empty for builds triggered by the initial commit of a new branch.
  CI_COMMIT_RANGE=$TRAVIS_COMMIT_RANGE

elif [ -n "$CIRCLECI" ]; then
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo CircleCI, CIRCLE_PULL_REQUEST=$CIRCLE_PULL_REQUEST;"
  fi
  if [ -n "$CIRCLE_PR_NUMBER" ]; then
    # TODO: Can I implement this without a GitHub API request?
    url="https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pulls/${CIRCLE_PR_NUMBER}"
    pull_json="$(get_url "${url}" | tr -d '[:cntrl:]')"
    CI_BRANCH=$(printf '%s' "${pull_json}" | jq -r '.head.ref')
    CI_COMMIT_RANGE_START=$(printf '%s' "${pull_json}" | jq -r '.base.sha')
    CI_COMMIT_RANGE_END=$(printf '%s' "${pull_json}" | jq -r '.head.sha')
    CI_COMMIT_RANGE="${CI_COMMIT_RANGE_START}...${CI_COMMIT_RANGE_END}"

    if ! git rev-parse --verify -q "${CI_COMMIT_RANGE_START}^{commit}" > /dev/null; then
      git fetch --depth=25 origin
      if ! git rev-parse --verify -q "${CI_COMMIT_RANGE_START}^{commit}" > /dev/null; then
        git fetch --unshallow origin
        if ! git rev-parse --verify -q "${CI_COMMIT_RANGE_START}^{commit}" > /dev/null; then
          echo "echo cannot find commit range start ${CI_COMMIT_RANGE_START} in origin"
          exit 2
        fi
      fi
    fi
  else
    CI_BRANCH=$CIRCLE_BRANCH
  fi

elif [ -n "${GITHUB_HEAD_REF}" ]; then
  # GitHub Actions pull request (no special handling is required for non-PR GitHub Actions jobs).
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo GitHub Actions pull request https://github.com/${GITHUB_REPOSITORY}/pull/${GITHUB_PR_NUMBER};"
    echo "echo   GITHUB_BASE_REF=${GITHUB_BASE_REF};"
    echo "echo   GITHUB_REF_NAME=${GITHUB_REF_NAME};"
    echo "echo   GITHUB_SHA=${GITHUB_SHA};"
    # GITHUB_HEAD_REF is in the PR fork which is not available.
    echo "echo   GITHUB_HEAD_REF=${GITHUB_HEAD_REF};"
  fi
  # At this point, these two are the same:
  # git rev-parse HEAD
  # git rev-parse remotes/pull/"${GITHUB_REF_NAME}"

  GITHUB_PR_NUMBER=${GITHUB_REF_NAME%/merge}

  CI_BRANCH="${GITHUB_HEAD_REF}"
  if ! CI_COMMIT_RANGE_START=$(git rev-parse "${GITHUB_BASE_REF}" 2> /dev/null); then
    if ! CI_COMMIT_RANGE_START=$(git rev-parse remotes/origin/"${GITHUB_BASE_REF}" 2> /dev/null); then
      if ! CI_COMMIT_RANGE_START=$(git rev-parse refs/remotes/origin/"${GITHUB_BASE_REF}" 2> /dev/null); then
        if ! CI_COMMIT_RANGE_START=$(git rev-parse refs/remotes/origin/"${DEFAULT_BRANCH_NAME}" 2> /dev/null); then
          pull_json="$(get_url "https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${GITHUB_PR_NUMBER}" | tr -d '[:cntrl:]')"
          if ! CI_COMMIT_RANGE_START=$(printf '%s' "${pull_json}" | jq -e -r '.base.sha // empty'); then
            echo "echo ${SCRIPT_NAME} in $(pwd): cannot find commit range start, tried ${GITHUB_BASE_REF} and remotes/origin/${GITHUB_BASE_REF} and refs/remotes/origin/${GITHUB_BASE_REF} and refs/remotes/origin/${DEFAULT_BRANCH_NAME}"
            echo 'echo ---------------- start of set'
            set | sed -e 's/^/echo /'
            echo 'echo ---------------- end of set'
            # echo "echo git branch:"
            # git --no-pager branch -a | cut -c 3- | sed -e 's/^/echo "echo /' -e 's/$/"/'
            echo "echo git log --graph:"
            git log --graph | head --lines=1000 | sed -e 's/^/echo "echo /' -e 's/$/"/'
            echo "echo git show:"
            git --no-pager show | head --lines=1000 | sed -e 's/^/echo "echo /' -e 's/$/"/'
            CI_COMMIT_RANGE_START=$(git rev-parse "${GITHUB_SHA}"^)
            # echo "echo exiting"
            # # exit 2
            # exit
          fi
        fi
      fi
    fi
  fi
  # These are the same: `sha`=`echo $GITHUB_SHA`=`git rev-parse remotes/pull/"${GITHUB_REF_NAME}"`.
  CI_COMMIT_RANGE_END=${GITHUB_SHA}
  CI_COMMIT_RANGE="${CI_COMMIT_RANGE_START}...${CI_COMMIT_RANGE_END}"

  # If this elif is reached, it's not a pull request (except maybe a re-run Azure Pipelines pull request).
elif [ -n "$GITHUB_ACTIONS" ]; then
  # GitHub Actions, non-pull-request.
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo GitHub Actions, not a pull request, GITHUB_REF_NAME=${GITHUB_REF_NAME}, GITHUB_SHA=${GITHUB_SHA}, head=$(git rev-parse HEAD);"
  fi

  if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
    echo "echo GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME}"
    exit 2
  fi

  # GITHUB_REF_NAME may be a branch name or "40/merge".
  if [ -n "$(git branch --list "${GITHUB_REF_NAME}")" ]; then
    CI_BRANCH="${GITHUB_REF_NAME}"
  else
    # TODO: Can I implement this without a GitHub API request?
    url="https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pulls/${CIRCLE_PR_NUMBER}"
    pull_json="$(get_url "${url}" | tr -d '[:cntrl:]')"
    CI_BRANCH=$(printf '%s' "${pull_json}" | jq -r '.head.ref')
  fi
  CI_COMMIT_RANGE_END="${GITHUB_SHA}"

else

  if [ "$DEBUG" = "--debug" ]; then
    echo "echo Else clause: no CI environment detected;"
  fi
  # git 2.22 and later has `git branch --show-current`; CircleCI doesn't have that version yet.
  CI_BRANCH=$(git rev-parse --abbrev-ref HEAD)
  # In an Azure Pipelines pull request, `git branch` yields "(HEAD detached at pull/4/merge)".
  # If you re-run a pull request via "Queue" rather than "Rebuild",
  # variables SYSTEM_PULLREQUEST_TARGETBRANCH and SYSTEM_PULLREQUEST_SOURCEBRANCH are not set.
  if [ "$CI_BRANCH" = '(HEAD' ]; then
    CI_BRANCH=$DEFAULT_BRANCH_NAME
  fi
fi

if [ "$DEBUG" = "--debug" ]; then
  echo "echo CI_BRANCH=${CI_BRANCH};"
  echo "echo CI_COMMIT_RANGE_START=${CI_COMMIT_RANGE_START};"
  echo "echo CI_COMMIT_RANGE_END=${CI_COMMIT_RANGE_END};"
  echo "echo CI_COMMIT_RANGE=${CI_COMMIT_RANGE};"
fi

## The above may not have set CI_COMMIT_RANGE.  Set it.

# Separate from "It's not a pull request" because sometimes this is not set for Travis.
if [ -z "$CI_COMMIT_RANGE" ]; then
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo Setting CI_COMMIT_RANGE from CI_BRANCH=$CI_BRANCH;"
  fi
  if [ "$CI_BRANCH" = "$DEFAULT_BRANCH_NAME" ]; then
    if git show --summary HEAD | grep -q ^Merge:; then
      CI_COMMIT_RANGE_START=$(git merge-base HEAD^1 HEAD^2)
    else
      CI_COMMIT_RANGE_START=$(head1)
    fi
    CI_COMMIT_RANGE_END=$(git rev-parse HEAD)
  else
    # "origin/$DEFAULT_BRANCH_NAME" is accurate when the pull request was
    # created, but it doesn't change when force-push occurs (as Renovate does),
    # making it out of date.
    case $CI_BRANCH in
      renovate/*) CI_COMMIT_RANGE_START=$(head1) ;;
      *)
        if git rev-parse --verify --quiet "origin/$DEFAULT_BRANCH_NAME" > /dev/null 2>&1; then
          CI_COMMIT_RANGE_START=$(git rev-parse "origin/$DEFAULT_BRANCH_NAME")
          if [ "$DEBUG" = "--debug" ]; then
            echo "echo Set CI_COMMIT_RANGE_START=$CI_COMMIT_RANGE_START from git rev-parse origin/$DEFAULT_BRANCH_NAME;"
          fi
        else
          if git show --summary HEAD | grep -q ^Merge:; then
            CI_COMMIT_RANGE_START=$(git merge-base HEAD^1 HEAD^2)
          else
            CI_COMMIT_RANGE_START=$(head1)
          fi
        fi
        ;;
    esac
    CI_COMMIT_RANGE_END=$(git rev-parse "$CI_BRANCH")
  fi
  CI_COMMIT_RANGE=${CI_COMMIT_RANGE_START}...${CI_COMMIT_RANGE_END}
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo CI_BRANCH=${CI_BRANCH};"
    echo "echo CI_COMMIT_RANGE_START=${CI_COMMIT_RANGE_START};"
    echo "echo CI_COMMIT_RANGE_END=${CI_COMMIT_RANGE_END};"
    echo "echo CI_COMMIT_RANGE=${CI_COMMIT_RANGE};"
  fi
fi

if [ -z "$CI_COMMIT_RANGE_START" ]; then
  if [ "$DEBUG" = "--debug" ]; then
    echo "echo Setting CI_COMMIT_RANGE_START from CI_COMMIT_RANGE=$CI_COMMIT_RANGE;"
  fi
  CI_COMMIT_RANGE_START=${CI_COMMIT_RANGE%%.*}
  CI_COMMIT_RANGE_END=${CI_COMMIT_RANGE##*.}
  if [ -z "$CI_COMMIT_RANGE_START" ]; then
    CI_COMMIT_RANGE_START=${CI_COMMIT_RANGE_END}
  fi
fi

# Avoid errors regarding empty range
if [ "$CI_COMMIT_RANGE_START" = "$CI_COMMIT_RANGE_END" ]; then
  if [ "$VERBOSE" = "--verbose" ]; then
    echo "echo Resetting CI_COMMIT_RANGE_START because CI_COMMIT_RANGE=$CI_COMMIT_RANGE;"
  fi
  if git show --summary "$CI_COMMIT_RANGE_END" | grep -q ^Merge:; then
    CI_COMMIT_RANGE_START=$(git merge-base "$CI_COMMIT_RANGE_END^1" "$CI_COMMIT_RANGE_END^2")
  else
    if git rev-parse --verify --quiet "$CI_COMMIT_RANGE_END^1" > /dev/null 2>&1; then
      CI_COMMIT_RANGE_START=$(git rev-parse "$CI_COMMIT_RANGE_END^1")
    fi
  fi
  # The start and end commits could still be the same, if the current branch was cloned with
  # --depth=1.
  CI_COMMIT_RANGE="${CI_COMMIT_RANGE_START}...${CI_COMMIT_RANGE_END}"
fi

if [ "$VERBOSE" = "--verbose" ]; then
  if [ -n "${pull_json}" ]; then
    echo "echo CI_ACCESSED_GITHUB_API=true;"
  else
    echo "echo CI_ACCESSED_GITHUB_API=false;"
  fi
fi

if [ "$VERBOSE" = "--verbose" ]; then
  echo 'echo ---------------- start of first 10000 lines of diff:'
  git --no-pager diff "${CI_COMMIT_RANGE}" | head --lines=10000 | sed -e 's/^/echo "echo /' -e 's/$/"/'
  echo 'echo ---------------- end of first 10000 lines of diff.'
fi

# The diff might be empty if the last commit is the revert of the one before it.

### Print it out

if [ "$VERBOSE" = "--verbose" ]; then
  echo "echo CI_ORGANIZATION=$CI_ORGANIZATION;"
fi
echo "CI_ORGANIZATION=$CI_ORGANIZATION; export CI_ORGANIZATION;"
if [ "$VERBOSE" = "--verbose" ]; then
  echo "echo CI_BRANCH=$CI_BRANCH;"
fi
echo "CI_BRANCH=$CI_BRANCH; export CI_BRANCH;"
if [ "$VERBOSE" = "--verbose" ]; then
  echo "echo CI_COMMIT_RANGE_START=$CI_COMMIT_RANGE_START;"
fi
echo "CI_COMMIT_RANGE_START=$CI_COMMIT_RANGE_START; export CI_COMMIT_RANGE_START;"
if [ "$VERBOSE" = "--verbose" ]; then
  echo "echo CI_COMMIT_RANGE_END=$CI_COMMIT_RANGE_END;"
fi
echo "CI_COMMIT_RANGE_END=$CI_COMMIT_RANGE_END; export CI_COMMIT_RANGE_END;"
if [ "$VERBOSE" = "--verbose" ]; then
  echo "echo CI_COMMIT_RANGE=$CI_COMMIT_RANGE;"
fi
echo "CI_COMMIT_RANGE=$CI_COMMIT_RANGE; export CI_COMMIT_RANGE;"
