#!/bin/bash -eu
#
# Copyright (c) 2018-2020 SUSE LLC
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

: ${TOPDIR:=/usr/src/packages}

kiwi_dir="${TOPDIR}"/KIWI
rpm_source_dir="${TOPDIR}"/SOURCES
files_dir=/usr/lib/build
properties_xml="${rpm_source_dir}"/rpm-properties.xml


get_xml_data() {
    local xpath="${1}"
    local xml_file="${2}"
    echo $(xmllint --xpath "${xpath}" "${xml_file}")
}

find_source() {
    # Kiwi can generate both files, so look for *.raw.xz only
    # if *.install.tar does not exist
    #
    (ls *.install.tar || ls *.tar.xz || ls *.raw.xz || true ) 2> /dev/null
}

parse_source() {
    local -n _cfg="${1}"
    local source="${2}"
    set -- $(echo "${source}" | sed -r 's/([^\.]+)\.([^-]+)-([^-]+)-(.*)/\1 \2 \3 \4/')
    _cfg['name']="${1}"
    _cfg['arch']="${2}"
    _cfg['version']="${3}"
    # Include the Build prefix in the release
    _cfg['release']=$(echo "${4}" | sed -r 's/.*(Build[0-9]+\.[0-9]+).*/\1/')

    _cfg['year']=$(date +%Y)

    case "${source}" in
	*.tar.xz)
	    _cfg['type']='pxe'
	    _cfg['sourcekernelboot']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}-*.kernel"
	    _cfg['sourceinitrdboot']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.initrd"
	    _cfg['sourceimage']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}"
	    ;;
	*.install.tar)
	    _cfg['type']='oem'
            # "pxeboot.${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.kernel"
	    _cfg['sourcekernelboot']="pxeboot*.kernel"
            # "pxeboot.${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.initrd.xz"
	    _cfg['sourceinitrdboot']="pxeboot*.initrd"
            # "${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.xz"
	    _cfg['sourceimage']="${_cfg[name]}*.xz"
	    ;;
	*.raw.xz)
	    _cfg['type']='raw'
	    _cfg['sourcekernelboot']=
	    _cfg['sourceinitrdboot']=
	    _cfg['sourceimage']="${_cfg[name]}*.raw.xz"
	    ;;
	*)
	    echo "ERROR: Unkown image type for ${source}"
	    exit 1
	    ;;
    esac
    # "${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.md5"
    _cfg['sourcemd5image']="${_cfg[name]}*.md5"

    # It could not apply, but is good to have a sane defaults here,
    # the spec file macro will skip over those values
    # "${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.kernel"
    _cfg['sourcekernelimage']="${_cfg[name]}*.kernel"
    # "${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.initrd"
    _cfg['sourceinitrdimage']="${_cfg[name]}*.initrd"
    # "${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.config.bootoptions"
    _cfg['sourcebootoptions']="${_cfg[name]}*.config.bootoptions"
    # "${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.append"
    _cfg['sourceappend']="${_cfg[name]}*.append"
}

untar_source() {
    local source="${1}"
    local type="${source##*.}"
    case $type in
	tar)
	    tar -xvf "${source}"
	    ;;
	xz)
	    tar -xJvf "${source}"
	    ;;
	*)
	    echo "ERROR: Do not know how to decompress ${source}"
	    exit 1
	    ;;
    esac
}

