#!/usr/bin/env bash
#
# Copyright (C) 2024 Laurent Jourden <laurent85@enarel.fr>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Script to build the ZFS packages against the linux kernel 
# for Arch Linux.

set -e -u

WD="$(pwd)"
appname="${0##*/}"
pkgbuild_dir="/usr/share/archuseriso/pkgbuild"
auiwork="${WD}/$(mktemp -u auiwork.XXXXXXXX)"
ar64="${auiwork}/archroot64"
help="no"
linuxtesting="no"
linuxversion=""
modulesversion=""
pkgdest=""
pkgdest_makepkg=""
release=""
SCRIPTUSER="${SUDO_USER:-}"
zfsversion=""
# https://openzfs.github.io/openzfs-docs/Project%20and%20Community/Signing%20Keys.html
ZFSPUBKEYS=(C77B9667 D4598027 C6AF658B)
declare -a zfsmissingkeys=()
declare -a zfspackages
declare -a zfssources

_usage () {
    echo
    echo "${appname}, ZFS packages creation tool."
    echo
    echo 'Synopsis:'
    echo "${appname} [options]"
    echo
    echo 'Help:'
    echo "${appname} --help"
    echo
    exit "${1}"
}

_help () {
    echo
    echo "${appname}, ZFS packages creation tool."
    echo
    echo 'Synopsis:'
    echo "${appname} [options]"
    echo
    echo 'Options:'
    echo '-h, --help                Command line help'
    echo "-D, --pkgbuild-dir=<path> Path to pkgbuild directory (default: ${pkgbuild_dir})"
    echo '--linuxtesting            Build packages against the linux kernel in testing repository'
    echo '--pkgdest=<path>          Packages destination directory. Default: ./out'
    echo '-r, --release             Specify the zfs release version to build.'
    echo "                          Example: ${appname} --release=2.1.2"
    echo
    exit "${1}"
}

_msg_info() {
    local _msg="${1}"
    printf '[%s] INFO: %s\n' "${appname}" "${_msg}"
}

_cleanup() {
    if [[ -d "${auiwork}" ]]; then
       rm -r -- "${auiwork}"
    fi
}

_init () {
    local _link="https://api.github.com/repos/openzfs/zfs/releases"
    if ! pacman -Q devtools &> /dev/null; then
        echo 'devtools package not installed, aborting!'
        exit 0
    fi
    if ! pacman -Q pacman-contrib &> /dev/null; then
        echo 'pacman-contrib package not installed, aborting!'
        exit 0
    else
        # check host PKGDEST setting in /etc/makepkg.conf
        # where packages created have to be placed
        pkgdest_makepkg="$(grep -s '^PKGDEST=' /etc/makepkg.conf || true)"
        pkgdest_makepkg="${pkgdest_makepkg#PKGDEST=}"
    fi
    pkgdest_makepkg="${pkgdest_makepkg:-"${WD}/out"}"
    pkgdest="${pkgdest:-${pkgdest_makepkg}}"
    mkdir -p -- "${ar64}" "${pkgdest}"
    if [[ -z "${release}" ]]; then
        mapfile -t zfssources < <(curl --silent --retry 3 --retry-connrefused --fail -- \
                                  "${_link}"/latest |                                   \
                                  grep browser_download_url | cut -d':' -f2- | sed 's/\"//g; s/^[[:blank:]]*//')
    else
        mapfile -t zfssources < <(curl --silent --retry 3 --retry-connrefused --fail -- \
                                  "${_link}" | grep "${release}" | \
                                  grep browser_download_url | cut -d':' -f2- | sed 's/\"//g; s/^[[:blank:]]*//')
        if [[ -z "${zfssources:-}" ]]; then 
            echo "release ${release} not found, aborting!"
            exit 0
        fi
    fi
    zfsversion="${zfssources[0]%/zfs-*}"
    zfsversion="${zfsversion#*-}"
    if [[ -z "${zfsversion}" ]]; then
        echo 'Retrieving ZFS data failed, aborting!'
        exit 0
    fi
}

_create_archroot64 () {
    _msg_info "Creating chroot environment!"
        LC_ALL=C mkarchroot -C "${pkgbuild_dir}/pacman.conf" -c /var/cache/pacman/pkg -- "${ar64}/root" base linux linux-headers base-devel > /dev/null
    if [[ "${linuxtesting}" == "yes" ]]; then
        unshare --fork --pid pacman --config "${pkgbuild_dir}/pacman-testing.conf" --root "${ar64}/root" -Sy
        if unshare --fork --pid pacman --config "${pkgbuild_dir}/pacman-testing.conf" --root "${ar64}/root" -Si testing/linux; then
            pacstrap -C "${pkgbuild_dir}/pacman-testing.conf" -c -G -M "${ar64}/root" linux linux-headers > /dev/null
        else
            echo
            echo 'No linux package in testing currently, aborting!'
            echo
            rm -r -- "${auiwork}"
            exit 1
        fi
    fi
    _msg_info "Done!"
}

