#!/bin/bash

JENKINS_DEBIAN_GLUE_VERSION=$(dpkg --list jenkins-debian-glue 2>/dev/null | awk '/^ii/ {print $3}')
JENKINS_DEBIAN_GLUE_VERSION=${JENKINS_DEBIAN_GLUE_VERSION:-unidentified}

# backwards compatibility, see PR#94
if [ -z "${REPOSITORY:-}" ] ; then
  repository_is_missing_in_env=true
else
  repository_is_missing_in_env=false
fi

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

# backwards compatibility, see PR#94
if [ -n "${REPOSITORY:-}" ] && $repository_is_missing_in_env ; then
  echo "*** WARNING: 'REPOSITORY' set in /etc/jenkins/debian_glue but should be DEFAULT_REPOSITORY ***"
  echo "*** WARNING: Setting DEFAULT_REPOSITORY to $REPOSITORY for backwards compatibility ***"
  echo "*** WARNING: Please replace REPOSITORY=... in /etc/jenkins/debian_glue with DEFAULT_REPOSITORY=... ***"
  DEFAULT_REPOSITORY="${REPOSITORY}"
fi

if [ -z "${DEFAULT_REPOSITORY:-}" ] ; then
  echo "*** Repository variable DEFAULT_REPOSITORY is unset, using default [$DEFAULT_REPOSITORY] ***"
  DEFAULT_REPOSITORY='/srv/repository'
fi

# REPOSITORY can overwrite DEFAULT_REPOSITORY, so define only if unset
if [ -z "${REPOSITORY:-}" ] ; then
  REPOSITORY="${DEFAULT_REPOSITORY}"
  echo "*** Repository variable REPOSITORY is unset, using default [$REPOSITORY] ***"
fi

# support usage of a reprepro wrapper
REPREPRO_CMD="${REPREPRO_CMD:-reprepro}"

if [ -z "${REPREPRO_OPTS:-}" ]  ; then
  REPREPRO_OPTS='--waitforlock 1000 -v'
fi

LOGFILE=$(mktemp)

bailout() {
  [ -n "${1:-}" ] && EXITCODE="$1" || EXITCODE=1
  rm -f "$LOGFILE"
  exit $EXITCODE
}

usage() {
  echo "$0

Listing options for packages:

  --list-package         <package>  List repositories which provide specified package (source + binary)
  --list-binary-package  <package>  List repositories which provide specified package (binary only)
  --list-source-package  <package>  List repositories which provide specified package (source only)

Listing options for repositories (known as Codenames in reprepro!):

  --list-repos           <repos>    List all packages (binary + source) registered in specified repository
  --list-binary-repos    <repos>    List all binary packages registered in specified repository
  --list-source-repos    <repos>    List all source packages registered in specified repository

Configuration options:

  --repository </some/directory/>   Use specified directory as base path

Validation options:

  --validate-source-bin-versions     Make sure source packages match with binary package versions.
  --validate-incoming                Make sure there are no leftover files in incoming repositories.
  --validate-skip-package <packages> List of source packages to skip.
  --version-remove-epoch             Remove epoch from package version

Misc options:

  --help                            Display this help screen.
  --version                         Display program version.
"
}

validate-incoming() {
  rc=0

  if ! [ -r "${REPOSITORY}/conf/incoming" ] ; then
    echo "No incoming repository in ${REPOSITORY} found. Nothing to be done."
    return 0
  fi

  for directory in $(awk '/IncomingDir: / {print $2}' "${REPOSITORY}/conf/incoming") ; do
    incoming="${REPOSITORY}/${directory}"

    if ! [ -d "$incoming" ] ; then
      echo "Directory $incoming of registered incoming repository does not exist, skipping."
      continue
    fi

    echo "Checking for leftover files in incoming directory ${incoming}:"
    if find "${incoming}" -type f | grep '.' ; then
      echo "Leftover files found. Needs investigation."
      rc=1
    else
      echo "Nothing found. Good."
    fi
  done

  return $rc
}

_remove_epoch() {
  if [[ "$1" =~ ^[[:digit:]]+:(.*) ]] ; then
    echo "${BASH_REMATCH[1]}"
  else
    echo "$1"
  fi
}

_skip_package() {
  local p
  for p in $1; do
    [[ $p = $2 ]] && return 0
  done
  return 1
}

