#!/bin/sh

# report back on a build assignment
# either on success via:
#   "$0 $package $revision $mod_revision $repository $arch $sub_pkgrel" and tar'ed packages and logs
#   (= a tar of package(s), signature(s) and log(s)) on stdin
# or on failure via:
#   "$0 $package $revision $mod_revision $repository $arch ERROR" and tar'ed logs
# or to abort:
#   "$0 ABORT"

# exit codes:
#  0: ok
#  1: another instance was already running
#  2: outdated package
#  3: signature error
#  4: package error (e.g. wrong packages sent)
#  5: wrong number of arguments

# TODO: sign database

# shellcheck disable=SC2119,SC2120

# shellcheck source=../lib/load-configuration
. "${0%/*}/../lib/load-configuration"

if [ -s "${work_dir}/build-master-sanity" ]; then
  >&2 echo 'Build master is not sane.'
  exit 1
fi

# aborting does not need any locks
if [ $# -eq 1 ] && \
  [ "$1" = 'ABORT' ]; then
  old_pkgbase=$(
    # shellcheck disable=SC2016
    {
      printf 'SELECT `package_sources`.`pkgbase`'
      printf ' FROM `build_slaves`'
      mysql_join_build_slaves_build_assignments
      mysql_join_build_assignments_package_sources
      # shellcheck disable=SC2154
      printf ' WHERE `build_slaves`.`id`=%s;\n' \
        "${slave_id}"
      printf 'UPDATE `build_slaves`'
      printf ' SET `build_slaves`.`currently_building`=NULL'
      # shellcheck disable=SC2154
      printf ' WHERE `build_slaves`.`id`=%s;\n' \
        "${slave_id}"
    } | \
      mysql_run_query 'unimportant'
  )
  if [ -z "${old_pkgbase}" ]; then
    >&2 printf 'Umm, nothing to abort for your.\n'
  else
    >&2 printf 'I aborted your build-assignment (%s).\n' \
      "${old_pkgbase}"
  fi
  exit
fi

if [ $# -ne 6 ]; then
  >&2 printf 'return-assignment: Wrong number of arguments (%s)\n' \
    "$#"
  >&2 printf 'call either:\n'
  >&2 printf '  return-assignment ABORT\n'
  >&2 printf '  return-assignment pkgbase git_revision mod_git_revision repository arch ERROR\n'
  >&2 printf '  return-assignment pkgbase git_revision mod_git_revision repository arch sub-pkgrel\n'
  exit 5
fi

# Create a lock file and a trap.

exec 9> "${build_list_lock_file}"
if ! verbose_flock -n 9; then
  >&2 echo 'come back (shortly) later - I cannot lock build list.'
  exit 1
fi

exec 8> "${sanity_check_lock_file}"
if ! verbose_flock -s -n 8; then
  >&2 echo 'come back (shortly) later - sanity-check running.'
  exit 1
fi

if [ "$6" = 'ERROR' ]; then
# the build failed on the build slave

  # shellcheck disable=SC2016
  infos=$(
    {
      printf 'SELECT DISTINCT'
      printf ' `build_assignments`.`id`,'
      printf 'IF(`build_assignments`.`is_broken`,"true","false")'
      printf ' FROM `build_slaves`'
      mysql_join_build_slaves_build_assignments
      mysql_join_build_assignments_package_sources
      mysql_join_package_sources_upstream_repositories
      mysql_join_build_assignments_binary_packages
      mysql_join_binary_packages_binary_packages_in_repositories
      printf ' JOIN `architecture_compatibilities`'
      printf ' ON `architecture_compatibilities`.`fully_compatible`'
      printf ' AND `architecture_compatibilities`.`built_for`=`build_assignments`.`architecture`'
      printf ' JOIN `architectures`'
      printf ' ON `architecture_compatibilities`.`runs_on`=`architectures`.`id`'
      printf ' WHERE `build_slaves`.`id`=from_base64("%s")' \
        "$(
          # shellcheck disable=SC2154
          printf '%s' "${slave_id}" | \
            base64 -w0
        )"
      printf ' AND `package_sources`.`%s`=from_base64("%s")' \
        'pkgbase' "$(printf '%s' "$1" | base64 -w0)" \
        'git_revision' "$(printf '%s' "$2" | base64 -w0)" \
        'mod_git_revision' "$(printf '%s' "$3" | base64 -w0)"
      printf ' AND `upstream_repositories`.`name`=from_base64("%s")' \
        "$(printf '%s' "$4" | base64 -w0)"
      printf ' AND `architectures`.`name`=from_base64("%s")' \
        "$(printf '%s' "$5" | base64 -w0)"
      printf ' AND `binary_packages_in_repositories`.`repository`=%s;\n' \
        "${repository_ids__any_build_list}"
    } | \
      mysql_run_query | \
      tr '\t' ' '
  )
  if [ -z "${infos}" ]; then
    >&2 echo 'You do not build this package (anymore) - move on.'
    exit 2
  fi
  was_broken_before="${infos##* }"
  build_assignment_id="${infos%% *}"

  # save sent build logs
  saved_build_logs=$(
    tar -vx \
      -C "${build_log_directory}/error" \
      --wildcards \
      --no-wildcards-match-slash \
      --transform="s|^|$1.$2.$3.$4.$5.|" \
      '*.build-log.gz'
  )

  # shellcheck disable=SC2016
  {
    if [ -n "${saved_build_logs}" ]; then
      printf 'CREATE TEMPORARY TABLE `failures` ('
      printf '`%s` %s,' \
        'date' 'TIMESTAMP' \
        'reason' 'SMALLINT' \
        'log_file' 'VARCHAR(512)' | \
        sed 's/,$//'
      printf ');\n'
      fail_reason_identifiers=$(
        {
          printf 'SELECT `fail_reasons`.`id`,replace(to_base64(`fail_reasons`.`identifier`),"\\n","")'
          printf ' FROM `fail_reasons` ORDER BY `fail_reasons`.`severity`'
        } | \
          mysql_run_query
      )
      for saved_build_log in ${saved_build_logs}; do
        printf '%s\n' "${fail_reason_identifiers}" | \
          while read -r reason_id identifier; do
            if zgrep -qx "$(
                printf '%s' "${identifier}" | \
                  base64 -d
              )" \
              "${build_log_directory}/error/$1.$2.$3.$4.$5.${saved_build_log}"; then

              printf ' (from_base64("%s"),%s,from_base64("%s")),' \
                "$(
                  printf '%s' "${saved_build_log}" | \
                    sed 's|\.build-log\.gz$||;s|^.*\.||' | \
                    base64 -w0
                )" \
                "${reason_id}" \
                "$(
                  printf '%s' "$1.$2.$3.$4.$5.${saved_build_log}" | \
                    base64 -w0
                )"
              break
            fi
          done
      done | \
        sed '
          1 s/^/INSERT IGNORE INTO `failures` (`date`,`reason`,`log_file`) VALUES /
          s/,$/;\n/
        '
      printf 'INSERT IGNORE INTO `failed_builds` (`build_slave`,`build_assignment`,`date`,`reason`,`log_file`,`log_file_exists`)'
      printf ' SELECT '
      printf 'from_base64("%s"),' \
        "$(printf '%s' "${slave_id}" | base64 -w0)" \
        "$(printf '%s' "${build_assignment_id}" | base64 -w0)"
      printf '`failures`.`%s`,' \
        'date' 'reason' 'log_file'
      printf '1'
      printf ' FROM `failures`;\n'
      printf 'DROP TEMPORARY TABLE `failures`;\n'
      printf 'COMMIT;\n'
    fi
    printf 'UPDATE `build_assignments`'
    printf ' SET `build_assignments`.`is_broken`=1,'
    printf '`build_assignments`.`priority`=0'
    printf ' WHERE `build_assignments`.`id`=from_base64("%s");\n' \
      "$(
        printf '%s' "${build_assignment_id}" | \
          base64 -w0
      )"
    printf 'COMMIT;\n'
    printf 'UPDATE `build_slaves`'
    printf ' SET `build_slaves`.`currently_building`=NULL,'
    printf ' `build_slaves`.`last_action`=NULL,'
    printf ' `build_slaves`.`logged_lines`=NULL,'
    printf ' `build_slaves`.`trials`=NULL'
    printf ' WHERE `build_slaves`.`id`=from_base64("%s");\n' \
      "$(
        printf '%s' "${slave_id}" | \
          base64 -w0
      )"
  } | \
    mysql_run_query

  if ! ${was_broken_before}; then

    # this will hold a list of "$time_stamp $haskell_package" meaning
    # that the given $haskell_package did not work at the given
    # $time_stamp
    broken_haskell_packages=$(mktemp 'tmp.return-assignment.broken-haskell-packages.XXXXXXXXXX' --tmpdir)
    trap 'rm "${broken_haskell_packages}"' EXIT

    find "${build_log_directory}/error" -type f \
      -name "$1.$2.$3.$4.$5.*.build-log.gz" \
      -exec zgrep -qFx '    The following packages are broken because other packages they depend on are missing. These broken packages must be rebuilt before they can be used.' {} \; \
      -printf '%p\n' | \
      sed '
        s/^.*\.\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}T[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}\)\.build-log\.gz$/\1 \0/
      ' | \
      while read -r build_time build_log; do
        build_time=$(
          date +%s -d"${build_time}"
        )
        zcat "${build_log}" | \
        sed -n '
          s/^installed package \(.*\) is broken due to missing package .*$/\1/
          T
          p
        ' | \
        tr ' ' '\n' | \
        sed '
          s/^/'"${build_time}"'\t/
          s/-[0-9.]\+$//
        '
      done | \
      sort -k2,2 -k1nr,1 | \
      uniq -f1 > \
      "${broken_haskell_packages}"

    # now we look if the broken packages have been rebuilt in the meantime
    haskell_rebuild_packages=$(
      # shellcheck disable=SC2016
      {
        printf 'CREATE TEMPORARY TABLE `broken`(`time_stamp` BIGINT,`pkgname` VARCHAR(64),UNIQUE KEY `pkgname`(`pkgname`),KEY `time_stamp`(`time_stamp`));\n'
        printf 'LOAD DATA LOCAL INFILE "%s" INTO TABLE `broken`(`time_stamp`,`pkgname`);\n' \
          "${broken_haskell_packages}"
        printf 'SELECT DISTINCT `broken`.`pkgname`'
        printf ' FROM `broken`'
        printf ' WHERE NOT EXISTS ('
          printf 'SELECT 1'
          printf ' FROM `binary_packages`'
          mysql_join_binary_packages_binary_packages_in_repositories
          mysql_join_binary_packages_in_repositories_repositories
          printf ' AND ('
            printf '`repositories`.`is_on_master_mirror`'
            printf ' OR `repositories`.`id`=%s' \
              "${repository_ids__any_build_list}"
          printf ')'
          mysql_join_binary_packages_build_assignments
          printf ' JOIN `architecture_compatibilities`'
          printf ' ON `architecture_compatibilities`.`fully_compatible`'
          printf ' AND `architecture_compatibilities`.`built_for`=`build_assignments`.`architecture`'
          printf ' JOIN `architectures`'
          printf ' ON `architecture_compatibilities`.`runs_on`=`architectures`.`id`'
          printf ' AND `architectures`.`name`=from_base64("%s")' \
            "$(
              printf '%s' "$5" | \
                base64 -w0
            )"
          printf ' WHERE ('
            printf '`binary_packages`.`pkgname`=`broken`.`pkgname`'
            printf ' OR `binary_packages`.`pkgname`=CONCAT("haskell-",`broken`.`pkgname`)'
          printf ') AND ('
            printf '`build_assignments`.`return_date`>FROM_UNIXTIME(`broken`.`time_stamp`)'
            printf ' OR `repositories`.`id`=%s' \
              "${repository_ids__any_build_list}"
          printf ')'
        printf ');\n'
      } | \
        mysql_run_query | \
        sed '
          s/^/-p ^(haskell-)?/
          s/$/$/
        '
    )

    # release lock on build-list - otherwise seed-build-list won't run
    flock -u 9

    rescheduled_packages=$(
      if [ -n "${haskell_rebuild_packages}" ]; then
        # shellcheck disable=SC2086
        "${base_dir}/bin/seed-build-list" ${haskell_rebuild_packages} | \
          sed 's/ .*$//'
      fi
    )

    # shellcheck disable=SC2119
    {
      printf '%s/%s ' \
        "$5" \
        "$1"
      if [ "${1%s}s" = "$1" ]; then
        printf 'are'
      else
        printf 'is'
      fi
      # shellcheck disable=SC2154
      printf ' broken (says %s)' \
        "${slave}"
      if [ "$1" = 'electron' ] || [ "$1" = 'electron2' ]; then
        printf -- ' - as usual'
      fi
      if [ -n "${rescheduled_packages}" ]; then
        printf -- ' - I rescheduled:'
        # shellcheck disable=SC2086
        printf ' %s,' ${rescheduled_packages} | \
          sed 's/,$//'
      fi
      printf '.\n'
    } | \
      if [ ! "x$5" = 'xi486' ]; then
        # TODO: remove, when i486 is fully bootstrapped
        irc_say
      fi
  fi

  exit 0

