#!/usr/bin/env bash

# Copyright (C) 2020  Matthew "strager" Glazar
# See end of file for extended copyright information.

set -e
set -u
shopt -s extglob

ssh_args=()
ssh_host=github-ci@c.quick-lint-js.com
remote_builds_root=/var/www/c.quick-lint-js.com/builds
remote_releases_root=/var/www/c.quick-lint-js.com/releases
remote_debian_root=/var/www/c.quick-lint-js.com/debian
remote_debian_root_testing=/var/www/c.quick-lint-js.com/debian-testing
apt_suite=experimental

testing=0
extra_builds=()
sync_releases=1

clean_up() {
  if [ -e "${ssh_control_socket:-}" ]; then
    ssh "${ssh_args[@]}" -O exit "${ssh_host}"
  fi

  if [ -n "${temp_dir:-}" ]; then
    rm -r "${temp_dir}"
  fi
}
trap clean_up EXIT

cd "$(dirname "${0}")"
temp_dir="$(mktemp -d)"

parse_args() {
  while [ "${#}" -gt 0 ]; do
    case "${1}" in
      --testing) testing=1 ;;

      --no-releases) sync_releases=0 ;;

      +([0-9a-f]))
        if [ "${#1}" -ne 40 ]; then
          printf 'error: expected full Git hash of build, but got %s\n' "${1}" >&2
          exit 1
        fi
        extra_builds+=("${1}")
        ;;

      *)
        printf 'error: unexpected argument: %s\n' "${1}" >&2
        exit 1
    esac
    shift
  done
}

main() {
  parse_args "${@}"

  if [ "${testing}" -ne 0 ]; then
    remote_debian_root="${remote_debian_root_testing}"
  fi
  if [ "${#extra_builds[@]}" -gt 0 ] && [ "${testing}" -eq 0 ]; then
    printf 'error: extra builds are only allowed with --testing\n' >&2
    exit 1
  fi
  if [ "${sync_releases}" -eq 0 ] && [ "${testing}" -eq 0 ]; then
    printf 'error: --no-releases is only allowed with --testing\n' >&2
    exit 1
  fi

  # Speed up repeated SSH/SCP commands by reusing a single TCP connection to the
  # server.
  ssh_control_socket="${temp_dir}/ssh-control.socket"
  ssh_args=(
    -o ControlMaster=auto
    -o ControlPath="${ssh_control_socket}"
    -o ControlPersist=yes
    "${ssh_args[@]}"
  )

  copy_releases_to_debian_pool
  create_unsigned_apt_metadata_files
  sign_apt_metadata_files
}

copy_releases_to_debian_pool() {
  printf 'Populating Debian pool from releases ...\n' >&2

  ssh "${ssh_args[@]}" "${ssh_host}" -- bash -s "${remote_builds_root}" "${remote_releases_root}" "${remote_debian_root}" "${sync_releases}" "${extra_builds[@]:+${extra_builds[@]}}" <<'EOF'
    set -e
    set -u

    builds_root="${1}"
    releases_root="${2}"
    debian_root="${3}"
    sync_releases="${4}"
    shift 4
    extra_builds=("${@}")

    archs=(all amd64 arm64)

    copy_build_to_pool() {
      local release_path="${1}"
      local release="$(basename "${release_path}")"
      if [ -d "${release_path}/debian/amd64" ]; then
        for arch_path in "${release_path}/debian/"*; do
          # NOTE(strager): Later architectures might overwrite files from
          # earlier architectures. This is fine. Unfortunately, some files (such
          # as {amd64,arm64}/quick-lint-js-vim_2.15.0-1_all.deb) are logically
          # identical but not bit-identical, so we choose an arbitrary version
          # of the file.
          #
          # FIXME(strager): There is a race condition. If someone downloads a
          # release while we copy, they might get the amd64 version of a file
          # before the arm64 version is written. (The amd64 and arm64 versions
          # might have different file hashes.)
          copy_build_bins_to_pool "${arch_path}/" "${release}"
        done
      else
        # Releases prior to 2.14.0 were only amd64 and did not have
        # architecture-specific directories.
        copy_build_bins_to_pool "${release_path}/debian/" "${release}"
      fi
    }

    copy_build_bins_to_pool() {
      local bin_path="${1}"
      local release="${2}"

      for file in "${bin_path}"/*; do
        local file_arch="$(classify_file "${file}")"
        mkdir -p "${debian_root}/pool/${release}/${file_arch}/"
        rsync -a "${file}" "${debian_root}/pool/${release}/${file_arch}/"
      done
    }

    classify_file() {
      local file="${1}"
      for arch in "${archs[@]}"; do
        if [[ "${file}" = *"_${arch}."* ]]; then
          echo "${arch}"
          return
        fi
      done
      echo source
    }

    mkdir -p "${debian_root}/pool/"
    if [ "${sync_releases}" -ne 0 ]; then
      for release_path in "${releases_root}"/[0-9]*; do
        copy_build_to_pool "${release_path}"
      done
    fi
    for extra_build in "${extra_builds[@]:+${extra_builds[@]}}"; do
      copy_build_to_pool "${builds_root}/${extra_build}"
    done
EOF
}

create_unsigned_apt_metadata_files() {
  printf 'Creating unsigned apt metadata files on server ...\n' >&2

  scp -q "${ssh_args[@]}" apt-ftparchive.conf "${ssh_host}:${remote_debian_root}/apt-ftparchive.conf"
  scp -q "${ssh_args[@]}" asgen-config.json "${ssh_host}:${remote_debian_root}/asgen-config.json"
  ssh "${ssh_args[@]}" "${ssh_host}" -- bash -s "${remote_debian_root}" <update-repository
}

sign_apt_metadata_files() {
  printf 'Signing apt metadata files ...\n' >&2

  scp -q "${ssh_args[@]}" "${ssh_host}:${remote_debian_root}/dists/${apt_suite}/Release" "${temp_dir}/Release"
  gpg --batch --yes --clear-sign --output "${temp_dir}/InRelease" "${temp_dir}/Release"
  gpg --batch --yes --armor --detach-sign --output "${temp_dir}/Release.gpg" "${temp_dir}/Release"
  scp -q "${ssh_args[@]}" "${temp_dir}/InRelease" "${temp_dir}/Release.gpg" "${ssh_host}:${remote_debian_root}/dists/${apt_suite}/"
}

main "${@}"

# quick-lint-js finds bugs in JavaScript programs.
# Copyright (C) 2020  Matthew "strager" Glazar
#
# This file is part of quick-lint-js.
#
# quick-lint-js is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# quick-lint-js is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with quick-lint-js.  If not, see <https://www.gnu.org/licenses/>.
