#!/bin/bash
#
# offload-build - build a PKGBUILD on a remote server using makechrootpkg.
#
# Copyright (c) 2019 by Eli Schwartz <eschwartz@archlinux.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later

_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-/usr/share/devtools}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/util/makepkg.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/makepkg.sh

source /usr/share/makepkg/util/config.sh

# Deprecation warning
if [[ -z $_DEVTOOLS_COMMAND ]]; then
	warning "${0##*/} is deprecated and will be removed. Use 'pkgctl build --offload' instead"
fi

# global defaults suitable for use by Arch staff
repo=extra
arch=x86_64
server=build.archlinux.org

usage() {
    cat <<- _EOF_
		Usage: ${BASH_SOURCE[0]##*/} [--repo REPO] [--arch ARCHITECTURE] [--server SERVER] -- [ARCHBUILD_ARGS]

		Build a PKGBUILD on a remote server using makechrootpkg. Requires a remote user
		that can run archbuild without password auth. Options passed after a -- are
		passed on to archbuild, and eventually to makechrootpkg.

		OPTIONS
		    -r, --repo      Build against a specific repository (current: $repo)
		    -a, --arch      Build against a specific architecture (current: $arch)
		    -s, --server    Offload to a specific build server (current: $server)
		    -h, --help      Show this help text
_EOF_
}

# option checking
while (( $# )); do
    case $1 in
        -h|--help)
            usage
            exit 0
            ;;
        -r|--repo)
            repo=$2
            shift 2
            ;;
        -a|--arch)
            arch=$2
            shift 2
            ;;
        -s|--server)
            server=$2
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            die "invalid argument: %s" "$1"
            ;;
    esac
done

# multilib must be handled specially
archbuild_arch="${arch}"
if [[ $repo = multilib* ]]; then
    archbuild_arch=
fi

archbuild_cmd=("${repo}${archbuild_arch:+-$archbuild_arch}-build" "$@")

[[ -z ${WORKDIR:-} ]] && setup_workdir
export TEMPDIR=$(mktemp --tmpdir="${WORKDIR}" --directory offload-build.XXXXXXXXXX)

# Load makepkg.conf variables to be available
load_makepkg_config

# Use a source-only tarball as an intermediate to transfer files. This
# guarantees the checksums are okay, and guarantees that all needed files are
# transferred, including local sources, install scripts, and changelogs.
export SRCPKGDEST="${TEMPDIR}"
makepkg_source_package || die "unable to make source package"

# Temporary cosmetic workaround makepkg if SRCDEST is set somewhere else
# but an empty src dir is created in PWD. Remove once fixed in makepkg.
rmdir --ignore-fail-on-non-empty src 2>/dev/null || true

# Create a temporary directory on the server
remote_temp=$(
    ssh "${SSH_OPTS[@]}" -- "$server" '
        temp="${XDG_CACHE_HOME:-$HOME/.cache}/offload-build" &&
        mkdir -p "$temp" &&
        mktemp --directory --tmpdir="$temp"
')

# Transfer the srcpkg to the server
msg "Transferring source package to the server..."
_srcpkg=("$SRCPKGDEST"/*"$SRCEXT")
srcpkg="${_srcpkg[0]}"
rsync "${RSYNC_OPTS[@]}" -- "$srcpkg" "$server":"$remote_temp" || die

# Prepare the srcpkg on the server
msg "Extracting srcpkg"
ssh "${SSH_OPTS[@]}" -- "$server" "cd ${remote_temp@Q} && bsdtar --strip-components 1 -xvf $(basename "$srcpkg")" || die

# Run the build command on the server
msg "Running archbuild"
# shellcheck disable=SC2145
if ssh "${SSH_OPTS[@]}" -t -- "$server" "cd ${remote_temp@Q} && export LOGDEST="" && ${archbuild_cmd[@]@Q}"; then
    msg "Build complete"

    # Get an array of files that should be downloaded from the server
    mapfile -t files < <(
        ssh "${SSH_OPTS[@]}" -- "$server" "
            cd ${remote_temp@Q}"' &&
            makepkg_user_config="${XDG_CONFIG_HOME:-$HOME/.config}/pacman/makepkg.conf" &&
            makepkg_config="/usr/share/devtools/makepkg.conf.d/'"${arch}"'.conf" &&
            if [[ -f /usr/share/devtools/makepkg.conf.d/'"${repo}"'-'"${arch}"'.conf ]]; then
                makepkg_config="/usr/share/devtools/makepkg.conf.d/'"${repo}"'-'"${arch}"'.conf"
            fi &&
            while read -r file; do
                [[ -f "${file}" ]] && printf "%s\n" "${file}" ||:
            done < <(makepkg --config <(cat "${makepkg_user_config}" "${makepkg_config}" 2>/dev/null) --packagelist) &&
            printf "%s\n" '"${remote_temp@Q}/PKGBUILD"'

            find '"${remote_temp@Q}"' -name "*.log"
    ')
else
    # Build failed, only the logs should be downloaded from the server
    mapfile -t files < <(
        ssh "${SSH_OPTS[@]}" -- "$server" '
            find '"${remote_temp@Q}"' -name "*.log"
    ')
fi


if (( ${#files[@]} )); then
    msg 'Downloading files...'
    rsync "${RSYNC_OPTS[@]}" -- "${files[@]/#/$server:}" "${TEMPDIR}/" || die

    if is_globfile "${TEMPDIR}"/*.log; then
        mv "${TEMPDIR}"/*.log "${LOGDEST:-${PWD}}/"
    fi
    if is_globfile "${TEMPDIR}"/*.pkg.tar*; then
        # Building a package may change the PKGBUILD during update_pkgver
        mv "${TEMPDIR}/PKGBUILD" "${PWD}/"
        mv "${TEMPDIR}"/*.pkg.tar* "${PKGDEST:-${PWD}}/"
    else
        error "Build failed, check logs in ${LOGDEST:-${PWD}}"
        exit 1
    fi
else
    exit 1
fi