_build_zfs () {
    linuxversion=$(pacman --sysroot "${ar64}/root" -Q linux | cut -d' ' -f2)
    if [[ "$(cut -d'.' -f3 <<< "${linuxversion}")" =~ 'arch' ]]; then
        modulesversion="${linuxversion%.arch*}.0-${linuxversion##*.}"
    else
        modulesversion="${linuxversion%.arch*}-${linuxversion##*.}"
    fi
    cp -arT -- "${pkgbuild_dir}/zfs-utils/" "${auiwork}/zfs-utils/"
    cp -arT -- "${pkgbuild_dir}/zfs-linux/" "${auiwork}/zfs-linux/"
    sed -i -- "s/%ZFSVERSION%/${zfsversion}/;
               s/%LINUXVERSION%/${linuxversion}/;
               s/%MODULESVERSION%/${modulesversion}/" \
              "${auiwork}/zfs-utils/PKGBUILD" "${auiwork}/zfs-linux/PKGBUILD"
    cd -- "${auiwork}/zfs-utils/"
    for _zfslink in "${zfssources[@]}"; do
            curl --silent --retry 3 --retry-connrefused --fail -L -O "${_zfslink}"
    done
    cp -a -- "zfs-${zfsversion}.tar.gz"{,.asc} ../zfs-linux
    chown -R -- "${SCRIPTUSER}": "${auiwork}/"{zfs-utils,zfs-linux}

    _msg_info "Building zfs-utils & zfs-utils-debug"
    LC_ALL=C sudo --user "${SCRIPTUSER}" makechrootpkg -r "${ar64}" -- PKGDEST="" --cleanbuild --clean --force --syncdeps --needed --noconfirm --noprogressbar
    mapfile -t zfspackages < <(find -- "${auiwork}/"{zfs-utils,zfs-linux} | grep -E '\.pkg\.tar\.(zst|xz)$')
    _msg_info "Done!"

    cd -- "${auiwork}/zfs-linux/"
    _msg_info "Building zfs-linux & zfs-linux-headers" 
    LC_ALL=C sudo --user "${SCRIPTUSER}" makechrootpkg -r "${ar64}" -I "${zfspackages[0]}" -I "${zfspackages[1]}" -- PKGDEST="" --cleanbuild --clean --force --syncdeps --needed --noconfirm --noprogressbar
    mapfile -t zfspackages < <(find -- "${auiwork}/"{zfs-utils,zfs-linux} | grep -E '\.pkg\.tar\.(zst|xz)$')
    _msg_info "Done!"

    cp -- "${zfspackages[@]}" "${pkgdest}"
    rm -r -- "${auiwork}"
}

OPTS=$(getopt -o 'C:,D:,h,r:' -l 'help,linuxtesting,pkgbuild-dir:,pkgdest:,release:' -n "${appname}" -- "$@")
[[ $? -eq 0 ]] || _usage 1
eval set -- "${OPTS}"
unset OPTS

while true; do
    case "${1}" in
        '-h'|'--help')
            help="yes"
            shift ;;
        '--linuxtesting')
            linuxtesting="yes"
            shift ;;
        '-D'|'--pkgbuild-dir')
            pkgbuild_dir="${2}"
            shift 2 ;;
        '--pkgdest')
            pkgdest="${2}"
            shift 2 ;;
        '-r'|'--release')
            release="${2}"
            shift 2 ;;
        '--')
            shift
            break ;;
    esac
done

if [[ "${help}" == "yes" ]]; then
    _help 0
fi

if [[ ${EUID} -ne 0 ]]; then
    echo "This script must be run as root."
    echo
    echo "Get help:"
    echo "${appname} --help"
    exit 1
fi

if [[ "${SCRIPTUSER}" = 'root' || -z "${SUDO_USER:-}" ]]; then
    echo
    echo 'The script must be run from a _user session_ using sudo!'
    echo 'Aborting...'
    exit 0
fi


for _zfspubkey in ${ZFSPUBKEYS[@]}; do
    if ! sudo --user "${SCRIPTUSER}" gpg --list-public-keys "${_zfspubkey}" &> /dev/null; then
        zfsmissingkeys+=("${_zfspubkey}")
    fi
done

if [[ -n "${zfsmissingkeys[*]}" ]]; then
    echo "Missing OpenZFS public keys: ${zfsmissingkeys[*]}"
    echo
    read -r -n1 -p 'Retreive missing OpenZFS public keys (N/y)? '
    echo
    if [[ ! "${REPLY}" =~ ^[Yy]$ ]]; then
        echo 'Operation canceled by user!'
        exit 0
    fi
    for _zfsmissingkey in ${zfsmissingkeys[@]}; do
        if ! sudo --user "${SCRIPTUSER}" gpg --recv "${_zfsmissingkey}"; then
            echo
            echo "Retreiving OpenZFS public key ${_zfsmissingkey} failed, aborting!"
            echo
        fi
    done
fi

trap _cleanup EXIT
_init
_create_archroot64
_build_zfs

cd "${WD}"

echo
echo 'Done!'
echo
echo "${zfspackages[*]}" | sed 's|\s|\n|g' | sed 's|.*/||'
echo
echo 'ZFS packages directory location: '\'"${pkgdest}"\'
echo

# vim:ts=4:sw=4:et:
