#!/bin/sh

set -e

ME=$(basename $0)

usage () {
	echo "$ME [--ask-upstream-git]"
	echo ""
	echo "Modernizes the packaging of a Debian OpenStack package. Each"
	echo "operation is idempotent (it does nothing when there is nothing"
	echo "to do), writes its own debian/changelog bullet, and does an"
	echo "atomic git commit. The git working tree must be clean."
	echo ""
	echo "Operations:"
	echo " - Update debian/salsa-ci.yml"
	echo " - Update debian/watch to version=4 mode=git"
	echo " - Remove the Priority: field from debian/control"
	echo " - Upgrade the package to debhelper-compat (= ${DEBHELPER_COMPAT})"
	echo " - Update Standards-Version to ${STANDARDS_VERSION}"
	exit 1
}

die () {
	echo "E: ${ME}: ${*}" >/dev/stderr
	exit 1
}

SELF=$(readlink -f "${0}")

# Reads the pkgos configuration file, which holds, among other things,
# the DEBHELPER_COMPAT and STANDARDS_VERSION values used by the
# operations below. The PKGOS_CONF variable overrides its path, which is
# what the unit tests do so that they never depend on the content of
# /etc/pkgos/pkgos.conf.
PKGOS_CONF=${PKGOS_CONF:-/etc/pkgos/pkgos.conf}
if [ -r "${PKGOS_CONF}" ] ; then
	. "${PKGOS_CONF}"
fi
DEBHELPER_COMPAT=${DEBHELPER_COMPAT:-13}
STANDARDS_VERSION=${STANDARDS_VERSION:-4.7.4}

# Prints the first comment line of the named operation function: that
# text is used both as the git commit message and as the debian/changelog
# bullet of the operation.
op_text () {
	awk -v fn="${1}" '
		BEGIN { in_fn = 0 }
		index($0, fn " () {") == 1 { in_fn = 1; next }
		in_fn == 1 && /^[ \t]*# / { sub(/^[ \t]*# /, ""); print; exit }
	' "${SELF}"
}

# Same as op_text, but expands the ${VARIABLES} the text may contain, so
# that operation texts can embed configuration values.
op_msg () {
	eval "echo \"$(op_text "${1}")\""
}

# Writes ${1} as a new bullet in debian/changelog. When the current
# version was already uploaded to unstable (as checked with rmadison),
# the version is first bumped using dch -i, which then carries ${1} as
# the first bullet of the new entry (dch only works non-interactively
# when given a message; with EDITOR=true it would refuse to save).
pkgos_mod_dch () {
	MSG=${1}
	SRC=$(dpkg-parsechangelog -SSource)
	VERS=$(dpkg-parsechangelog -SVersion)
	RMADISON_OUT=$(rmadison "${SRC}" 2>/dev/null) || true
	if [ -n "${RMADISON_OUT}" ] ; then
		if echo "${RMADISON_OUT}" | awk -F"|" '{ gsub(/ /, "", $2); print $2 }' | grep -Fxq "${VERS}" ; then
			if [ "$(dpkg-parsechangelog -SDistribution)" = "UNRELEASED" ] ; then
				sed -i "0,/UNRELEASED/s//aUNRELEASED/" debian/changelog
				dch --maintmaint -i "${MSG}"
				sed -i "0,/aUNRELEASED/s//UNRELEASED/" debian/changelog
			else
				dch --maintmaint -i "${MSG}"
			fi
			return 0
		fi
	fi
	dch --append "${MSG}"
}

# Commits everything under debian/ with ${1} as the commit message.
pkgos_mod_commit () {
	MSG=${1}
	echo "===> ${MSG}"
	git add debian/
	git commit -m "${MSG}" >/dev/null
}

