#!/bin/bash
#
# © 2015 - 2026 Christopher Roy Bratušek <nano@jpberlin.de>
#
# License: GNU GPL v3+
#
# Various functions to aid maintanance of an apt repo and building packages
#
# See /usr/share/nanolx/repokit.conf for example configuration. Save that file as
#   ${HOME}/.repokit.conf
#

version="1.7.9"
reldate="2026/09/10"

scriptname=$(basename ${0})

error () {
    echo -e "${@}"
    exit 1
}

if [[ -f ${HOME}/.${scriptname}.conf ]]; then
        source "${HOME}/.${scriptname}.conf"
else    error "no ${HOME}/.${scriptname}.conf exists (see /usr/share/nanolx/${scriptname}.conf for example)"
fi

debhelper_prepare () {
    if [[ -d "${PWD}/debian" ]]; then
        error "debian packaging scripts already exist in ${PWD}
you may want to use ${scriptname} release instead."
    fi

    get_package_name_and_ver $(basename "${PWD}") package version
    dh_make -s -e "${REPOKIT_MAIL}" --copyright "${@}" -p "${package}" || exit 1

    if [ -d "${PWD}/debian" ]; then
        rm -rf debian/*.ex debian/README* debian/*.EX

        sed -e "s/1) UNRELEASED.*/1${REPOKIT_REV}) ${REPOKIT_DIST}; urgency=low/g" \
            -e "s/ (Closes: #nnnn)  <nnnn is the bug number of your ITP>//g" \
            -i "${PWD}/debian/changelog"
        sed -e 's/\#export/export/g' -i "${PWD}/debian/rules"
        sed -e 's/^#/d' -i "${PWD}/debian/copyright"
        sed -e 's/<insert.*//g' \
            -e '/\#Vcs/d' \
            -e 's/Section.*/Section:/g' \
            -i "${PWD}/debian/control"
    fi
}

get_package_name_and_ver () {
    local -n xpackage="${2}"
    local -n xversion="${3}"
    if [[ "${1}" == *_* ]]; then
        xpackage="${1%%_*}"
        xversion="${1#*_}"
    elif [[ "${1}" == *-* ]]; then
        xpackage="${1%-*}"
        xversion="${1##*-}"
    fi

    [ -z "${xpackage}" ] && error "could not determine package name, stop."
    [ -z "${xversion}" ] && error "could not determine package version, stop."
}

debhelper_get_scripts () {
    get_package_name_and_ver $(basename "${PWD}") package version

    if [[ ! -d "${REPOKIT_BUILD_SCRIPTS}/${package}" ]]; then
        error "no packaging scripts for ${package} found"
    fi

    [[ -d "${PWD}/debian" ]] && rm -rf "${PWD}/debian"
    mkdir -p "${PWD}/debian"

    rsync -a --exclude='.git*' "${REPOKIT_BUILD_SCRIPTS}/${package}/" "${PWD}/debian/"
    debhelper_add_release

}

debhelper_add_release () {
    if [[ ! -d "${PWD}/debian" ]]; then
        error "No debian packagings scripts in ${PWD}, stop."
    fi

    local revision=1
    local rev_text="New upstream release"

    get_package_name_and_ver $(basename "${PWD}") package version

    if [[ -s "${PWD}/debian/changelog" ]]; then
        local first_line=$(head -n 1 "${PWD}/debian/changelog")
        local regex="^${package} \((([0-9]+:)?)(.+)-([0-9]+)${REPOKIT_REV}\)"

       if [[ "${first_line}" =~ ${regex} ]]; then
            epoch="${BASH_REMATCH[1]}"
            local changelog_upstream="${BASH_REMATCH[3]}"
            local changelog_revision="${BASH_REMATCH[4]}"

            version="${epoch}${version}"

            if [[ "${version}" == "${epoch}${changelog_upstream}" ]]; then
                revision=$(( changelog_revision + 1 ))
                rev_text="New build"
            fi
        fi
    fi

    DEBEMAIL="${REPOKIT_MAIL}" DEBFULLNAME="${REPOKIT_NAME}" \
    dch --changelog "${PWD}/debian/changelog" \
        --newversion "${version}-${revision}${REPOKIT_REV}" \
        --distribution "${REPOKIT_DIST}" \
        --urgency "low" \
        "${rev_text}"
}