fi

# the build was successful on the build slave

# so we also need a lock on the package database

exec 7> "${package_database_lock_file}"
if ! verbose_flock -n 7; then
  >&2 echo 'come back (shortly) later - I cannot lock package database.'
  exit 1
fi

# shellcheck disable=SC2016
build_assignment_id=$(
  {
    printf 'SELECT DISTINCT `build_assignments`.`id`'
    printf ' FROM `build_slaves`'
    mysql_join_build_slaves_build_assignments
    mysql_join_build_assignments_package_sources
    mysql_join_package_sources_upstream_repositories
    mysql_join_build_assignments_binary_packages
    mysql_join_binary_packages_binary_packages_in_repositories
    printf ' JOIN `architecture_compatibilities`'
    printf ' ON `build_assignments`.`architecture`=`architecture_compatibilities`.`built_for`'
    printf ' AND `architecture_compatibilities`.`fully_compatible`'
    printf ' JOIN `architectures`'
    printf ' ON `architecture_compatibilities`.`runs_on`=`architectures`.`id`'
    printf ' WHERE `build_slaves`.`id`=from_base64("%s")' \
      "$(
        # shellcheck disable=SC2154
        printf '%s' "${slave_id}" | \
          base64 -w0
      )"
    printf ' AND `package_sources`.`%s`=from_base64("%s")' \
      'pkgbase' "$(
        printf '%s' "$1" | \
          base64 -w0
      )" \
      'git_revision' "$(
        printf '%s' "$2" | \
          base64 -w0
      )" \
      'mod_git_revision' "$(
        printf '%s' "$3" | \
          base64 -w0
      )"
    printf ' AND `upstream_repositories`.`name`=from_base64("%s")' \
      "$(
        printf '%s' "$4" | \
          base64 -w0
      )"
    printf ' AND `architectures`.`name`=from_base64("%s")' \
      "$(
        printf '%s' "$5" | \
          base64 -w0
      )"
    printf ' AND `binary_packages_in_repositories`.`repository`=%s' \
      "${repository_ids__any_build_list}"
    printf ' AND `binary_packages`.`sub_pkgrel`=from_base64("%s");\n' \
      "$(
        printf '%s' "$6" | \
          base64 -w0
      )"
  } | \
    mysql_run_query | \
    tr '\t' ' '
)

