#!/bin/bash

set -x
set -e
set -u

if [ -r /etc/jenkins/debian_glue ] ; then
  . /etc/jenkins/debian_glue
fi

[ -n "${DEBEMAIL:-}" ] || DEBEMAIL="jenkins-debian-glue Autobuilder <jenkins@`hostname -f`>"
export DEBEMAIL

[ -n "${SOURCE_DIRECTORY:-}" ] || SOURCE_DIRECTORY='source' # where checkout of sources resides

if [ ! -d "${SOURCE_DIRECTORY}" ] ; then
  echo "Please run the script in the jenkins workspace." >&2
  echo 'NOTE: Make sure that Source Code Management -> Branches to build -> Advanced has "Local subdirectory for repo (optional)" configured to "source"' >&2
  exit 1
fi

if [ -z "${BUILD_NUMBER:-}" ] ; then
  echo "No BUILD_NUMBER defined, please run it in jenkins." >&2
  exit 1
fi

if [ -z "${GIT_COMMIT:-}" ] ; then
  echo "No GIT_COMMIT defined, please run it with git-plugin in jenkins. ">&2
  exit 1
fi

JENKINS_DEBIAN_GLUE_VERSION=$(dpkg --list jenkins-debian-glue 2>/dev/null | awk '/^ii/ {print $3}')
if [ -n "${JENKINS_DEBIAN_GLUE_VERSION:-}" ] ; then
  echo "*** Running jenkins-debian-glue version $JENKINS_DEBIAN_GLUE_VERSION ***"
fi

gbp_opts() {
  # support overriding git-buildpackage options
  if [ -n "${GBP_OPTS:-}" ] ; then
    echo "Found environment variable GBP_OPTS, set to ${GBP_OPTS}"
  else
    GBP_OPTS=" -nc --git-force-create --git-ignore-new --git-ignore-branch -S -us -uc --git-verbose --git-builder=/bin/true --git-cleaner=/bin/true --git-export-dir= "
    echo "Using git-buildpackage default options provided by jenkins-debian-glue"
  fi
}

version_information() {
  # required for dpkg-parsechangelog
  if ! [ -r debian/changelog ] ; then
    echo "Error: debian/changelog could not be read. Is this really a Debian package?" >&2
    return 1
  fi

  # retrieve and adjust information
  ORIG_VERSION=$(dpkg-parsechangelog --count 1 | awk '/^Version/ {print $2}')
  INCREASED_VERSION=$(increase-version-number $ORIG_VERSION)
  DISTRIBUTION=$(dpkg-parsechangelog --count 1 | awk '/^Distribution/ {print $2}')

  # we want to get a version string like
  # $ORIG_VERSION+0~$TIMESTAMP.$BUILD_NUMBER~1.$GIT_ID
  # so the version is always increasing over time, no matter what
  TIMESTAMP="$(date -u +%Y%m%d%H%M%S)"
  BUILD_VERSION="${TIMESTAMP}.${BUILD_NUMBER}" # git-dch appends something like ~1.gbp5f433e then

  # we do NOT raise the version number, if we detect an unreleased version,
  # otherwise the released version would be older than our snapshot builds
  APPLY_VERSION_WORKAROUND=false
  if [ "$DISTRIBUTION" = "UNRELEASED" ] && dpkg --compare-versions "$ORIG_VERSION" lt "$INCREASED_VERSION" ; then
    echo "*** Not increasing version number as distribution is set to UNRELEASED ***"
    INCREASED_VERSION="$ORIG_VERSION"
    APPLY_VERSION_WORKAROUND=true
  fi

  VERSION_STRING="${INCREASED_VERSION}~${BUILD_VERSION}"

  if [ -n "${distribution:-}" ] ; then
    echo "Distribution variable found. Adding distribution specific version."
    VERSION_STRING="${VERSION_STRING}+${distribution}"
  fi

  echo "*** Version string set to $VERSION_STRING ***"
}

# support overriding git-dch options
dch_opts() {
  if [ -n "${DCH_OPTS:-}" ] ; then
    echo "Found environment variable DCH_OPTS, set to ${DCH_OPTS}"
  else
    DCH_OPTS="-S --multimaint-merge --ignore-branch"
    echo "Using default git-dch options (${DCH_OPTS})"
  fi

  # support overriding extra options for git-dch
  if [ -n "${DCH_EXTRA_OPTS:-}" ] ; then
    echo "Found environment variable DCH_EXTRA_OPTS, set to ${DCH_EXTRA_OPTS}"
  else
    DCH_EXTRA_OPTS="--new-version=${VERSION_STRING}"
    echo "Using extra git-dch options (${DCH_EXTRA_OPTS})"
  fi
}