pkgos_mod_salsa_ci () {
	# Update debian/salsa-ci.yml
	MSG=$(op_msg pkgos_mod_salsa_ci)
	[ -n "${MSG}" ] || die "cannot read the text of pkgos_mod_salsa_ci"
	TARGET=$(mktemp "${TMPDIR:-/tmp}/pkgos-modernize-salsa-ci.XXXXXX")
	cat >"${TARGET}" <<'EOF'
---
include:
  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
variables:
  SALSA_CI_DISABLE_BUILD_PACKAGE_I386: 1
EOF
	if [ -f debian/salsa-ci.yml ] && cmp -s debian/salsa-ci.yml "${TARGET}" ; then
		rm -f "${TARGET}"
		return 0
	fi
	cat "${TARGET}" > debian/salsa-ci.yml
	rm -f "${TARGET}"
	pkgos_mod_dch "${MSG}"
	pkgos_mod_commit "${MSG}"
}

pkgos_mod_watch () {
	# Update debian/watch to version=4 mode=git
	MSG=$(op_msg pkgos_mod_watch)
	[ -n "${MSG}" ] || die "cannot read the text of pkgos_mod_watch"
	OPTS_LINE='opts="mode=git,uversionmangle=s/\.0rc/~rc/;s/\.0b1/~b1/;s/\.0b2/~b2/;s/\.0b3/~b3/" \'
	TAG_LINE='refs/tags/(\d[brc\d\.]+)'
	if [ -f debian/watch ] \
		&& grep -q "^version=4$" debian/watch \
		&& grep -qF "${OPTS_LINE}" debian/watch \
		&& grep -qF "${TAG_LINE}" debian/watch ; then
		return 0
	fi
	SRC=$(dpkg-parsechangelog -SSource)
	UPSTREAM_GIT=$(sed -n "s/^UPSTREAM_GIT [?:]*= //p" debian/rules 2>/dev/null | head -1 | sed "s/\.git$//") || true
	if [ -z "${UPSTREAM_GIT}" ] ; then
		if [ "${ASK_UPSTREAM_GIT}" = "yes" ] ; then
			echo -n "Cannot guess upstream git url, please type it: "
			read watch_tag_git_url || die "no upstream git url provided"
			UPSTREAM_GIT=${watch_tag_git_url}
		else
			UPSTREAM_GIT="https://github.com/openstack/${SRC}"
			if ! timeout 30 git ls-remote --exit-code "${UPSTREAM_GIT}" HEAD >/dev/null 2>&1 ; then
				echo "W: ${ME}: cannot verify ${UPSTREAM_GIT} exists; skipping the debian/watch update" >/dev/stderr
				echo "W: ${ME}: (define UPSTREAM_GIT in debian/rules or use --ask-upstream-git)" >/dev/stderr
				return 0
			fi
		fi
	fi
	TARGET=$(mktemp "${TMPDIR:-/tmp}/pkgos-modernize-watch.XXXXXX")
	{
		echo 'version=4'
		echo "${OPTS_LINE}"
		echo "${UPSTREAM_GIT} ${TAG_LINE}"
	} >"${TARGET}"
	if [ -f debian/watch ] && cmp -s debian/watch "${TARGET}" ; then
		rm -f "${TARGET}"
		return 0
	fi
	cat "${TARGET}" > debian/watch
	rm -f "${TARGET}"
	pkgos_mod_dch "${MSG}"
	pkgos_mod_commit "${MSG}"
}

pkgos_mod_control_priority () {
	# Remove the Priority: field from debian/control
	MSG=$(op_msg pkgos_mod_control_priority)
	[ -n "${MSG}" ] || die "cannot read the text of pkgos_mod_control_priority"
	grep -q "^Priority:" debian/control || return 0
	sed -i "/^Priority:/d" debian/control
	pkgos_mod_dch "${MSG}"
	pkgos_mod_commit "${MSG}"
}

