#!/bin/sh

set -e

# Script to semi-automate updating Devuan forked packages.
# Optional argument is git tag to merge

suite=unstable
debian_base=deb.debian.org/debian

exit_with_msg()
{
    echo "$1"
    exit "${2:-0}"
}

usage()
{
    exit_with_msg "Usage: $0 [ -a ] [ -b ] [ -f ] [ -i ] [ -s suite ] [ -t ] [ <hint> ]" 2
}

# parse command line
while getopts abfis:t opt
do
    case $opt in
	a) auto=$opt;;
	b) do_build=$opt;;
	f) force=$opt;;
	i) debian_base=incoming.debian.org/debian-buildd;;
	s) suite=$OPTARG
	   [ "${suite%%*-security}" ] || debian_base=security.debian.org/debian-security
	   ;;
	t) do_tag=$opt;;
      \?) usage;;
    esac
done

shift $((OPTIND - 1))

[ $# -gt 1 ] && usage

git rev-parse --is-inside-work-tree >/dev/null || {
    echo Not a git repository
    exit 1
}

expand_gbp_config ()
{
    f=$(gbp config "$1")
    expansions="{
    		  'pkg': '$(dpkg-parsechangelog -S Source)',
    		  'version': '$(dpkg-parsechangelog -S Version | cut -d: -f2- | tr "~" "_")'
	        }"

    python3 -c "print('$f' % $expansions)"
}

debian_get_dsc()
{
    url=$1
    curl -fSs "$url"
}

deb822_get()
{
    grep "^$1:" | cut -d: -f2- | sed 's/^[[:space:]]*//'
}

git_remote_name()
{
    vcs_git=$1
    for u in $vcs_git $(echo "$vcs_git" | sed 's#^http.\?://#git@#; s#/#:#') # Try both http:// and git@ schemes
    do
	match=$(git remote -v | grep "$u" | cut -f1 | uniq)
	[ -z "$match" ] || {
	    echo "$match" && break
	}
    done
}

debian_dsc_url()
{
    p=$1
    v=$2
    pfx=$(echo "$p" | grep --only-matching '^\(lib\)\?.')
    printf >&2 "http://%s/pool/main/%s/%s/%s_%s.dsc\n" "${debian_base}" "$pfx" "$p" "$p" "${v#*:}"
    printf "http://%s/pool/main/%s/%s/%s_%s.dsc" "${debian_base}" "$pfx" "$p" "$p" "${v#*:}"
}

debian_suite()
{
    suite=$1

    case $suite in
	jessie|experimental|*stable|testing)
	    deb_suite=$suite
	    ;;
	*)
	    debian_jessie=8
	    ascii_a=97
	    debian_v=$(($(printf "%d" "'$suite") - ascii_a + debian_jessie + 1))
	    [ "$suite" != "${suite#*-}" ] && sub_suite=-${suite#*-}
	    deb_suite=$(awk -F, "\$1==\"$debian_v\" { print \$3 }" /usr/share/distro-info/debian.csv)${sub_suite}
	    ;;
    esac
    [ -z "$deb_suite" ] && exit_with_msg 'Failed to find Debian suite codename' 1
    echo "$deb_suite"
}

debian_version()
{
    src=$1
    suite=$2
    for url in debian udd; do
	v=$(rmadison -u "$url" -s "$suite" -a source "$src" | cut -d'|' -f2 | sort --version-sort | tail -1 | tr -d '[:space:]')
	if [ "$v" ] ; then
	    echo "$v"
	    return
	fi
    done
    exit_with_msg "Failed to get current version in Debian $suite" 1
}

importdsc()
{
    debian_branch=$1
    url=$2

    gbp import-dsc --debian-tag='debian/%(version)s' --debian-branch="$debian_branch" "$url"
}

cd "$(git rev-parse --show-toplevel)"

[ -z "$(git status --porcelain)" ] || {
    echo "ERROR: Tree not clean." && exit_with_msg "$(git status)" 1
}

devuan_remote=$(git_remote_name 'https://git.devuan.org/devuan')
[ -z "${devuan_remote}" ] && exit_with_msg 'Devuan remote not found' 1

git fetch "$devuan_remote"

git checkout "suites/$suite"

grep -q '^Origin: \+Devuan' debian/control || exit_with_msg "d/control missing Origin: Devuan. Is this a Devuan package?" 1

git pull --ff-only "${devuan_remote}" "suites/$suite"

src=$(dpkg-parsechangelog -S Source)

[ -z "$(rmadison -u https://api.pkginfo.devuan.org/madison -s "$suite" -a source "$src")" ] && echo "WARNING: Source $src not present in Devuan $suite"