if [ -z "${build_assignment_id}" ]; then
  >&2 echo 'Sorry, the sent package is outdated.'
  exit 2
fi

clean_up_tmp_dir() {
  cd "${base_dir}"
  rm -rf --one-file-system "${tmp_dir}"
}

tmp_dir=$(mktemp -d "${work_dir}/tmp.return-assignment.XXXXXXXXXX")
cd "${tmp_dir}"
trap clean_up_tmp_dir EXIT

export TMPDIR="${tmp_dir}"

# extract package(s)
tar -x \
  --wildcards \
  --no-wildcards-match-slash \
  '*.pkg.tar.xz' \
  '*.pkg.tar.xz.sig' \
  '*.pkg.tar.xz-namcap.log.gz' \
  '*.pkg.tar.xz.so.needs.gz' \
  '*.pkg.tar.xz.so.provides.gz'

# check if all packages come with:
#  - a package file
#  - a signature
#  - a namcap log
#  - a list of needed libraries
#  - a list of provided libraries
missing_files=$(
  find . -maxdepth 1 \( \
    \( \
      -name '*.pkg.tar.xz' \
      -printf '%f package\n' \
    \) -o \
    \( \
      -name '*.pkg.tar.xz.sig' \
      -printf '%f signature\n' \
    \) -o \
    \( \
      -name '*.pkg.tar.xz-namcap.log.gz' \
      -printf '%f namcap\n' \
    \) -o \
    \( \
      -name '*.pkg.tar.xz.so.needs.gz' \
      -printf '%f needed-libraries\n' \
    \) -o \
    \( \
      -name '*.pkg.tar.xz.so.provides.gz' \
      -printf '%f provided-libraries\n' \
    \) \
  \) | \
    sed '
      s/\(\.pkg\.tar\.xz\)\(\.sig\|\(-namcap\.log\|\.so\.\(provides\|needs\)\)\.gz\) /\1 /
    ' | \
    sort -k1,1 -k2,2 | \
    sed '
      :a
        $!N
        s/^\(\(\S\+\) [^\n]\+\)\n\2 /\1 /
        ta
      P
      D
    ' | \
    sed -n '
      s/$/ /
      / package /!{
        h
        s/^\(\S\+\) .*$/Package "\1" is missing./
        p
        g
      }
      / signature /!{
        h
        s/^\(\S\+\) .*$/Signature of "\1" is missing./
        p
        g
      }
      / namcap /!{
        h
        s/^\(\S\+\) .*$/Namcap log of "\1" is missing./
        p
        g
      }
      / needed-libraries /!{
        h
        s/^\(\S\+\) .*$/List of libraries needed by "\1" is missing./
        p
        g
      }
      / provided-libraries /!{
        h
        s/^\(\S\+\) .*$/List of libraries provided by "\1" is missing./
        p
        g
      }
    '
)

