#!/bin/bash

set -x
set -u

# make sure cowbuilder/pbuilder/... are available
PATH='/bin:/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin'

echo "*** Starting $0 at $(date) ***"
start_seconds=$(cut -d . -f 1 /proc/uptime)

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

checks_and_defaults() {
  if [ -r /etc/jenkins/debian_glue ] ; then
    . /etc/jenkins/debian_glue
  fi

  # make sure cowbuilder/pbuilder has access to the variable
  if [ -n "${DEB_KEEP_BUILD_ENV:-}" ] ; then
    export DEB_KEEP_BUILD_ENV
  fi

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

  if [ -z "${architecture:-}" ] ; then
    echo "*** No architecture defined. Consider running it with matrix configuration. ***"
    architecture="$(dpkg-architecture -qDEB_HOST_ARCH)"
    echo "*** Falling back to default, using host architecture ${architecture}. ***"
  fi

  if [ -z "${REPOSITORY:-}" ] ; then
    REPOSITORY='/srv/repository'
  fi

  if [ -z "${PBUILDER_HOOKDIR:-}" ] ; then
    PBUILDER_HOOKDIR='/usr/share/jenkins-debian-glue/pbuilder-hookdir/'
  fi

  # Evaluate Freight default options - we use the system wide freight
  # directories and configuration, unless either $FREIGHT_REPOSITORY or
  # $FREIGHT_BASE are specified.
  if [ -z "${FREIGHT_REPOSITORY:-}" ] && [ -z "${FREIGHT_BASE:-}" ] ; then
    FREIGHT_VARLIB=/var/lib/freight
    FREIGHT_VARCACHE=/var/cache/freight
    FREIGHT_CONF=/etc/freight.conf
  elif [ -z "${FREIGHT_REPOSITORY:-}" ] && [ -n "${FREIGHT_BASE:-}" ] ; then
    FREIGHT_VARLIB=${FREIGHT_BASE}/default-source
    FREIGHT_VARCACHE=${FREIGHT_BASE}/default
    FREIGHT_CONF=${FREIGHT_BASE}/default.conf
  else
    # Default to /srv/freight unless specifed
    if [ -z "${FREIGHT_BASE:-}" ] ; then
      FREIGHT_BASE=/srv/freight
    fi

    FREIGHT_VARLIB=${FREIGHT_BASE}/${FREIGHT_REPOSITORY}-source
    FREIGHT_VARCACHE=${FREIGHT_BASE}/${FREIGHT_REPOSITORY}
    FREIGHT_CONF=${FREIGHT_BASE}/${FREIGHT_REPOSITORY}.conf
  fi
}

clean_workspace() {
  echo "*** The following files have been noticed in the workspace [$(pwd)]: ***"
  ls -la ./

#  echo "*** Cleaning workspace in $(pwd) to make sure we're building from scratch. ***"
#  rm -f ./* || true
}