parse_properties_xml() {
    local -n _cfg="${1}"
    local properties_xml="${2}"
    for key in summary description license licensefile group url \
		       provides excludearches targetboot kernelboot \
		       initrdboot ignoreimage targetimage image \
		       md5image kernelimage initrdimage bootoptions \
		       append; do
	_cfg[$key]=$(get_xml_data "string(//props/$key)" ${properties_xml})
    done

    declare -A default=(
	['licensefile']='LICENSE'
	['targetboot']="/srv/${_cfg[name]}"
	['kernelboot']='linux'
	['initrdboot']='initrd'
	['ignoreimage']='false'
	['image']="${_cfg[sourceimage]}"
	# "${_cfg[sourcemd5image]}"
	['md5image']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.md5"
	# "${_cfg[sourcekernelimage]}"
	['kernelimage']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.kernel"
	# "${_cfg[sourceinitrdimage]}"
	['initrdimage']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.initrd"
	['bootoptions']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.config.bootoptions"
	# "${_cfg[sourceappend]}"
	['append']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.append"
    )
    if [ "${_cfg[type]}" == "oem" -o "${_cfg[type]}" == "raw" ]; then
	_cfg['image']="${_cfg[name]}.${_cfg[arch]}-${_cfg[version]}.xz"
    fi

    for key in "${!_cfg[@]}"; do
	if [ "${_cfg[$key]}" == "''" -o -z "${_cfg[$key]}" ]; then
	    _cfg[$key]="${default[$key]-}"
	fi
    done

    # Some defaults depend on other default values, so we need a last
    # iteration to resolve them
    declare -A default=(
	["targetimage"]="${_cfg[targetboot]}/image"
    )
    for key in "${!_cfg[@]}"; do
	if [ "${_cfg[$key]}" == "''" -o -z "${_cfg[$key]}" ]; then
	    _cfg[$key]="${default[$key]-}"
	fi
    done
}

expand_cfg() {
    local -n _cfg="${1}"
    if [ "${_cfg[url]}" != "''" -a ! -z "${_cfg[url]}" ]; then
	_cfg['url']="URL:            ${_cfg[url]}"
    fi

    local provides=""
    for provide in "${_cfg[provides]}"; do
	[ -z "${provide}" ] && continue
	provides="${provides}Provides:       ${provide}\n"
    done
    _cfg['provides']="${provides}"

    local excludearches=""
    for arch in "${_cfg[excludearches]}"; do
	[ -z "${arch}" ] && continue
	excludearches="${excludearches}ExcludeArch:    ${arch}\n"
    done
    _cfg['excludearches']="${excludearches}"

    if [ "${cfg[ignoreimage]}" == "true" ]; then
	cfg['ignoreimage']=1
    else
	cfg['ignoreimage']=0
    fi
}


echo '██████████████████████████████████████████████████████████████'
echo '█                                                            █'
echo '█  containment-rpm-pxe: Turn your KIWI PXE images into RPMs  █'
echo '█       https://github.com/openSUSE/container-rpm-pxe        █'
echo '█                                                            █'
echo '██████████████████████████████████████████████████████████████'

cd "${kiwi_dir}"

echo 'INFO: Looking for a tar file from KIWI ...'
source=$(find_source)
if [ -z "${source}" ]; then
  echo 'ERROR: No tar file from KIWI found!'
  exit 1
elif [ $(echo "${source}" | wc -l) -gt 1 ]; then
  echo 'ERROR: More than one tar file found!'
  exit 1
fi
echo "INFO: tar file from KIWI found at ${source}"

# Read the properties from source and XML files
declare -A cfg
parse_source cfg "${source}"
if [ ! -f "${properties_xml}" ]; then
  echo "ERROR: No ${properties_xml} file. Make sure you have one in your package"
  exit 1
fi
parse_properties_xml cfg "${properties_xml}"

echo '=========================================='
echo "- source: ${source}"
echo '=========================================='
for item in "${!cfg[@]}"; do
    echo "- ${item}: ${cfg[$item]}"
done
echo '=========================================='

echo "INFO: Linking the ${source} into ${rpm_source_dir}"
ln "${source}" "${rpm_source_dir}"

