#!/bin/sh

# report back on a build assignment
# either on success via:
#   "$0 $package $revision $mod_revision $repository" 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 ERROR" and tar'ed logs

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

# TODO: sign database

# shellcheck source=conf/default.conf
. "${0%/*}/../conf/default.conf"

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

# Create a lock file and a trap.

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

clean_up_lock_file() {
  rm -f "${build_list_lock_file}"
}

trap clean_up_lock_file EXIT

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

  if [ ! -f "${work_dir}/package-states/$1.$2.$3.$4.locked" ]; then
    # too late, package already outdated -> ignore error report
    exit 0
  fi

  # shellcheck disable=SC2154
  if ! grep -qxF "${slave}" "${work_dir}/package-states/$1.$2.$3.$4.locked"; then
    # whoops, this package is built by someone else
    exit 0
  fi

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

  head -n1 \
    "${work_dir}/package-states/$1.$2.$3.$4.locked" >> \
    "${work_dir}/package-states/$1.$2.$3.$4.broken"

  rm "${work_dir}/package-states/$1.$2.$3.$4.locked"

  # unlock every loop this package would have broken and which is not
  # broken by another locked package
  locked_packages=$(
    find "${work_dir}/package-states/" -maxdepth 1 -name '*.locked' -printf '%f\n' | \
      sed 's@^\(.\+\)\.\([0-9a-f]\{40\}\.\)\{2\}[^.]\+\.locked$@\1@'
  )
  find "${work_dir}/build-list.loops" -maxdepth 1 \
    -name 'loop_*' \
    -not -name 'loop_*.locked' \
    -exec grep -qxF "$1" '{}' \; \
    -print | \
    while read -r loop; do
      if [ -z "$(
          (
            echo "${locked_packages}"
            cat "${loop}"
          ) | \
            sort | \
            uniq -d
        )" ]; then
        rm -f "${loop}.locked"
      fi
    done

  # move build order to end of build list

  sed -i \
    "/^$(str_to_regex "$1 $2 $3 $4")\$/d" \
    "${work_dir}/build-list"
  echo "$1 $2 $3 $4" >> \
    "${work_dir}/build-list"

  exit 0

fi

# the build was successful on the build slave

# so we also need a lock on the package database

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

clean_up_lock_file() {
  rm -f "${build_list_lock_file}"
  rm -f "${package_database_lock_file}"
}

if ! grep -qxF "$1 $2 $3 $4" "${work_dir}/build-list" ||
  ! [ -f "${work_dir}/package-states/$1.$2.$3.$4.locked" ]; 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}"
  clean_up_lock_file
}

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

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

# check if all packages are signed and all signatures belong to a package
missing_files=$(
  find . -maxdepth 1 -name '*.pkg.tar.xz' -o -name '*.pkg.tar.xz.sig' -o -name '*.pkg.tar.xz-namcap.log.gz' | \
    sed '
      s@\.sig$@ signature@
      t
      s@-namcap\.log\.gz$@ namcap@
      t
      s@$@ package@
    ' | \
    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
      }
    '
)

if [ -n "${missing_files}" ]; then
  >&2 echo 'The following packages lack a signature, namcap log or package file:'
  >&2 echo "${missing_files}"
  exit 3
fi

# check if the sent packages are the expected ones
packages=$(
  find . -maxdepth 1 -name '*.pkg.tar.xz' -printf '%f\n'
)
package_errors=$(
  (
    # shellcheck disable=SC2086
    printf '%s\n' ${packages} | \
      sed '
        s@\(-[^-]\+\)\{2\}-\([^-]\+\)\.pkg\.tar\.xz$@ \2@
        / any$/{
          s|any$|i686|
        }
        s|^|was_built: |
      '
    sed '
      s|$| i686|
      s|^|expected: |
    ' "${work_dir}/package-infos/$1.$2.$3.$4.packages"
  ) | \
    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 echo "${package_errors}"
  exit 4
fi

# move namcap.logs
find . -maxdepth 1 -name '*.pkg.tar.xz-namcap.log.gz' -execdir mv '{}' "${build_log_directory}/success/" \;

# move packages
destination=$(official_or_community "$1.$2.$3.$4" 'staging')

${master_mirror_rsync_command} \
  "${master_mirror_rsync_directory}/i686/${destination}/${destination}.db."* \
  "${master_mirror_rsync_directory}/i686/${destination}/${destination}.files."* \
  .
# shellcheck disable=SC2086
repo-add "${destination}.db.tar.gz" ${packages}
# repo-add -v -s -k "${repo_key}" "${destination}.db.tar.gz" ${packages}

${master_mirror_rsync_command} \
  "${destination}.db."* \
  "${destination}.files."* \
  ./*".pkg.tar.xz" \
  ./*".pkg.tar.xz.sig" \
  "${master_mirror_rsync_directory}/i686/${destination}/"

for package in ${packages}; do
  remove_old_package_versions 'i686' "${destination}" "${package}"
done

# remove old state files (these should be only "done" markers, but
# actually we don't care what it is) - as long as it's not "testing"
find "${work_dir}/package-states" -maxdepth 1 -regextype grep \
  -not -name '*.testing' \
  -regex ".*/$(str_to_regex "$1")\(\.[^.]\+\)\{4\}" \
  -not -regex ".*/$(str_to_regex "$1.$2.$3.$4")\.[^.]\+" \
  -exec rm '{}' \;

# remove all loops which are broken by this package
find "${work_dir}/build-list.loops" -maxdepth 1 \
  -name 'loop_*' \
  -not -name 'loop_*.locked' \
  -exec grep -qxF "$1" '{}' \; \
  -exec rm '{}' '{}.locked' \;

if ! find "${work_dir}/build-list.loops" -maxdepth 1 -printf '%f\n' | \
  grep -q '^loop_[0-9]\+$'; then
  # no loops left
  sed -i '/^break_loops$/d' "${work_dir}/build-list"
fi

# remove package from build list
sed -i "/^$(str_to_regex "$1 $2 $3 $4")\$/d" "${work_dir}/build-list"

# remove package lock file
if ! [ "${destination}" = 'build-support' ]; then
  # shellcheck disable=SC2086
  printf '%s\n' ${packages} > \
    "${work_dir}/package-states/$1.$2.$3.$4.done"
fi
rm -f \
  "${work_dir}/package-states/$1.$2.$3.$4.locked" \
  "${work_dir}/package-states/$1.$2.$3.$4.broken"