# make sure we don't leave files for next run
bailout() {
  [ -n "${1:-}" ] && EXIT="${1}" || EXIT=0
  [ -n "${2:-}" ] && echo "$2" >&2

  echo "*** Getting rid of files in $WORKSPACE/binaries/ to avoid problems in next run. ***"
  rm -f "$WORKSPACE"/binaries/*
  ${SUDO_CMD:-} rm -rf /tmp/adt-$$ /tmp/apt-$$

  [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
  echo "*** Finished execution of $0 at $(date) [running ${SECONDS} seconds] ***"

  exit $EXIT
}

identify_package_name() {
  # make sure we get rid of 'repos' and 'binaries' from Jenkins job name
  PACKAGE=${JOB_NAME%-repos*}
  PACKAGE=${PACKAGE%-binaries*}

  if [ -n "${PACKAGE:-}" ] ; then
    echo "*** Identified Debian package name $PACKAGE ***"
  else
    bailout 1 "Error: could not identify Debian package name based on job name ${JOB_NAME:-}."
  fi
}

set_base_path() {
  # when BASE_PATH is set in the build step then don't default to $WORKSPACE
  if [ -n "${BASE_PATH:-}" ] ; then
    echo "*** Using provided ${BASE_PATH} as BASE_PATH ***"
  else
    BASE_PATH="${WORKSPACE}"
    echo "*** Using \$WORKSPACE [$BASE_PATH] as default for BASE_PATH ***"
  fi

}

build_info() {
  if [ -n "${REPOS:-}" ] ; then
    echo "*** Using supplied repository name $REPOS ***"
  else
    REPOS="${JOB_NAME%-binaries*}"
    REPOS="${REPOS%-repos*}"

    if [ -z "${distribution:-}" ]; then
      echo "*** No repository supplied, using repository name $REPOS ***"
    else
      REPOS="${REPOS}-${distribution}"
      echo "*** No repository supplied but distribution has been set, using repository name $REPOS ***"
    fi
  fi
}

identify_sourcefile() {
  if [ -n "${sources:-}" ] ; then
    echo "*** WARNING: sources variable [$sources] is set, please use BASE_PATH variable instead ***"
    echo "*** If \$sources is unrelated to build-and-provide-package you can ignore this warning ***"
  fi

  echo "*** Identifying newest package version ***"
  newest_version="0"

  for file in "${BASE_PATH}/"*.dsc ; do
    SOURCE_PACKAGE="$(awk '/^Source: / {print $2}' $file)"
    p="$(basename $file .dsc)"
    if [ "$p" = '*' ] ; then
      bailout 1 "Error: No source package found (forgot to configure source files deployment?)"
    fi
    cur_version="${p#*_}"
    if dpkg --compare-versions "${cur_version}" gt "${newest_version}" ; then
      newest_version="${cur_version}"
    else
      base_version="${cur_version}"
    fi
  done

  echo "*** Found package version $newest_version ***"

  sourcefile="${BASE_PATH}/${SOURCE_PACKAGE}"_*"${newest_version}".dsc

  echo "*** Using $sourcefile (version: ${newest_version})"
}

dist_and_arch_settings() {
  if [ -z "${architecture:-}" ] || [ "${architecture:-}" = "all" ] ; then
    arch="$(dpkg-architecture -qDEB_HOST_ARCH)"
    echo "*** No architecture set or architecture set to 'all', using system arch ${arch} ***"
  else
    arch="${architecture}"
    echo "*** architecture is set to ${architecture} ***"
  fi

  # only set $arch for other functions in this script if PROVIDE_ONLY is set
  if [ -n "${PROVIDE_ONLY:-}" ] ; then
    echo "*** Config variable 'PROVIDE_ONLY' is set, not setting COWBUILDER_BASE, COWBUILDER_DIST and DIST ***"
    return 0
  fi

  if [ -n "${distribution:-}" ] ; then
    local DIST="${distribution}"
  else
    # default to the currently running distribution to avoid hardcoding
    # a distribution which might not be supported by the running system
    local distribution=$(lsb_release --short --codename 2>/dev/null)
    [ -n "${distribution}" ] || distribution="sid"  # fallback to "sid" iff lsb_release fails
    local DIST="$distribution"
  fi

  # if COWBUILDER_DIST is set it overrides distribution then
  if [ -n "${COWBUILDER_DIST:-}" ]; then
    echo "*** COWBUILDER_DIST is set to $COWBUILDER_DIST - using it for base.cow if it does not exist yet. ***"
  else
    echo "*** Using cowbuilder base for distribution ${DIST} ***"
    COWBUILDER_DIST="${DIST}"
  fi

  if [ -n "${COWBUILDER_BASE:-}" ] ; then
    echo "*** COWBUILDER_BASE is set to $COWBUILDER_BASE - using as cowbuilder base.cow ***"
  else
    COWBUILDER_BASE="/var/cache/pbuilder/base-${COWBUILDER_DIST}-${arch}.cow"
    echo "*** No COWBUILDER_BASE set, using $COWBUILDER_BASE as cowbuilder base.cow ***"
  fi
}

cowbuilder_init() {

  local pbuilderrc=$(mktemp)
  echo "# pbuilder config file generated by jenkins-debian-glue on $(date)" > "$pbuilderrc"

  # allow setting main pbuilder configuration file from outside, then append data
  # as needed without actually writing anything to user-provided $PBUILDER_CONFIG
  if [ -n "${PBUILDER_CONFIG:-}" ] ; then
    echo "*** PBUILDER_CONFIG is set, considering $PBUILDER_CONFIG for pbuilder config ***"
    if [ -r "${PBUILDER_CONFIG:-}" ] ; then
      echo "*** Adding content of $PBUILDER_CONFIG to pbuilder configfile ***"
      echo "# $PBUILDER_CONFIG added via jenkins-debian-glue:" >> "$pbuilderrc"
      cat $PBUILDER_CONFIG >> "$pbuilderrc"
    else
      echo "*** WARNING: File $PBUILDER_CONFIG could not be read, ignoring ***"
    fi
  fi

  if [ -n "${COMPONENTS:-}" ] ; then
    echo "*** COMPONENTS is set [$COMPONENTS], using for pbuilder configuration ***"
    echo "# COMPONENTS set by jenkins-debian-glue:" >> "$pbuilderrc"
    echo "COMPONENTS=\"${COMPONENTS}\"" >> "$pbuilderrc"
  fi

  # workaround for Ubuntu problem, as cowdancer is available only in universe :(
  # https://bugs.launchpad.net/ubuntu/+source/cowdancer/+bug/237591
  # https://bugs.launchpad.net/ubuntu/+source/cowdancer/+bug/747053
  if lsb_release --id 2>/dev/null | grep -q Ubuntu ; then
    if [ -z "${COMPONENTS:-}" ] ; then
      echo "*** Ubuntu detected, enabling universe repository component to work around cowdancer issue ***"
      echo "# Ubuntu detected, enabling universe repository component to work around cowdancer issue:" >> "$pbuilderrc"
      echo 'COMPONENTS="main universe"' >> "$pbuilderrc"
    fi
  fi

  echo "*** Listing pbuilder configuration file as reference: ***"
  cat "$pbuilderrc"

  if [ ! -d "${COWBUILDER_BASE}" ]; then
    echo "*** Creating cowbuilder base $COWBUILDER_BASE for arch $arch and distribution $COWBUILDER_DIST ***"
    sudo DIST="${distribution:-}" ARCH="${architecture:-}" ${ADT:+ADT=$ADT} \
      cowbuilder --create --basepath "${COWBUILDER_BASE}" --distribution "${COWBUILDER_DIST}" \
         --debootstrapopts --arch --debootstrapopts "$arch" \
         --debootstrapopts --variant=buildd --configfile="${pbuilderrc}" \
         --hookdir "${PBUILDER_HOOKDIR}"
    [ $? -eq 0 ] || bailout 1 "Error: Failed to create cowbuilder base ${COWBUILDER_BASE}."
  else
    echo "*** Updating cowbuilder cow base ***"
    sudo DIST="${distribution:-}" ARCH="${architecture:-}" ${ADT:+ADT=$ADT} \
      cowbuilder --update --basepath "${COWBUILDER_BASE}" --configfile="${pbuilderrc}"
    [ $? -eq 0 ] || bailout 1 "Error: Failed to update cowbuilder base ${COWBUILDER_BASE}."
  fi

  [ -n "${pbuilderrc:-}" ] && rm -f "$pbuilderrc"
}


identify_build_type() {
  # defaults
  DEBBUILDOPTS="-sa"
  SKIP_ARCH_BUILD=false

  if [ "${architecture:-}" = "all" ] ; then
    echo "*** \$architecture is set to 'all', skipping further identify_build_type checks. ***"
    echo "*** Consider setting \$architecture to amd64, i386,... instead. ***"
    return 0
  fi

  if [ -z "${MAIN_ARCHITECTURE:-}" ] ; then
    if [ "$(dpkg-architecture -qDEB_HOST_ARCH)" = "${architecture:-}" ] ; then
      echo "*** MAIN_ARCHITECTURE is unset. ***"
      echo "*** Host architecture [$(dpkg-architecture -qDEB_HOST_ARCH)] matches \$architecture [${architecture:-}], using default ${DEBBUILDOPTS:-} buildoption ***"
      return 0
    else
      echo "*** MAIN_ARCHITECTURE is unset. ***"
      echo "*** Host architecture [$(dpkg-architecture -qDEB_HOST_ARCH)] does not match \$architecture [${architecture:-}] ... ***"
      echo "*** ... setting binary only build and continuing with identify_build_type ***"
      DEBBUILDOPTS="-B"
    fi
  else
    if [ "${MAIN_ARCHITECTURE:-}" = "${architecture:-}" ] ;then
      echo "*** MAIN_ARCHITECTURE is set [${MAIN_ARCHITECTURE:-}]. ***"
      echo "*** MAIN_ARCHITECTURE matches \$architecture [${architecture:-}], using default ${DEBBUILDOPTS:-} buildoption ***"
      return 0
    else
      echo "*** MAIN_ARCHITECTURE [${MAIN_ARCHITECTURE:-}] does not match \$architecture [${architecture:-}], setting binary only build and continuing with identify_build_type ***"
      DEBBUILDOPTS="-B"
    fi
  fi

  local TMPDIR=$(mktemp -d)
  local old_dir=$(pwd)
  cd "$TMPDIR"
  for file in  ${BASE_PATH}/${SOURCE_PACKAGE}_*.tar.* ; do
    if tar atf "$file" 2>/dev/null | egrep -q '^[^/]+/debian/control$' ; then
      # might be source/debian/control - so let's identify the path to debian/control
      local control_file=$(tar atf "$file" 2>/dev/null | egrep '^[^/]+/debian/control$')
      tar axf "$file" "$control_file" || bailout 1 "Error while looking at debian/control in source archive."

      if grep -q '^Architecture: all' "$control_file" ; then
        if grep -q '^Architecture: .*any' "$control_file" ; then
          echo "*** Package provides arch 'all' + 'any', enabling -B buildoption for this architecture. ***"
          # -B -> binary-only build, limited to architecture dependent packages
          DEBBUILDOPTS="-B"
          break
        else
          # only "Architecture: all", so no arch specific packages since
          # we aren't building for $MAIN_ARCHITECTURE
          SKIP_ARCH_BUILD=true
          break
        fi
      fi
    fi
  done
  cd "$old_dir"

  rm -rf "${TMPDIR}"
}

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

  # copy autopkgtest results from /tmp
  rm -rf adt
  ${SUDO_CMD:-} chmod -R go+rX /tmp/adt-$$
  cp -a /tmp/adt-$$ adt
}

cowbuilder_run() {
  echo "*** cowbuilder build phase for arch $architecture ***"
  mkdir -p "$WORKSPACE"/binaries/ /tmp/adt-$$ /tmp/apt-$$

  local BINDMOUNTS="/tmp/adt-$$ /tmp/apt-$$ /var/cache/pbuilder/build ${USER_BINDMOUNTS:-}"

  # make sure we build arch specific packages only when necessary
  identify_build_type

  if $SKIP_ARCH_BUILD ; then
    autopkgtest_results
    bailout 0 "Nothing to do, architecture all binary packages only for non-primary architecture."
  fi

  # For release builds use release repo to satisfy dependencies
  if [ -n "${release:-}" ] && [ "$release" != "none" ] && [ "$release" != "trunk" ] && \
    [ "${release}" != '${release}' ] ; then
    if [ -n "${RELEASE_REPOSITORY:-}" ]; then
      local REPOSITORY="${RELEASE_REPOSITORY}"
    else
      local REPOSITORY="${REPOSITORY}/release/${release}"
    fi;

    if [ -d "${REPOSITORY}/dists/${release}" ]; then
      BINDMOUNTS="$BINDMOUNTS $REPOSITORY"
      local components="$(awk -F': ' '/^Components:/ { print $2 }' \
        "${REPOSITORY}/dists/${release}/Release")"
      # Check if keyring is provided so the repository can be verified.
      if [ -n "${REPOSITORY_KEYRING:-}" ]; then
        local trusted=
      else
        # If no keyring is provided, just assume that the repository is
        # trustworthy. This option appeared in apt 0.8.16~exp3 which is not
        # available in Squeeze.
        local trusted="[trusted=yes]"
      fi
      cat > /tmp/apt-$$/release.list <<EOF
deb ${trusted} file://${REPOSITORY} ${release} ${components}
EOF
    fi

    if [ -n "${REPOSITORY_KEYRING:-}" ]; then
      cp -a "${REPOSITORY_KEYRING}" /tmp/apt-$$/keyring.gpg
    fi
  fi

  local pbuilderrc=$(mktemp)
  echo "# pbuilder config file generated by jenkins-debian-glue on $(date)" > "$pbuilderrc"

  # allow setting main pbuilder configuration file from outside, then append data
  # as needed without actually writing anything to user-provided $PBUILDER_CONFIG
  if [ -n "${PBUILDER_CONFIG:-}" ] ; then
    echo "*** PBUILDER_CONFIG is set, considering $PBUILDER_CONFIG for pbuilder config ***"
    if [ -r "${PBUILDER_CONFIG:-}" ] ; then
      echo "*** Adding content of $PBUILDER_CONFIG to pbuilder configfile ***"
      cat $PBUILDER_CONFIG >> "$pbuilderrc"
    else
      echo "*** File $PBUILDER_CONFIG could not be read, ignoring ***"
    fi
  fi

  case "$architecture" in
    i386)
      linux32 sudo DIST="${distribution:-}" ARCH="${architecture:-}" ${ADT:+ADT=$ADT} \
        cowbuilder --buildresult "$WORKSPACE"/binaries/ \
        --build $sourcefile \
        --basepath "${COWBUILDER_BASE}" --debbuildopts "${DEBBUILDOPTS:-}" \
        --hookdir "${PBUILDER_HOOKDIR}" --bindmounts "$BINDMOUNTS" --configfile="${pbuilderrc}"
      [ $? -eq 0 ] || bailout 1 "Error: Failed to build with cowbuilder."
      ;;
    amd64|all|*)
      sudo DIST="${distribution:-}" ARCH="${architecture:-}" ${ADT:+ADT=$ADT} \
        cowbuilder --buildresult "$WORKSPACE"/binaries/ \
        --build $sourcefile \
        --basepath "${COWBUILDER_BASE}" --debbuildopts "${DEBBUILDOPTS:-}" \
        --hookdir "${PBUILDER_HOOKDIR}" --bindmounts "$BINDMOUNTS" --configfile="${pbuilderrc}"
      [ $? -eq 0 ] || bailout 1 "Error: Failed to build with cowbuilder."
      ;;
    *)
      bailout 1 "Error: Unsupported architecture: $architecture"
      ;;
  esac

  [ -n "${pbuilderrc:-}" ] && rm -f "$pbuilderrc"
}

# replacement for dcmd, sadly available only in the devscripts package
list_deb_files() {
  if [ "$#" -lt 1 ] ; then
    echo "Error: list_deb_files function needs a file name as argument." >&2
    return 1
  fi

  local files

  for arg in "$@" ; do
    if ! [ -r "$arg" ] ; then
      echo "Error: could not read $arg" >&2
      continue
    fi

    # cmdline based on usage in dcmd, we're interested only in .deb files though
    sed --regexp-extended -n 's,^ [0-9a-f]{32} [0-9]+ ((([a-zA-Z0-9_.-]+/)?[a-zA-Z0-9_.-]+|-) ([a-zA-Z]+|-) )?(.*.deb)$,\5,p' "$arg"
  done
}

remove_packages() {
  if [ -n "${SKIP_REMOVAL:-}" ] ; then
    echo "*** Skipping removal of existing packages as requested via SKIP_REMOVAL ***"
    return 0
  fi

  echo "*** Removing source package version from repository ***"
  ${SUDO_CMD:-} reprepro -v -A source -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${SOURCE_PACKAGE}"

  echo "*** Removing previous binary package versions from repository ***"
  for p in $(list_deb_files "${WORKSPACE}/binaries/"*"${newest_version}_${arch}.changes") ; do
    file="$(basename $p)"
    binpackage="${file%%_*}"
    binary_list="${binary_list:-} ${binpackage}"

    skip=false # don't skip any package(s) unless it's listed in SKIP_PACKAGE_FROM_REMOVAL

    if [ -n "${SKIP_PACKAGE_FROM_REMOVAL:-}" ] ; then
      echo "*** SKIP_PACKAGE_FROM_REMOVAL is set [${SKIP_PACKAGE_FROM_REMOVAL}]"
      for package in $SKIP_PACKAGE_FROM_REMOVAL ; do
        if echo "${package}" | grep -q "${binpackage}" ; then
          skip=true
        fi
      done
    fi

    if $skip ; then
      echo "*** Package '$binpackage' listed in SKIP_PACKAGE_FROM_REMOVAL - skipping removal therefore ***"
    elif echo "$file" | egrep -q '_all.deb$'; then
      # note: "removesrc" would remove foreign arch files (of different builds)
      echo "*** Removing existing package ${binpackage} from repository ${REPOS} (arch all) ***"
      ${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${binpackage}"
    else
      echo "*** Removing existing package ${binpackage} from repository ${REPOS} for arch ${arch} ***"
      ${SUDO_CMD:-} reprepro -v -A "${arch}" -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${binpackage}"
    fi

  done

}

remove_missing_binary_packages() {
  if [ -n "${SKIP_REMOVAL:-}" ] ; then
    echo "*** Skipping removal of existing packages as requested via SKIP_REMOVAL ***"
    return 0
  fi

  echo "*** Checking for missing binary packages to be considered for removal ***"

  # In a binary-only build we don't get any arch-all (*_all.deb) packages and
  # therefore they won't be listed in the changes file.  As a result they would
  # be reported as missing from the build and to be considered for removal.
  # As we don't want to remove the arch-all package e.g. from the amd64 repos
  # in the i386 run we've to skip the removal procedure then.
  case "${DEBBUILDOPTS:-}" in
    *-B*)
      echo "*** Skipping removal of missing binaries as being a binary-only build ***"
      return 0
      ;;
  esac

  for p in $(${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 --list-format '${package}\n' listmatched "${REPOS}" '*' | sort -u); do
    echo " $binary_list " | grep -q " $p " || missing_packages="${missing_packages:-} $p"
  done

  if echo "${missing_packages:-}" | grep -q '.' ; then
    echo "*** Binary package(s) found, missing in build version: ${missing_packages:-} ***"

    for p in $missing_packages ; do
      skip=false # don't skip any package(s) unless it's listed in SKIP_PACKAGE_FROM_REMOVAL

      if [ -n "${SKIP_PACKAGE_FROM_REMOVAL:-}" ] ; then
        echo "*** SKIP_PACKAGE_FROM_REMOVAL is set [${SKIP_PACKAGE_FROM_REMOVAL}]"
        for package in $SKIP_PACKAGE_FROM_REMOVAL ; do
          if echo "${package}" | grep -q "${p}" ; then
            skip=true
          fi
        done
      fi

      if $skip ; then
        echo "*** Package '$p' listed in SKIP_PACKAGE_FROM_REMOVAL - skipping removal therefore ***"
      else
        echo "*** Removing $p from $REPOS to avoid out-of-date data ***"
        ${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${p}"
      fi
   done

  fi
}

reprepro_wrapper() {
  if ! [ -d "$REPOSITORY" ] ; then
    bailout 1 "Error: repository ${REPOSITORY} does not exist."
  fi

  ${SUDO_CMD:-} generate-reprepro-codename "${REPOS}"

  remove_packages
  remove_missing_binary_packages

  archall=false
  case $architecture in
    all) archall=true
      architecture='*' # support as file expansion in reprepro cmdline
      ;;
  esac

  echo "*** Including packages in repository $REPOS ***"
  ${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 \
    --ignore=wrongdistribution --ignore=uploaders \
    include "${REPOS}" "${WORKSPACE}/binaries/"*"${newest_version}"_${architecture}.changes
  [ $? -eq 0 ] || bailout 1 "Error: Failed to include binary package in $REPOS repository."
}

freight_ensure_repo() {
  echo "*** Creating freight directory structure ***"
  ${SUDO_CMD:-} mkdir -p "${FREIGHT_VARCACHE}" "${FREIGHT_VARLIB}"

  if [ ! -f "$FREIGHT_CONF" ] ; then
    echo "*** Creating freight repository configuration in $FREIGHT_CONF ***"

    ${SUDO_CMD:-} sh -c "cat > ${FREIGHT_CONF}" <<EOF
# Generated by jenkins-debian-glue
#
# Directories for the Freight library and Freight cache.
# Your web server's document root should point to \$VARCACHE.
VARLIB="${FREIGHT_VARLIB:-/var/lib/freigh}"
VARCACHE="${FREIGHT_VARCACHE:-/var/cache/freight}"

# Default 'Origin' and 'Label' fields for 'Release' files.
ORIGIN="Freight"
LABEL="Freight"

# Cache the control files after each run (on), or regenerate them every
# time (off).
CACHE="off"

# GPG key to sign repositories, derived from jenkins-debian-glue's \$KEY_ID setting
GPG="${KEY_ID:-}"

# Whether to follow symbolic links in \$VARLIB to produce extra components
# in the cache directory (on) or not (off).
SYMLINKS="off"
EOF
  fi

  [ -f "$FREIGHT_CONF" ] || bailout 1 "Error: Failed to create freight configuration in $FREIGHT_CONF"
}

freight_wrapper() {
  freight_ensure_repo

  echo "*** Including packages via freight in repository ${FREIGHT_VARLIB}/${REPOS} ***"
  ${SUDO_CMD:-} freight add -v -c "$FREIGHT_CONF" "${WORKSPACE}/binaries/"*"${newest_version}"*"deb" "apt/${REPOS}"
  [ $? -eq 0 ] || bailout 1 "Error: Failed to add binary package to repository."

  echo "*** Generating freight cache ***"
  ${SUDO_CMD:-} freight cache -v -c "$FREIGHT_CONF"
  [ $? -eq 0 ] || bailout 1 "Error: Failed to generate freight cache for ${FREIGHT_VARCACHE}."
}

trunk_release() {
  # setting TRUNK_RELEASE=true enables release-trunk repository,
  # to always get a copy of the package(s) to a central place
  if [ -z "${TRUNK_RELEASE:-}" ] ; then
    echo "*** TRUNK_RELEASE is not enabled ***"
  elif [ "${IGNORE_RELEASE_TRUNK:-}" = "true" ] ; then
    echo "*** IGNORE_RELEASE_TRUNK is set, ignoring request to add package(s) to $TRUNK_RELEASE repos ***"
  else
    echo "*** TRUNK_RELEASE is enabled ($TRUNK_RELEASE) ***"

    ${SUDO_CMD:-} generate-reprepro-codename "$TRUNK_RELEASE"

    ${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 --ignore=wrongdistribution copymatched "$TRUNK_RELEASE" "$REPOS" '*'
    [ $? -eq 0 ] || bailout 1 "Error: Failed to copy packages from ${REPOS} to ${TRUNK_RELEASE}."
  fi
}

release_repos() {
  echo "*** Environment variable 'release' is set, running through release steps. ***"

  if [ -n "${RELEASE_REPOSITORY:-}" ]; then
    local REPOSITORY="${RELEASE_REPOSITORY}"
  else
    local REPOSITORY="${REPOSITORY}/release/${release}"
  fi;

  mkdir -p "${REPOSITORY}/incoming/${release}"
  mkdir -p "${REPOSITORY}/conf"

  if [ "${REMOVE_FROM_RELEASE:-}" = 'true' ]; then
    echo "*** REMOVE_FROM_RELEASE is set, trying to remove package(s) from release repository"
    REPOS="${release}" remove_packages
  fi

  cp "${WORKSPACE}/binaries/"* "${REPOSITORY}/incoming/${release}/"
  [ $? -eq 0 ] || bailout 1 "Error: Failed to copy binary packages to release directory."

  REPOSITORY=$REPOSITORY generate-reprepro-codename "${release}"

  # lock access to file to avoid duplicate entries when two build-and-provide-package
  # runs happen at the very same time with regard to conf/incoming setup
  (
  flock --timeout 5 9 || bailout 1 "Error: could not lock file ${REPOSITORY}/conf/incoming, giving up."

  if ! grep -q "^Name: $release$" "${REPOSITORY}/conf/incoming" 2>/dev/null ; then
    cat >> "${REPOSITORY}/conf/incoming" << EOF

Name: $release
IncomingDir: incoming/$release
TempDir: tmp
LogDir: log
MorgueDir: ${REPOSITORY}/morgue
Default: $release
Cleanup: unused_files on_deny on_error

EOF
  fi
  ) 9>/var/lock/jdg-build-and-provide-incoming."$(id -un)" || bailout 1 "Error while setting up incoming repository."

  local old_dir=$(pwd)
  cd "${REPOSITORY}/incoming/${release}"
  ${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 --ignore=wrongdistribution \
                processincoming "${release}" "$(basename ${WORKSPACE}/binaries/*.changes)"
  local RC=$?
  cd "$old_dir"

  if [ $RC -ne 0 ] ; then
    bailout 1 "Error: Failed to execute processincoming for release ${release}."
  fi
}

deploy_to_releases() {
  if [ -n "${USE_FREIGHT:-}" ] ; then
    freight_wrapper
    # Freight is currently not able to manage release or trunk release repos,
    # so this is the stage where we exit in that case.
    return 0
  fi

  if [ -n "${release:-}" ] && [ "$release" != "none" ] && [ "$release" != "trunk" ] && \
    # '${release}' is a hidden bomb: when provided through predefined parameters
    # from an upstream jenkins job (like foo-binaries receiving the parameters
    # from foo-source) but the job (foo-binaries) gets triggered manually (without
    # setting the predefined parameters therefore) then ${release} is set to
    # '${release}' instead of being empty
    [ "${release}" != '${release}' ] ; then
    release_repos
  else
    reprepro_wrapper
    trunk_release
  fi
}

# make them available for the Jenkin's 'Archiving artifacts'
binaries_to_workspace() {
  echo "*** Moving binaries files to workspace. ***"
  mv "${WORKSPACE}/binaries/"* "${WORKSPACE}/"
  rmdir "${WORKSPACE}/binaries/"
}

# main execution
trap bailout SIGHUP SIGINT SIGQUIT SIGABRT SIGKILL SIGALRM SIGTERM

checks_and_defaults
clean_workspace
identify_package_name
set_base_path
build_info
identify_sourcefile
dist_and_arch_settings

# do not run in repos job?
if [ -n "${PROVIDE_ONLY:-}" ] ; then
  echo "*** Config variable 'PROVIDE_ONLY' is set, ignoring request to run cowbuilder. ***"
else
  cowbuilder_init
  cowbuilder_run
fi

# do not run in binaries job?
if [ -n "${BUILD_ONLY:-}" ] ; then
  echo "*** Config variable 'BUILD_ONLY' is set, ignoring request to use local repository. ***"
else
  deploy_to_releases
fi

binaries_to_workspace
autopkgtest_results

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

bailout 0

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