pkgos_mod_debhelper_compat () {
	# Upgrade the package to debhelper-compat (= ${DEBHELPER_COMPAT})
	MSG=$(op_msg pkgos_mod_debhelper_compat)
	[ -n "${MSG}" ] || die "cannot read the text of pkgos_mod_debhelper_compat"
	TARGET=$(mktemp "${TMPDIR:-/tmp}/pkgos-modernize-control.XXXXXX")
	cp debian/control "${TARGET}"
	COMPAT_EXISTED=no
	if [ -f debian/compat ] ; then
		COMPAT_EXISTED=yes
		rm -f debian/compat
	fi
	if ! grep -q "debhelper-compat (= ${DEBHELPER_COMPAT})" debian/control ; then
		if grep -q "debhelper-compat (= " debian/control ; then
			sed -i "s/debhelper-compat (= [0-9]\+)/debhelper-compat (= ${DEBHELPER_COMPAT})/" debian/control
		elif grep -q "^ debhelper\( (\)\?" debian/control ; then
			sed -i -e "s/^ debhelper\( ([^)]\+)\)\?,$/ debhelper-compat (= ${DEBHELPER_COMPAT}),/" \
				-e "s/^ debhelper\( ([^)]\+)\)\?$/ debhelper-compat (= ${DEBHELPER_COMPAT}),/" \
				-e "s/^ debhelper\( ([^)]\+)\)\?, / debhelper-compat (= ${DEBHELPER_COMPAT}), /" debian/control
		elif grep -q "^Build-Depends:.*debhelper" debian/control ; then
			sed -i "/^Build-Depends: /s/debhelper\( ([^)]\+)\)\?/debhelper-compat (= ${DEBHELPER_COMPAT})/" debian/control
		elif grep -q "^Build-Depends:" debian/control ; then
			sed -i "0,/^Build-Depends:/s//Build-Depends:\n debhelper-compat (= ${DEBHELPER_COMPAT}),/" debian/control
		fi
	fi
	if cmp -s debian/control "${TARGET}" && [ "${COMPAT_EXISTED}" = "no" ] ; then
		rm -f "${TARGET}"
		return 0
	fi
	rm -f "${TARGET}"
	pkgos_mod_dch "${MSG}"
	pkgos_mod_commit "${MSG}"
}

pkgos_mod_standards_version () {
	# Update Standards-Version to ${STANDARDS_VERSION}
	MSG=$(op_msg pkgos_mod_standards_version)
	[ -n "${MSG}" ] || die "cannot read the text of pkgos_mod_standards_version"
	if [ "$(grep -c "^Standards-Version: ${STANDARDS_VERSION}$" debian/control || true)" -ge 1 ] ; then
		return 0
	fi
	if grep -q "^Standards-Version:" debian/control ; then
		sed -i "s/^Standards-Version: .*/Standards-Version: ${STANDARDS_VERSION}/" debian/control
	else
		sed -i "/^Source: /a Standards-Version: ${STANDARDS_VERSION}" debian/control
	fi
	pkgos_mod_dch "${MSG}"
	pkgos_mod_commit "${MSG}"
}

main () {
	if [ ! -f debian/changelog ] || ! git rev-parse --show-toplevel >/dev/null 2>&1 ; then
		echo "Usage: ${ME} must be run from the top of a package git repository."
		usage
	fi
	[ -z "$(git status --porcelain)" ] || die "the git working tree is not clean"
	command -v git >/dev/null 2>&1 || die "git is missing"
	command -v dch >/dev/null 2>&1 || die "dch is missing (please install devscripts)"
	command -v rmadison >/dev/null 2>&1 || die "rmadison is missing (please install devscripts)"
	cd "$(git rev-parse --show-toplevel)"
	pkgos_mod_salsa_ci
	pkgos_mod_watch
	pkgos_mod_control_priority
	pkgos_mod_debhelper_compat
	pkgos_mod_standards_version
	echo "==> All done."
}

ASK_UPSTREAM_GIT=no
for i in $@ ; do
	case "${1}" in
	"--ask-upstream-git")
		ASK_UPSTREAM_GIT=yes
		shift
	;;
	*)
		usage
	;;
	esac
done
main