if [ -n "${missing_files}" ]; then
  >&2 echo 'The following packages lack a signature, namcap log, package file or list of needed/provided libraries:'
  printf '/j %s Your buildslave "%s" uploaded some incomplete package(s):\n' \
    "${operator}" "${slave}" | \
    sponge "${irc_dir}/in"
  printf '%s\n' "${missing_files}" | \
    irc_say "${operator}" 'copy' >&2
  exit 3
fi

# check if the signatures are valid
signatures=$(
  find . -maxdepth 1 -name '*.pkg.tar.xz' \
    -printf 'package file %f\n' \
    -exec gpg --batch --status-fd 1 -q --homedir /etc/pacman.d/gnupg --verify '{}.sig' '{}' \; 2> /dev/null
)
if [ -z "$(
  printf '%s\n' "${signatures}" | \
    cut -d' ' -f2 | \
    grep -x 'file\|TRUST_FULLY' | \
    sort | \
    uniq -c | \
    awk '{print $1}' | \
    uniq -d
)" ]; then
  >&2 echo 'Signature(s) is/are not fully trusted:'
  printf '/j %s Your buildslave "%s" uploaded a package with a not fully-trusted signature:\n' \
    "${operator}" "${slave}" | \
    sponge "${irc_dir}/in"
  printf '%s\n' "${signatures}" | \
    irc_say "${operator}" 'copy' >&2
  exit 3
fi