debian_suite=$(debian_suite "$suite")
debian_version=$(debian_version "$src" "$debian_suite")
[ "$debian_version" ] || exit_with_msg "Failed to get current version in Debian $debian_suite" 1
devuan_version=$(dpkg-parsechangelog -S Version)

if [ "$(git cat-file -p HEAD | grep ^parent -c)" -gt 1 ] && [ "${debian_version}" = "${devuan_version}" ]
then
    echo Found manually resolved merge. Going to release.
else
    dpkg --compare-versions "$debian_version" gt "$devuan_version"  || \
	exit_with_msg "Devuan src:$src/$devuan_version in suites/$suite already up to date with Debian version $debian_version."

    echo "Updating src:$src $devuan_version to Debian version: $debian_version."

    local_commits=$(git rev-list "$(git log -n 1 --pretty=format:%H -- debian/changelog)"..)
fi

debian_dsc=$(debian_get_dsc "$(debian_dsc_url "$src" "$debian_version")")
debian_git=$(echo "$debian_dsc" | deb822_get Vcs-Git)

if [ -z "$debian_git" ]
then

    debian_branch=debian/master
    debian_tag=debian/$debian_version
    git show-ref --tags "$debian_tag" --quiet || \
	importdsc "$debian_branch" "$(debian_dsc_url "$src" "$debian_version")" || \
	exit_with_msg "If verification has failed install debian-keyring package or set DGET_VERIFY=no in ~/.devscripts." 1

else

    debian_remote=$(git_remote_name "$debian_git")
    [ -z "${debian_remote}" ] && {
	debian_remote=debian
	echo "Debian git remote not found, adding $debian_git as $debian_remote"
	git remote add "$debian_remote" "$debian_git"
    }
    git fetch "$debian_remote"
    debian_branch=$(git remote show "$debian_remote" |  grep 'HEAD branch' | cut -d' ' -f5)
    debian_tag=${1:-$(git tag -l "$debian_remote/$debian_branch" "*$(echo "$debian_version" | tr ':~' '%_')")}
    if [ -z "$debian_tag" ]
    then
	if [ -n "$force" ]
	then
	    common_ancestor=$(git merge-base "suites/$suite" "$debian_branch")
	    debian_branch=$(echo "debian/$debian_version" | tr ':~' '%_')
	    git branch "$debian_branch" "$common_ancestor"
	    importdsc "$debian_branch" "$(debian_dsc_url "$src" "$debian_version")"
	    debian_tag=$debian_branch
	else
	    debian_maintainer="$(echo "$debian_dsc" | deb822_get Maintainer)"
	    echo "Tag matching $debian_version not found in $debian_remote/$debian_branch."
	    echo "Specify tag on command line or"
	    echo "specify -f to import dsc or"
	    exit_with_msg "request '$debian_maintainer' to update repostory at $debian_git." 1
	fi
    fi

    echo "Trying tag $debian_tag."
fi

git show-ref --tags "$debian_tag" --quiet || \
    exit_with_msg "Tag $debian_tag not found" 1

[ "$(git check-attr -a debian/changelog)" = 'debian/changelog: merge: dpkg-mergechangelogs' ] || {
    echo Setting up debian/changelog to use dpkg-mergechangelogs
    echo debian/changelog merge=dpkg-mergechangelogs >> .git/info/attributes
}

git merge ${auto:+--no-edit} "$debian_tag" || {
    echo 'Fixup and commit merge manually'
    [ ! -d debian/po ] || [ -z "$(git status --porcelain -- debian/po/*.po)" ] || {
	[ -z "$(git status --porcelain -- debian/*.templates)" ] || printf  'After resolving merges in debian/*.templates, '
	echo 'd1po-debconf(1) might be useful'
    }
    exit_with_msg "Then re-run $0" 1
}

if ! grep -q '1.0\|native' debian/source/format ; then
    if [ "$(gbp config buildpackage.pristine-tar)" = "True" ]; then
	git branch --force pristine-tar "${debian_remote}/pristine-tar"
	git push "${devuan_remote}" pristine-tar
    else
	git push "${devuan_remote}" "$(expand_gbp_config buildpackage.upstream-tag | sed 's/-[^-]*$//')"
    fi
fi

if [ -n "$(quilt series)" ]
then
    quilt push -a
    quilt pop -a
    rm -r .pc/
fi

devuan_version="${debian_version}devuan1"
dch --newversion "${devuan_version}" "Merge tag $debian_tag."
for sha in $local_commits ; do
    dch "$(git show --pretty=format:%s --no-patch "$sha")"
done
dch --release --no-force-save-on-release ${suite:+--distribution "${suite}"} ${auto:+''}
git commit ${auto:+--no-edit} -m "Version ${devuan_version}." debian/changelog

[ -z "$do_build" ] || \
    gbp buildpackage

[ -z "$do_tag" ] || \
    gbp buildpackage --git-tag-only