# support overrides from gbp.conf
gbp_conf() {

  if [ "${IGNORE_GBP_CONF:-}" = "true" ] ; then
    echo "Skipping gbp_conf execution as requested via IGNORE_GBP_CONF ***"
    return 0
  fi

  # List of config files in order
  gbp_conf_files=(
  /etc/git-buildpackage/gbp.conf
  ~/.gbp.conf
  .gbp.conf
  debian/gbp.conf
  .git/gbp.conf
  )

  # Defaults
  UPSTREAM_BRANCH="upstream"
  DEBIAN_BRANCH="debian"
  PRISTINE_TAR="false"

  for gbp_conf_file in "${gbp_conf_files[@]}"; do
    if [ -f ${gbp_conf_file} ]; then
      local value;
      value=$(git config --file ${gbp_conf_file} --get DEFAULT.upstream-branch 2>&- || true)
      if [ -n "${value:-}" ]; then
        UPSTREAM_BRANCH=${value}
      fi

      value=$(git config --file ${gbp_conf_file} --get DEFAULT.debian-branch 2>&- || true)
      if [ -n "${value:-}" ]; then
        DEBIAN_BRANCH=${value}
      fi

      value=$(git config --file ${gbp_conf_file} --bool --get DEFAULT.pristine-tar 2>&- || true)
      if [ -n "${value:-}" ]; then
        PRISTINE_TAR=${value}
      fi
    fi
  done
}

create_local_branch() {
  [ -n "${1:-}" ] || return 1

  local BRANCH="$1"

  if git branch -a | grep -q "remotes/origin/${BRANCH}"'$' ; then
    git branch -D "${BRANCH}" || true
    git branch "${BRANCH}" "remotes/origin/${BRANCH}"
  else
    echo "NOTE: branch $BRANCH does not exist, ignoring request to checkout therefore."
  fi
}

git_dch_auto() {
  if $APPLY_VERSION_WORKAROUND ; then
    echo "Applying version workaround workaround"
    dch -b --distribution=UNRELEASED --newversion=$VERSION_STRING -- \
      "SNAPSHOT autobuild for unreleased $ORIG_VERSION via jenkins-debian-glue."
  else
    git-dch --auto $DCH_OPTS $DCH_EXTRA_OPTS
  fi
}

identify_latest_change() {
  # debian/changelog might be a symlink (e.g. because debian points to
  # pkg/debian), so make sure we don't access a non-existing file
  # by calling readlink
  git checkout -- $(readlink -f debian/changelog)

  local OLD_VERSION=$(dpkg-parsechangelog | awk '/^Version: / {print $2}')

  local last_tag=$(git describe $(git rev-list --tags='[^u]*' --max-count=1 HEAD))
  local last_merge=$(git describe $(git rev-list --all --merges --max-count=1 HEAD))
  local since=${last_tag}

  if [ -n "$last_merge" ] ; then
    local merge_date=$(git log ${last_merge} --pretty="format:%at" -1)
    local tag_date=$(git log ${last_tag} --pretty="format:%at" -1)
    if [ ${merge_date} -gt ${tag_date} ] ; then
      local since=${last_merge}
    fi
  fi

  echo "Latest tag [${last_tag:-}] / merge [${last_merge:-}] seems to be $since"
  git-dch -s "${since}" $DCH_OPTS $DCH_EXTRA_OPTS

  local NEW_VERSION=$(dpkg-parsechangelog | awk '/^Version: / {print $2}')

  if dpkg --compare-versions "$NEW_VERSION" lt "$OLD_VERSION" ; then
    echo "Calculated version is older than last release, falling back to auto mode."
    # debian/changelog might be a symlink (e.g. because debian points to
    # pkg/debian), so make sure we don't access a non-existing file
    # by calling readlink
    git checkout -- $(readlink -f debian/changelog)

    git_dch_auto
  fi
}