# check if the package maintainer is set
errors=$(
  find . -maxdepth 1 -name '*.pkg.tar.xz' | \
    while read -r pkg; do
      tar -OxJf "${pkg}" '.BUILDINFO' 2>/dev/null | \
        grep -vxF 'packager = Unknown Packager' | \
        grep -q '^packager = ' || \
        printf '%s misses a valid packager.\n' \
          "${pkg##*/}"
    done
)
if [ -n "${errors}" ]; then
  >&2 echo 'Packager error(s):'
  printf '/j %s Your buildslave "%s" uploaded package(s) with invalid packager:\n' \
    "${operator}" "${slave}" | \
    sponge "${irc_dir}/in"
  printf '%s\n' "${errors}" | \
    irc_say "${operator}" 'copy' >&2
  exit 3
fi

# check if the sent packages are the expected ones
find . -maxdepth 1 -name '*.pkg.tar.xz' -printf '%f\n' > \
  "${tmp_dir}/packages"

# shellcheck disable=SC2016
{
  printf 'SELECT'
  printf ' `binary_packages`.`id`,'
  mysql_package_name_query
  printf ' FROM `binary_packages`'
  mysql_join_binary_packages_architectures
  mysql_join_binary_packages_binary_packages_in_repositories
  printf ' WHERE `binary_packages`.`build_assignment`=from_base64("%s")' \
    "$(
      printf '%s' "${build_assignment_id}" | \
        base64 -w0
    )"
  printf ' AND `binary_packages_in_repositories`.`repository`=%s' \
    "${repository_ids__any_build_list}"
  printf ';\n'
} | \
  mysql_run_query | \
  tr '\t' ' ' | \
  sort -k2 > \
  "${tmp_dir}/package-ids"

package_errors=$(
  {
    sed '
      s|^|was_built: |
    ' "${tmp_dir}/packages"
    sed '
      s|^[0-9]\+ |expected: |
    ' "${tmp_dir}/package-ids"
  } | \
    sort -k2 | \
    uniq -u -f1
)

if [ -n "${package_errors}" ]; then
  >&2 echo 'The following packages should have been built but are missing or vice versa:'
  >&2 printf '%s\n' "${package_errors}"
  {
    printf 'Your buildslave "%s" uploaded the wrong package(s):\n' \
      "${slave}"
    printf '%s\n' "${package_errors}"
  } | \
    sed '
      s@^@/j '"${operator}"' @
    ' | \
    sponge "${irc_dir}/in"
  exit 4
fi

if [ ! -s "${tmp_dir}/package-ids" ]; then
  >&2 echo 'No package was expected, no package was built.'
  >&2 echo 'That should not happen!'
  exit 4
fi

# TODO: maybe, we should not put "any" packages into repositories of all
# architectures at once, but wait until they can actually be installed
# on that architecture? (They might be missing an architecture specific
# dependency)

# shellcheck disable=SC2016
{
  printf 'SELECT `binary_packages`.`id`,'
  mysql_package_name_query
  printf ',`t`.`id`,`t_a`.`name`,`t`.`name`'
  printf ' FROM `build_assignments`'
  mysql_join_build_assignments_binary_packages
  mysql_join_binary_packages_binary_packages_in_repositories
  printf ' AND `binary_packages_in_repositories`.`repository`=%s' \
    "${repository_ids__any_build_list}"
  mysql_join_binary_packages_architectures
  mysql_join_build_assignments_package_sources
  mysql_join_package_sources_upstream_repositories
  mysql_join_upstream_repositories_repository_moves
  printf ' JOIN `repositories` as `t`'
  printf ' ON `t`.`id`=`repository_moves`.`to_repository`'
  mysql_join_repositories_architectures 't' 't_a'
  printf ' JOIN `architecture_compatibilities`'
  printf ' ON `architecture_compatibilities`.`built_for`=`binary_packages`.`architecture`'
  printf ' AND `architecture_compatibilities`.`runs_on`=`t`.`architecture`'
  printf ' AND `architecture_compatibilities`.`fully_compatible`'
  printf ' WHERE `repository_moves`.`from_repository`=%s' \
    "${repository_ids__any_build_list}"
  printf ' AND `build_assignments`.`id`=from_base64("%s");\n' \
    "$(
      printf '%s' "${build_assignment_id}" | \
        base64 -w0
    )"
} | \
  mysql_run_query | \
  tr '\t' ' ' | \
  sort -u > \
  "${tmp_dir}/repository-ids"

errors=$(
  {
    cut -d' ' -f2 < \
      "${tmp_dir}/repository-ids" | \
      sort -u | \
      sed 's/^/repository-was-found: /'
    cut -d' ' -f2 < \
      "${tmp_dir}/package-ids" | \
      sort -u | \
      sed 's/^/package-was-sent: /'
  } | \
    sort -k2 | \
    uniq -uf1
)

if [ -n "${errors}" ]; then
  >&2 echo 'I cannot determine, where this package (or some part of it)'
  >&2 echo 'should be published:'
  >&2 printf '%s\n' "${errors}"
  >&2 echo 'This is some internal error and not (necessarily) your fault.'
  # We give a temporary error (although resolving this needs manual
  # intervention), because there is nothing wrong with the sent package
  # whence it does not need to be built again, but can simply be sent again.
  exit 1
fi

mysql_load_min_and_max_versions