echo 'INFO: Generating SPEC ...'
expand_cfg cfg
sed -e "s/__NAME__/${cfg[name]}/g" \
    -e "s/__YEAR__/${cfg[year]}/g" \
    -e "s/__TYPE__/${cfg[type]}/g" \
    -e "s/__IGNORE_IMAGE__/${cfg[ignoreimage]}/g" \
    -e "s/__VERSION__/${cfg[version]}/g" \
    -e "s/__RELEASE__/${cfg[release]}/g" \
    -e "s|__SUMMARY__|${cfg[summary]}|g" \
    -e "s/__LICENSE__/${cfg[license]}/g" \
    -e "s|__GROUP__|${cfg[group]}|g" \
    -e "s|__URL__|${cfg[url]}|g" \
    -e "s|__SOURCE__|${source}|g" \
    -e "s/__LICENSE_FILE__/${cfg[licensefile]}/g" \
    -e "s/__EXCLUDE_ARCHS__/${cfg[excludearches]}/g" \
    -e "s/__PROVIDES__/${cfg[provides]}/g" \
    -e "s|__DESCRIPTION__|${cfg[description]}|g" \
    -e "s|__TARGET_BOOT__|${cfg[targetboot]}|g" \
    -e "s/__SOURCE_KERNEL_BOOT__/${cfg[sourcekernelboot]}/g" \
    -e "s/__KERNEL_BOOT__/${cfg[kernelboot]}/g" \
    -e "s/__SOURCE_INITRD_BOOT__/${cfg[sourceinitrdboot]}/g" \
    -e "s/__INITRD_BOOT__/${cfg[initrdboot]}/g" \
    -e "s|__TARGET_IMAGE__|${cfg[targetimage]}|g" \
    -e "s/__SOURCE_IMAGE__/${cfg[sourceimage]}/g" \
    -e "s/__IMAGE__/${cfg[image]}/g" \
    -e "s/__SOURCE_MD5_IMAGE__/${cfg[sourcemd5image]}/g" \
    -e "s/__MD5_IMAGE__/${cfg[md5image]}/g" \
    -e "s/__SOURCE_KERNEL_IMAGE__/${cfg[sourcekernelimage]}/g" \
    -e "s/__KERNEL_IMAGE__/${cfg[kernelimage]}/g" \
    -e "s/__SOURCE_INITRD_IMAGE__/${cfg[sourceinitrdimage]}/g" \
    -e "s/__INITRD_IMAGE__/${cfg[initrdimage]}/g" \
    -e "s/__SOURCE_BOOTOPTIONS__/${cfg[sourcebootoptions]}/g" \
    -e "s/__BOOTOPTIONS__/${cfg[bootoptions]}/g" \
    -e "s/__SOURCE_APPEND__/${cfg[sourceappend]}/g" \
    -e "s/__APPEND__/${cfg[append]}/g" \
    < ${files_dir}/image.spec.in \
    > ${files_dir}/image.spec

echo 'INFO: Adding changelog to the SPEC ...'
/.build/changelog2spec --target rpm --file "${rpm_source_dir}"/"${cfg[name]}".changes >> "${files_dir}"/image.spec
echo 'INFO: SPEC to be used:'
echo '=========================================='
cat "${files_dir}"/image.spec
echo '=========================================='

echo 'INFO: Building RPM packages...'
rpmbuild -ba ${files_dir}/image.spec

# Required in OBS / IBS to find the RPM, because it is a "non-standard
# result file for KIWI"
mkdir -p "${TOPDIR}"/OTHER
arch_rpm="${TOPDIR}/RPMS/noarch/${cfg[name]}-${cfg[version]}-${cfg[release]}.noarch.rpm"
src_rpm="${TOPDIR}/SRPMS/${cfg[name]}-${cfg[version]}-${cfg[release]}.src.rpm"
echo "INFO: Moving ${arch_rpm} to ${TOPDIR}/OTHER/"
mv "${arch_rpm}" "${TOPDIR}"/OTHER/
echo "INFO: Moving ${src_rpm} to ${TOPDIR}/OTHER/"
mv "${src_rpm}" "${TOPDIR}"/OTHER/