debhelper_put_scripts () {
    if [[ ! -d "${PWD}/debian" ]]; then
        error "No debian packagings scripts in ${PWD}, stop."
    fi

    get_package_name_and_ver $(basename "${PWD}") package version

    if [[ ! -d ${REPOKIT_BUILD_SCRIPTS}/${package} ]]; then
            mkdir -p "${REPOKIT_BUILD_SCRIPTS}/${package}"
            status="[new]"
    else
        local git_version=$(sed -n '1s/.*(\([^)]*\)).*/\1/p' "${REPOKIT_BUILD_SCRIPTS}/${package}/changelog")
        git_version="${git_version%-*}"
        if [[ "${version}" == "${git_version}" ]]; then
            status="[rebuild]"
        else
            status="[update]"
        fi
    fi

    "${PWD}/debian/rules" clean &>/dev/null

    rsync -a --delete "${PWD}/debian/" "${REPOKIT_BUILD_SCRIPTS}/${package}"
    cd "${REPOKIT_BUILD_SCRIPTS}"
    git add -A "${package}"
    git commit "${package}" -m "${package} ${status} ${version}"
}

reprepro_add () {
    reprepro -Vb "${REPOKIT_PATH}" include "${REPOKIT_DIST}" "${1}" && \
        if ${REPOKIT_FTP_AUTO}; then
            ftp_push
        fi
}

reprepro_remove () {
    reprepro -Vb "${REPOKIT_PATH}" remove "${REPOKIT_DIST}" "${1}" && \
        if ${REPOKIT_FTP_AUTO}; then
            ftp_push
        fi
}

ftp_push () {
    if [ -f ${HOME}/.${scriptname}.pw ]; then
        REPOKIT_FTP_PW=$(gpg --decrypt ${HOME}/.${scriptname}.pw) || REPOKIT_FTP_PW=""
    else
        error "No GPG encrypted password file (${HOME}/.${scriptname}.pw) found"
    fi

    for dir in pool dists logs; do
        lftp -c "set ssl:check-hostname no; \
                open ftp://${REPOKIT_FTP_USR}:${REPOKIT_FTP_PW}@${REPOKIT_FTP_SRV}; \
                mirror -R -L --delete ${REPOKIT_PATH}/${dir}/ /${REPOKIT_FTP_DIR}/${dir}/"
    done
}

user_add_subids () {
    if ! grep -q "${USER}" /etc/subuid; then
        echo "user ${USER} doesn't have sub UIDs/sub GIDs, adding"
        echo "(sudo may ask for password)"
        MAX_ID=$(awk -F: '{print $2}' /etc/subuid 2>/dev/null | sort -n | tail -n 1)
        START_ID=$(( ${MAX_ID:-100000} + 65536 ))
        END_ID=$(( START_ID + 65535 ))
        sudo usermod --add-subuids ${START_ID}-${END_ID} \
            --add-subgids ${START_ID}-${END_ID} "${USER}"
    fi
}

sbuild_bootstrap () {
    user_add_subids

    local native_arch=$(dpkg --print-architecture)

    mkdir -p "${HOME}/.cache/sbuild/"
    mmdebstrap --include=ca-certificates \
        --skip=output/dev "${REPOKIT_CROSS_DIST}" \
        --arch=${native_arch} \
        --arch=${REPOKIT_CROSS} \
        --include=ccache \
        --customize-hook='chroot "$1" update-ccache-symlinks' \
         "${HOME}/.cache/sbuild/${REPOKIT_CROSS_DIST}-${native_arch}.tar.zst" \
         https://deb.debian.org/debian

    if [ ! -f "${HOME}/.config/sbuild/config.pl" ]; then
        echo "no sbuild configuration file at ${HOME}/.config/sbuild/config.pl found
see /usr/share/nanolx/config.pl for example file and
https://wiki.debian.org/sbuild#Setup for more information"
    fi
}

sbuild_get_arch () {
    mapfile -t archs < <(tar --wildcards -O -xf "${1}" './var/lib/dpkg/arch' 2>/dev/null)
    echo "${archs[0]} (${archs[@]:1})"
}

sbuild_get_suite () {
    tar --wildcards -O -xf "${1}" './etc/apt/sources.list' 2>/dev/null \
        | grep -v '^#' \
        | grep -m 1 'deb ' \
        | awk '{print $3}' \
        | sed 's/[-/].*//' || echo "unstable"
}