while read -r package_id package_name; do
  # move namcap.logs
  mv \
    "${tmp_dir}/${package_name}-namcap.log.gz" \
    "${build_log_directory}/success/"
  # generate checksum
  sha512sum "${tmp_dir}/${package_name}" | \
    awk '{print "'"${package_id}"'\t" $1}' >> \
    "${tmp_dir}/sha512sums"
  # generate list of required/provided libraries
  for lib in 'provides' 'needs'; do
    zcat "${tmp_dir}/${package_name}.so.${lib}.gz" | \
      sed '
        s/\(=\|<\|<=\|>=\|>\)\([^[:space:]-]\+\)$/\t\1\t\2/
        t
        h
        s/$/\t>=\t'"${min_version}"'/
      '"$(
        if [ "${lib}" = 'provides' ]; then
          printf '%s\n' \
            'p' \
            'g' \
            's/$/\t<=\t'"${max_version}"'/'
        fi
      )" | \
      sed '
        s/\(\s[0-9]\+\):\(\S\+\)$/\1\t\2/
        t coda
        s/\s\S\+$/\t0\0/
        :coda
        s/^/'"${package_id}"'\t/
      ' >> "${tmp_dir}/so.${lib}"
  done
done < \
  "${tmp_dir}/package-ids"

# move packages

cut -d' ' -f4,5 "${tmp_dir}/repository-ids" | \
  sort -u | \
  while read -r arch repo; do

    mkdir -p "${arch}/${repo}"
    failsafe_rsync \
      "${master_mirror_rsync_directory}/${arch}/${repo}/${repo}.db."* \
      "${master_mirror_rsync_directory}/${arch}/${repo}/${repo}.files."* \
      "${arch}/${repo}/"

    grep " $(str_to_regex "${arch} ${repo}")\$" "${tmp_dir}/repository-ids" | \
      cut -d' ' -f2 | \
      xargs -r repo-add "${arch}/${repo}/${repo}.db.tar.gz"
    # xargs -r repo-add -v -s -k "${repo_key}" "${destination}.db.tar.gz"

  done