branch_checkout() {
  # git-dch and git-buildpackage refuse to operate on a single git
  # commit, so instead create a temporary branch to work on
  random_branch="jenkins-debian-glue-buildbranch$RANDOM"

  if [ -n "${tag:-}" ] && [ "${tag:-}" != "none" ] ; then
    echo "Making sure that random_branch $random_branch does not exist"
    git branch -D "$random_branch" || true

    echo "Checking out branch $random_branch based on tag $tag"
    git checkout -b "$random_branch" "$tag"
  elif [ -n "${branch:-}" ] ; then
    # prepend "origin" when checking out a branch, otherwise we might be
    # checking out a branch which doesn't exist locally yet, failing with:
    # | fatal: git checkout: updating paths is incompatible with switching branches.
    # | Did you intend to checkout '$BRANCH' which can not be resolved as commit?
    local checkout_branch

    case "$branch" in
      # rewrite refs/heads/FOO to refs/remotes/origin/FOO, otherwise it fails;
      # the refs/heads/FOO is something e.g. github pushes through its API
      refs/heads/*) checkout_branch="${branch/refs\/heads/refs/remotes/origin}" ;;

      # keep as it is when receiving origin/* already
          origin/*) checkout_branch="$branch" ;;

      # prepend "origin/" otherwise
                 *) checkout_branch="origin/${branch}" ;;
    esac

    echo "Making sure that random_branch $random_branch does not exist"
    git branch -D "$random_branch" || true

    echo "Checking out branch $random_branch based on branch $checkout_branch"
    git checkout -b "$random_branch" "$checkout_branch"
  else
    echo "Making sure that random_branch $random_branch does not exist"
    git branch -D "$random_branch" || true

    echo "Checking out branch $random_branch based on commit $GIT_COMMIT"
    git checkout -b "$random_branch" "$GIT_COMMIT"
  fi
}

changelog_generation() {
  # Allow skipping of changelog creation, e.g. to build a branch as-is
  if [ "${SKIP_DCH:-}" = "true" ] ; then
    echo "*** SKIP_DCH is set, not modifying debian/changelog therefore ***"
  else
    if [ -n "${tag:-}" ] && [ "${tag:-}" != "none" ] ; then
      echo "Tag parameter found (and not set to 'none'), not touching the changelog."
    elif [ "${1:-}" = "identify" ] ; then
      echo "Trying to identify latest tag / merge..."

      if ! git describe $(git rev-list --tags='[^u]*' --max-count=1 HEAD) >/dev/null ; then
        echo "Failed to identify latest change, falling back to auto mode."
        git_dch_auto
      else
        identify_latest_change
      fi
    elif [ -r debian/gbp.conf ] ; then
      echo "Found debian/gbp.conf, using git-dch with auto mode."
      git_dch_auto
    else
      echo "Using git-dch with auto mode."
      git_dch_auto
    fi

    # get rid of "UNRELEASED" distribution header, only
    # necessary if the debian/changelog was modified
    if [ -z "${tag:-}" ] ; then
      debchange --release ""
    fi
  fi
}

git_cleanup() {
  if [ -n "${SKIP_GIT_CLEANUP:-}" ] ; then
    echo "*** Skipping git_cleanup as requested via SKIP_QUILT_CLEANUP ***"
    return 0
  fi

  echo "*** Cleaning git repository to make sure there are no modified files ***"
  echo "*** Note: You can skip this step by setting SKIP_GIT_CLEANUP=true ***"
  git clean -fxd
  git reset --hard HEAD
}

quilt_cleanup() {
  if [ -n "${SKIP_QUILT_CLEANUP:-}" ] ; then
    echo "*** Skipping quilt_cleanup as requested via SKIP_QUILT_CLEANUP ***"
    return 0
  fi

  echo "*** Unapplying all possibly applied quilt patches and removing quilt directory ***"
  echo "*** Note: You can skip this step by setting SKIP_QUILT_CLEANUP=true ***"
  quilt pop -a || true
  rm -rf .pc
}

# main execution
echo "*** source package build phase ***"
rm -f ./* || true

if [ -n "${PRE_SOURCE_HOOK:-}" ] ; then
  echo "*** Found environment variable PRE_SOURCE_HOOK, set to ${PRE_SOURCE_HOOK:-} ***"
  sh ${PRE_SOURCE_HOOK:-}
fi

cd "$SOURCE_DIRECTORY"

git_cleanup

# gbp.conf handling
gbp_conf

# make sure common branches are available for git-buildpackage
create_local_branch ${UPSTREAM_BRANCH}
create_local_branch ${DEBIAN_BRANCH}
if [ ${PRISTINE_TAR} = 'true' ]; then
  create_local_branch pristine-tar
fi

# check out the according branch/tag with help of $random_branch
branch_checkout

if [ -n "${PRE_DCH_HOOK:-}" ] ; then
  echo "*** Found environment variable PRE_DCH_HOOK, set to ${PRE_DCH_HOOK:-} ***"
  sh ${PRE_DCH_HOOK:-}
fi

# retrieve version information *after* checking out the according branch/tag,
# otherwise version number might result in something different than expected
version_information

# handling of debian/changelog
dch_opts
changelog_generation

# set $GBP_OPTS
gbp_opts

# prepare orig.tar.gz using pristine-tar, but without actually building the source package
if [ "${IGNORE_GIT_BUILDPACKAGE:-}" = "true" ] ; then
  echo "Skipping git-buildpackage execution as requested via IGNORE_GIT_BUILDPACKAGE ***"
else
  git-buildpackage ${GBP_OPTS:-}
fi

quilt_cleanup

# build source package, run before switching back to previous branch
# to get the actual requested version
dpkg-buildpackage -uc -us -nc -d -S -i -I --source-option=--unapply-patches

# get rid of the *_source.changes files generate by dpkg-buildpackage
# to avoid failures in a later stage when copying all files around
rm -f ../*_source.changes

# revert to original debian/changelog to avoid merge conflicts
git checkout -- $(readlink -f debian/changelog)

git checkout -f ${GIT_BRANCH} # switch back to previous "branch" before removing the tmp branch
git branch -D "$random_branch"

# vim:foldmethod=marker ts=2 ft=sh ai expandtab sw=2