sbuild_list () {
    i=0
    for tarball in ${HOME}/.cache/sbuild/*.tar.*; do
        ((i++))
        echo ""
        echo "[${i}] $(basename "${tarball}")"
        echo -n "   -> Architectures: "
        sbuild_get_arch "${tarball}"
        echo -n "   -> Suite: "
        sbuild_get_suite "${tarball}"
        echo ""
    done
}

sbuild_update () {
    for tarball in ${HOME}/.cache/sbuild/*.tar.*; do
        echo "updating $(basename "${tarball}")"
        mmdebstrap --variant=buildd $(sbuild_get_suite "${tarball}") "${tarball}"
    done
}

sbuild_cross () {
    if [[ "${1}" == *.dsc ]]; then
        for arch in ${REPOKIT_CROSS//,/ }; do
            sbuild --host=${arch} \
                --dist ${REPOKIT_CROSS_DIST} \
                ${REPOKIT_SBUILD_FLAGS} \
                "${1}"
        done
    else
        echo "${1} is not a .dsc file"
    fi
}

debhelper_build_orig () {
    if [[ -d "${1}" ]]; then
        tarname="$(basename "${1}")"
        tardir="$(dirname "${1}")"
        tar cf "${tarname}".orig.tar -C "${tardir}" "${tarname}"
        ${REPOKIT_ORIG_COMPRESSOR} "${REPOKIT_LEVL_COMPRESSOR}" "${tarname}".orig.tar
    fi
}

source_dist () {
    if [[ -d ${1} ]]; then
        dir=$(readlink -m "${1}")

        for comp in "${REPOKIT_DIST_COMPRESSOR[@]}"; do
            compexe=${comp/*:}
            compsuf=${comp/:*}

            tarname="$(basename "${1}")"
            tardir="$(dirname "${1}")"
            tar cf "${dir}".tar -C "${tardir}" "${tarname}"
            ${compexe} "${REPOKIT_LEVL_COMPRESSOR}" "${dir}.tar"

            [[ ${REPOKIT_SHA256} == True ]] && \
                sha256sum "${dir}.tar.${compsuf}" > "${dir}.tar.${compsuf}.sha256"

            [[ ${REPOKIT_SHA256} == True && ${REPOKIT_SIGN} == True ]] && \
                gpg --sign --detach-sign "${dir}.tar.${compsuf}.sha256"
        done
    fi
}

cmdarg="${1}"
shift

case ${cmdarg} in
    prepare )   debhelper_prepare "${@}" ;;
    get )       debhelper_get_scripts ;;
    release )   debhelper_add_release ;;
    put )       debhelper_put_scripts ;;

    build )     dpkg-buildpackage "${REPOKIT_BUILD_FLAGS}" "${@}" ;;
    clean )     debian/rules clean ;;

    add )
        for arg in "${@}"; do
            reprepro_add "${arg}"
        done
    ;;
    remove )
        for arg in "${@}"; do
            reprepro_remove "${arg}"
        done
    ;;
    ftp )       ftp_push ;;

    screate )   sbuild_bootstrap ;;
    supdate )   sbuild_update ;;
    slist )     sbuild_list ;;
    scross )
        for arg in "${@}"; do
            sbuild_cross "${arg}"
        done
    ;;

    orig )
        for arg in "${@}"; do
            debhelper_build_orig "$(readlink -m "${arg}")"
        done
    ;;
        dist )
        for arg in "${@}"; do
            source_dist "${arg}"
        done
    ;;

    version)
        echo "${scriptname} ${version} (${reldate})"
    ;;

    * )
        echo "${scriptname} ${version} (${reldate})

© 2015 - 2026 Christopher Roy Bratušek <nano@jpberlin.de>

licensed under the GNU General Public License v3 (or newer)

${scriptname} is my personal wrapper script for reprepro with config file support and
many features. ${scriptname} expects a configuration file \${HOME}/.${scriptname}.conf -
see /usr/share/nanolx/${scriptname}.conf for example configuration.

Options
option      Parameters  Description

version     ---         print version

** packaging scripts **
prepare     license     prepare packaging scripts for new package
get         ---         get packaging scripts for current package (if any)
release     ---         add new release to debian/changelog
put         ---         push packaging scripts for current package to git repo

** package building **
build       ---         build package using dpkg-buildpackage
clean       ---         clean package build directory

** package management **
add         .changes    add package to repository
remove      package     remove package from repository
ftp         ---         push local apt repo to remote using lftp

** cross building **
screate     ---         create tarball for sbuild
supdate     ---         update tarball for sbuild
slist       ---         list availabe unshare chroots
scross      .dsc        cross build package using sbuild

** source management **
orig        directory   create orig tarball from source tree
dist        directory   generate tarballs, checksums and signatures for release distribution
"
    ;;

esac