# upload the packages into /pool
failsafe_rsync -c --copy-dest=/.transfer \
  ./*".pkg.tar.xz" \
  ./*".pkg.tar.xz.sig" \
  "${master_mirror_rsync_directory}/pool/"

# create symlinks
find . \( -name '*.pkg.tar.xz' -o -name '*.pkg.tar.xz.sig' \) -printf '%f\n' | \
  while read -r file; do
    rm "${file}"
    ln -s "../../pool/${file}" "${file}"
  done

# upload the database and the symlinks into /$arch/$repo

cut -d' ' -f4,5 "${tmp_dir}/repository-ids" | \
  sort -u | \
  while read -r arch repo; do
    recompress_gz \
      "${tmp_dir}" \
      "${arch}/${repo}/${repo}."*".tar.gz" \
      "${arch}/${repo}/${repo}."*".tar.gz.old"
    # shellcheck disable=SC2046
    failsafe_rsync \
      "${arch}/${repo}/${repo}.db."* \
      "${arch}/${repo}/${repo}.files."* \
      $(
        grep " $(str_to_regex "${arch} ${repo}")\$" "${tmp_dir}/repository-ids" | \
          cut -d' ' -f2 | \
          sed '
            s,^,./,
            p
            s/$/.sig/
          '
      ) \
      "${master_mirror_rsync_directory}/${arch}/${repo}/"
  done

trigger_mirror_refreshs

# shellcheck disable=SC2016
{
  # insert checksums into database
  printf 'CREATE TEMPORARY TABLE `pkg_hashes` (`pkgid` BIGINT, `sha512sum` VARCHAR(128));\n'
  printf 'LOAD DATA LOCAL INFILE "%s" INTO TABLE `pkg_hashes`;\n' \
    "${tmp_dir}/sha512sums"
  printf 'UPDATE `binary_packages`'
  printf ' JOIN `pkg_hashes`'
  printf ' ON `pkg_hashes`.`pkgid`=`binary_packages`.`id`'
  printf ' SET `binary_packages`.`sha512sum`=`pkg_hashes`.`sha512sum`;\n'
  printf 'COMMIT;\n'

  # insert provided/needed libraries into database
  for lib_link in 'pl:provides' 'nl:needs'; do
    printf 'CREATE TEMPORARY TABLE `%s` (' \
      "${lib_link%:*}"
      printf '`pkgid` BIGINT,'
      printf '`lib` VARCHAR(128),'
      printf '`relation` VARCHAR(2),'
      printf '`epoch` MEDIUMINT,'
      printf '`version` VARCHAR(32)'
    printf ');\n'
    printf 'LOAD DATA LOCAL INFILE "%s" INTO TABLE `%s`;\n' \
      "${tmp_dir}/so.${lib_link#*:}" "${lib_link%:*}"

    printf 'INSERT IGNORE INTO `install_targets` (`name`)'
    printf ' SELECT DISTINCT `%s`.`lib`' \
      "${lib_link%:*}"
    printf ' FROM `%s`;\n' \
      "${lib_link%:*}"
    printf 'COMMIT;\n'

# TODO: possibly remove install_target_providers with less restrictive
# versions than we have now
    printf 'INSERT IGNORE INTO `versions` (`epoch`,`version`)'
    printf ' SELECT DISTINCT `%s`.`epoch`,`%s`.`version`' \
      "${lib_link%:*}" "${lib_link%:*}"
    printf ' FROM `%s`;\n' \
      "${lib_link%:*}"
    printf 'COMMIT;\n'

    if [ "${lib_link%:*}" = 'pl' ]; then
      printf 'INSERT IGNORE INTO `install_target_providers` (`package`,`install_target`,`install_target_is_group`,`version`)'
    else
      printf 'INSERT IGNORE INTO `dependencies` (`dependent`,`depending_on`,`dependency_type`,`version_relation`,`version`)'
    fi
    printf ' SELECT `%s`.`pkgid`,`install_targets`.`id`,' \
      "${lib_link%:*}"
    if [ "${lib_link%:*}" = 'nl' ]; then
      printf '`dependency_types`.`id`,'
      printf '`%s`.`relation`,' \
        "${lib_link%:*}"
    else
      printf '0,'
    fi
    printf '`versions`.`id`'
    printf ' FROM `install_targets`'
    if [ "${lib_link%:*}" = 'nl' ]; then
      printf ' JOIN `dependency_types`'
      printf ' ON `dependency_types`.`name`="link"'
    fi
    printf ' JOIN `%s`' \
      "${lib_link%:*}"
    printf ' ON `%s`.`lib`=`install_targets`.`name`' \
      "${lib_link%:*}"
    printf ' JOIN `versions`'
    printf ' ON `versions`.`epoch`=`%s`.`epoch`' \
      "${lib_link%:*}"
    printf ' AND `versions`.`version`=`%s`.`version`' \
      "${lib_link%:*}"
    printf ';\n'
    printf 'COMMIT;\n'
  done

  # remove build_assignment's markers
  printf 'UPDATE `build_assignments`'
  printf ' SET'
  printf ' `build_assignments`.`is_broken`=0,'
  printf ' `build_assignments`.`priority`=0,'
  printf ' `build_assignments`.`return_date`=NOW()'
  printf ' WHERE `build_assignments`.`id`=from_base64("%s");\n' \
    "$(
      printf '%s' "${build_assignment_id}" | \
        base64 -w0
    )"
  printf 'COMMIT;\n'

  # insert into appropriate repositories and retrieve ids
  while read -r package_id _ repository_id _; do
    printf 'INSERT INTO `binary_packages_in_repositories` (`package`,`repository`,`is_to_be_deleted`) VALUES '
    printf '(%s,%s,0)' \
      "${package_id}" "${repository_id}"
    printf ' ON DUPLICATE KEY UPDATE `id`=LAST_INSERT_ID(`binary_packages_in_repositories`.`id`);\n'
    printf 'SELECT LAST_INSERT_ID();\n'
  done < \
    "${tmp_dir}/repository-ids"
  printf 'COMMIT;\n'

  # reschedule toolchain packages if they were not fully unblocked
  printf 'INSERT IGNORE INTO `binary_packages` ('
  printf '`build_assignment`,'
  printf '`epoch`,'
  printf '`pkgver`,'
  printf '`pkgrel`,'
  printf '`has_issues`,'
  printf '`is_tested`,'
  printf '`pkgname`,'
  printf '`architecture`,'
  printf '`sub_pkgrel`)'
  printf ' SELECT '
  printf '`binary_packages`.`%s`,' \
    'build_assignment' \
    'epoch' \
    'pkgver' \
    'pkgrel' \
    'has_issues' \
    'is_tested' \
    'pkgname' \
    'architecture'
  printf '`binary_packages`.`sub_pkgrel`+1'
  printf ' FROM `binary_packages`'
  printf ' JOIN `pkg_hashes`'
  printf ' ON `pkg_hashes`.`pkgid`=`binary_packages`.`id`'
  mysql_join_binary_packages_build_assignments
  mysql_join_build_assignments_package_sources
  printf ' JOIN `toolchain_order` AS `late`'
  printf ' ON `late`.`pkgbase`=`package_sources`.`pkgbase`'
  printf ' JOIN `toolchain_order` AS `early`'
  printf ' ON `early`.`number`<`late`.`number`'
  printf ' AND `early`.`pkgbase`!=`late`.`pkgbase`'
  printf ' JOIN `package_sources` AS `early_ps`'
  printf ' ON `early`.`pkgbase`=`early_ps`.`pkgbase`'
  mysql_join_package_sources_build_assignments 'early_ps' 'early_ba'
  mysql_join_build_assignments_binary_packages 'early_ba' 'early_bp'
  printf ' WHERE ('
    printf '`early_bp`.`architecture`=`binary_packages`.`architecture`'
    # shellcheck disable=SC2154
    printf ' OR `%s`.`architecture`=%s' \
      'early_bp' "${architecture_ids__any}" \
      'binary_packages' "${architecture_ids__any}"
  printf ') AND NOT EXISTS ('
    printf 'SELECT 1'
    printf ' FROM `binary_packages` AS `early_bp`'
    mysql_join_binary_packages_binary_packages_in_repositories 'early_bp' 'early_bpir'
    mysql_join_binary_packages_in_repositories_repositories 'early_bpir' 'early_r'
    printf ' WHERE `early_r`.`is_on_master_mirror`'
    printf ' AND `early_bp`.`build_assignment`=`early_ba`.`id`'
  printf ');\n'

  join_part=$(
    printf ' JOIN `pkg_hashes`'
    printf ' ON `built_bp`.`id`=`pkg_hashes`.`pkgid`'
    printf ' JOIN `binary_packages` AS `new_bp`'
    printf ' ON'
    printf ' `built_bp`.`%s`=`new_bp`.`%s` AND' \
      'build_assignment' 'build_assignment' \
      'epoch' 'epoch' \
      'pkgver' 'pkgver' \
      'pkgrel' 'pkgrel' \
      'has_issues' 'has_issues' \
      'is_tested' 'is_tested' \
      'pkgname' 'pkgname' \
      'architecture' 'architecture'
    printf ' `built_bp`.`sub_pkgrel`+1=`new_bp`.`sub_pkgrel`'
  )
  printf 'INSERT IGNORE INTO `binary_packages_in_repositories`'
  printf ' (`package`,`repository`,`is_to_be_deleted`)'
  printf ' SELECT `new_bp`.`id`,%s,0' \
    "${repository_ids__any_build_list}"
  printf ' FROM `binary_packages` AS `built_bp`'
  printf '%s;\n' "${join_part}"
  printf 'INSERT IGNORE INTO `dependencies`'
  printf ' (`dependent`,`depending_on`,`dependency_type`,`version`,`version_relation`)'
  printf ' SELECT `new_bp`.`id`'
  printf ',`dependencies`.`%s`' \
    'depending_on' \
    'dependency_type' \
    'version' \
    'version_relation'
  printf ' FROM `dependencies`'
  mysql_join_dependencies_binary_packages '' 'built_bp'
  printf '%s;\n' "${join_part}"
  # TODO: set version and install_target_providers, too
  printf 'INSERT IGNORE INTO `install_target_providers`'
  printf ' (`package`,`install_target`)'
  printf ' SELECT `new_bp`.`id`,`install_target_providers`.`install_target`'
  printf ' FROM `install_target_providers`'
  mysql_join_install_target_providers_binary_packages '' 'built_bp'
  printf '%s;\n' "${join_part}"

  # remove from build-list
  printf 'DELETE `binary_packages_in_repositories`'
  printf ' FROM `binary_packages_in_repositories`'
  printf ' JOIN `pkg_hashes`'
  printf ' ON `pkg_hashes`.`pkgid`=`binary_packages_in_repositories`.`package`'
  printf ' WHERE `binary_packages_in_repositories`.`repository`=%s;\n' \
    "${repository_ids__any_build_list}"
  printf 'COMMIT;\n'

  # update package information
  printf 'UPDATE `binary_packages`'
  printf ' SET'
  printf ' `binary_packages`.`has_issues`=0,'
  printf ' `binary_packages`.`is_tested`=0'
  printf ' WHERE `binary_packages`.`id` IN ('
  cut -d' ' -f1 < \
    "${tmp_dir}/package-ids" | \
    base64_encode_each | \
    sed '
      s/^/from_base64("/
      s/$/"),/
      $ s/,$//
    '
  printf ');\n'
  printf 'COMMIT;\n'

  # remove from build slave's `currently_building`
  printf 'UPDATE `build_slaves`'
  printf ' SET `build_slaves`.`currently_building`=NULL,'
  printf ' `build_slaves`.`last_action`=NULL,'
  printf ' `build_slaves`.`logged_lines`=NULL,'
  printf ' `build_slaves`.`trials`=NULL'
  printf ' WHERE `build_slaves`.`currently_building`=from_base64("%s");\n' \
    "$(
      printf '%s' "${build_assignment_id}" | \
        base64 -w0
    )"
  printf 'COMMIT;\n'

  # remove broken loops
  printf 'CREATE TEMPORARY TABLE `loops_to_delete` (`loop` MEDIUMINT);\n'
  printf 'INSERT IGNORE INTO `loops_to_delete`'
  printf ' SELECT `build_dependency_loops`.`loop`'
  printf ' FROM `build_dependency_loops`'
  mysql_join_build_dependency_loops_binary_packages
  mysql_join_binary_packages_binary_packages_in_repositories
  printf ' WHERE NOT `binary_packages_in_repositories`.`repository`=%s;\n' \
    "${repository_ids__any_build_list}"
  printf 'COMMIT;\n'
  printf 'DELETE FROM `build_dependency_loops`'
  printf ' WHERE EXISTS ('
    printf 'SELECT 1'
    printf ' FROM `loops_to_delete`'
    printf ' WHERE `loops_to_delete`.`loop`=`build_dependency_loops`.`loop`'
  printf ');\n'
  printf 'DROP TEMPORARY TABLE `loops_to_delete`;\n'
} | \
  mysql_run_query | \
  sort -u | \
  remove_old_package_versions