validate-source-bin-versions() {
  rc=0

  for repository in $(awk '/^Codename: / {print $2}' "${REPOSITORY}"/conf/distributions 2>"${LOGFILE}") ; do
    for sourcepackage in $(${REPREPRO_CMD} $REPREPRO_OPTS -A source -b "${REPOSITORY}" --list-format='${package}\n' list $repository 2>"${LOGFILE}") ; do
      if $_opt_validate_skip_package && _skip_package "${SKIP_PACKAGE}" "$sourcepackage" ; then
        echo "skipping: $sourcepackage"
        continue
      fi
      sourceversion=$(${REPREPRO_CMD} $REPREPRO_OPTS -A source -b "${REPOSITORY}" --list-format='${version}\n' list $repository $sourcepackage 2>"${LOGFILE}")
      for binarypackage in $(${REPREPRO_CMD} $REPREPRO_OPTS -b "${REPOSITORY}" --list-format='${package}\n' -T deb listfilter "$repository" "\$Source (==$sourcepackage)" 2>"${LOGFILE}") ; do

        if $_opt_validate_skip_package && _skip_package "${SKIP_PACKAGE}" "$binarypackage" ; then
          echo "skipping: $binarypackage"
          continue
        fi

        archversion=$(${REPREPRO_CMD} $REPREPRO_OPTS -A amd64 -b "${REPOSITORY}" --list-format='${version}\n' list $repository $binarypackage 2>"${LOGFILE}")

        if $_opt_version_remove_epoch ; then
          sourceversion=$(_remove_epoch $sourceversion)
          archversion=$(_remove_epoch $archversion)
        fi

        if [ -z "$archversion" ] && [ -n "$sourceversion" ] ; then
          echo "Warning: package $binarypackage in repository $repository has sourceversion $sourceversion but lacks archversion"
          rc=1
          continue
        fi

        if [ -z "$sourceversion" ] && [ -n "$archversion" ] ; then
          echo "Warning: package $binarypackage in repository $repository has archversion $archversion but lacks sourceversion"
          rc=1
          continue
        fi

        if ! [[ "$sourceversion" == "$archversion" ]] ; then
          echo "Mismatch in repo $repository with ${binarypackage}: arch version [${archversion}] vs. sourceversion [$sourceversion]"
          rc=1
          continue
        fi

      done
    done
  done

  return $rc
}


trap bailout SIGHUP SIGINT SIGQUIT SIGABRT SIGKILL SIGALRM SIGTERM

# command line handling
CMDLINE_OPTS=list-package:,list-binary-package:,list-binary-repos:,list-repos:,list-source-package:,list-source-repos:,repository:,,help,validate-incoming,validate-source-bin-versions,version,version-remove-epoch,validate-skip-package:

_opt_temp=$(getopt --name repository_checker -o +vhV --long $CMDLINE_OPTS -- "$@")
if [ $? -ne 0 ]; then
  echo "Try '$0 --help' for more information." >& 2
  exit 1
fi
eval set -- "$_opt_temp"

# defaults
_opt_list_binary_package=false
_opt_list_binary_repos=false
_opt_list_packages=false
_opt_list_repos=false
_opt_list_source_package=false
_opt_list_source_repos=false
_opt_validate_source_bin_versions=false
_opt_validate_incoming=false
_opt_version_remove_epoch=false
_opt_validate_skip_package=false

while :; do
  case "$1" in
  --list-package)
    shift; _opt_list_packages=true ; PACKAGE="$1"
    ;;
  --list-binary-package)
    shift; _opt_list_binary_package=true; PACKAGE="$1"
    ;;
  --list-binary-repos)
    shift; _opt_list_binary_repos=true; REPOS="$1"
    ;;
  --list-repos)
    shift; _opt_list_repos=true; REPOS="$1"
    ;;
  --list-source-package)
    shift; _opt_list_source_package=true; PACKAGE="$1"
    ;;
  --list-source-repos)
    shift; _opt_list_source_repos=true; REPOS="$1"
    ;;
  --repository)
    shift; REPOSITORY="$1"
    ;;
  --help)
    usage ; exit 0;
    ;;
  --validate-incoming)
    _opt_validate_incoming=true
    ;;
  --validate-source-bin-versions)
    _opt_validate_source_bin_versions=true
    ;;
  --version)
    echo "$0 version $JENKINS_DEBIAN_GLUE_VERSION"; exit 0;
    ;;
  --version-remove-epoch)
    _opt_version_remove_epoch=true
    ;;
  --validate-skip-package)
    shift; _opt_validate_skip_package=true ; SKIP_PACKAGE="$1"
    ;;
  --)
    shift; break
    ;;
  *)
    echo "Internal getopt error! $1" >&2
    exit 1
    ;;
  esac
  shift
done

# sanity checks
if ! [ -r "${REPOSITORY}"/conf/distributions ] ; then
  echo "Error: reprepro configuration ${REPOSITORY}/conf/distributions can not be read." >&2
  bailout 1
fi

# main execution
if $_opt_list_packages ; then
  ${REPREPRO_CMD} ${REPREPRO_OPTS} -b "${REPOSITORY}" ls "${PACKAGE}"
  bailout 0
fi

if $_opt_list_source_package ; then
  ${REPREPRO_CMD} ${REPREPRO_OPTS} -b "${REPOSITORY}" -A source ls "${PACKAGE}"
  bailout 0
fi

if $_opt_list_binary_package ; then
  ${REPREPRO_CMD} ${REPREPRO_OPTS} -b "${REPOSITORY}" -T deb ls "${PACKAGE}"
  bailout 0
fi

if $_opt_list_repos ; then
  ${REPREPRO_CMD} ${REPREPRO_OPTS} -b "${REPOSITORY}" list "${REPOS}"
  bailout 0
fi

if $_opt_list_source_repos ; then
  ${REPREPRO_CMD} ${REPREPRO_OPTS} -b "${REPOSITORY}" -A source list "${REPOS}"
  bailout 0
fi

if $_opt_list_binary_repos ; then
  ${REPREPRO_CMD} ${REPREPRO_OPTS} -b "${REPOSITORY}" -T deb list "${REPOS}"
  bailout 0
fi

if $_opt_validate_source_bin_versions ; then
  validate-source-bin-versions
  bailout $?
fi

if $_opt_validate_incoming ; then
  validate-incoming
  bailout $?
fi

usage >&2
bailout 1

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