#!/bin/bash -e

# Helper script to develop/debug mini-buildd.
#
# Quickstart:
#  Enter your dev chroot (preferably sid). sudo should configured.
#  $ cd mini-buildd
#  $ ./devel installdeps
#  $ ./devel update

MBD_HOME=~mini-buildd
# m-b-tool shortcut for testing calls
MBD_TOOL="/usr/bin/mini-buildd-tool admin@localhost:8066"

# Use first found mirror from sources as default
_read_mirror()
{
	for s in /etc/apt/sources.list $(ls /etc/apt/sources.list.d/*.list 2>/dev/null | sort); do
		[ -z "${MIRROR}" ] || break
		MIRROR=$(grep "^deb " "${s}" 2>/dev/null | head -n1 | cut -d" " -f2)
	done
	read -p "Mirror: " -e -i "${MIRROR}" MIRROR
}

_head()
{
	printf "\n=> ${*}\n"
}

_check_prog()
{
	local path
	for path in $(printf "${PATH}" | tr ":" " "); do
		local prog="${path}/${1}"
		if [ -x "${prog}" ]; then
			printf "I: Found: ${prog}.\n"
			return 0
		fi
	done
	printf "E: '${1}' not found in path; please install.\n" >&2
	printf "I: You may use './devel installdeps' to install all deps needed.\n" >&2
	exit 1
}

PJPATH="$(readlink -f $(dirname $0))"
PYPATH="${PJPATH}/src"
PYLINTRC="${PJPATH}/.pylintrc"

mbd_installdeps()
{
	sudo apt-get update
	sudo apt-get --no-install-recommends install devscripts equivs

	# Debian package build dependencies; using target-release=*
	# here to always allow highest versions of any sources
	# configured (for example, for backports).
	mk-build-deps --install --root-cmd=sudo --remove --tool="apt-get --no-install-recommends --target-release='*'"

	# Extra tools needed for checks
	sudo apt-get install --no-install-recommends pycodestyle pylint pychecker pyflakes python-apt python-bs4 python-keyrings.alt
	# Extra tools needed vc and package building
	sudo apt-get install --no-install-recommends git git-buildpackage
	# binary package dependecies so we can just dpkg -i for testing
	sudo apt-get install --no-install-recommends --target-release='*' sbuild schroot reprepro debootstrap lintian
}

# grml: The only thing I want here is that my manual test rig can do non-interactive API calls
mbd_pythonkeyringtestconfig()
{
	sudo apt-get install python-keyrings.alt || true   # for the PlainTextKeyring (newer versions only)
	local configDir="$(python -c "import keyring.util.platform_; print(keyring.util.platform_.config_root())")" || true
	[ -n "${configDir}" ] || configDir="${HOME}/.local/share/python_keyring"
	mkdir -p "${configDir}"

	local configFile="${configDir}/keyringrc.cfg"
	local pkVersion=$(dpkg-query --show --showformat='${Version}' python-keyring)
	if dpkg --compare-versions ${pkVersion} gt 7; then
		cat <<EOF >"${configFile}"
[backend]
# stretch (p-k > 8)
default-keyring=keyrings.alt.file.PlaintextKeyring
EOF
	else
		cat <<EOF >"${configFile}"
[backend]
# jessie (p-k 4.0)
default-keyring=keyring.backends.file.PlaintextKeyring
EOF
	fi
	cat "${configFile}"
}

mbd_pyenv()
{
	printf "export PYTHONPATH=\"${PYPATH}\"\n"
	printf "export PYLINTRC=\"${PYLINTRC}\""
}

pyenv()
{
	eval "$(mbd_pyenv)"

	# Be sure we have src/mini_buildd/__init__.py (missing after fresh checkout or brutal git cleans) so it will be recognized as package
	# (this is implicitly always auto-generated by setup.py)
	python ./setup.py --version >/dev/null
}

mbd_pycodestyle()
{
	_check_prog pycodestyle
	(
		pyenv
		pycodestyle --show-source --show-pep8 --config=${PJPATH}/.pycodestyle doc/conf.py setup.py src/mini-buildd src/mini-buildd-tool src/mini_buildd/
	)
}

mbd_pymisc()
{
	local f regex
	for f in src/mini-buildd $(find src/mini_buildd -type f -name "*.py"); do
		for regex in \
			^from\ __future__\ import\ unicode_literals \
			^from\ __future__\ import\ absolute_import \
			^\#\ -\\*-\ coding:\ utf-8\ -\\*-; do
			printf "Checking '${f}' for '${regex}'..."
			grep --quiet "${regex}" "${f}"
			printf "OK.\n"
		done
	done
}

mbd_codespell()
{
	local ups=$(codespell --quiet-level=2 src/mini-buildd src/mini-buildd-tool src/mini_buildd/ doc/)
	if [ -n "${ups}" ]; then
		printf "${ups}\n" >&2
		return 1
	fi
}

mbd_pydoctests()
{
	(
		pyenv
		for m in $(find src/mini_buildd/ -maxdepth 1 -name "*.py"); do
			local module="$(basename $(tr '/' '.' <<< ${m}) '.py' | cut -d. -f2-)"
			printf "=> Doctest on %s (%s)\n" "${m}" "${module}"
			./src/run-doctest "${module}"
		done
		python -m doctest -v src/mini-buildd src/mini-buildd-tool
	)
}

mbd_pyflakes()
{
	_check_prog pyflakes
	(
		pyenv
		pyflakes src/mini-buildd src/mini_buildd/
	)
}

mbd_pylintgeneratedmembers()
{
	# Generate all identifiers with "has no xxx member" error. If
	# needed, manually add those that are _actually_
	# false-positive due to django to -> .pylintrc.
	for o in $(${0} pylint | grep "has no.*member" | cut -d"'" -f4 | sort | uniq); do
		printf "${o},"
	done
	printf "\n"
}

mbd_pylint()
{
	[ "$(lsb_release -s -c)" != "squeeze" ] || { printf "W: Skipping pylint on squeeze\n" >&2; return; }
	_check_prog pylint
	(
		pyenv
		pylint setup.py src/mini-buildd src/mini-buildd-tool src/mini_buildd/
	)
}

mbd_pychecker()
{
	_check_prog pychecker
	(
		pyenv
		pychecker src/mini-buildd src/mini-buildd-tool $(find src/mini_buildd/ -name "*.py")
	)
}

mbd_css()
{
	local class errors=0 ignoreRegex="mbd-action-.*"

	for class in $(grep -h -o "#mbd[[:alnum:]\-]\+" src/mini_buildd/static/css/*.css | sort -r | uniq); do
		if [[ ${class:1} =~ ${ignoreRegex} ]]; then
			printf "Skipping %s.\n" "${class}"
		else
			if ! grep -r -q "${class:1}" src/mini_buildd/templates/; then
				printf "E: CSS class unused: %s:\n" "${class}" >&2
				grep -l "${class}" src/mini_buildd/static/css/*.css
				errors+=1
			fi
		fi
	done
	printf "CSS: %s unused classes found.\n" "${errors}"
	return ${errors}
}

mbd_tidy()
{
	local url
	for url in \
		http://localhost:8066/mini_buildd \
		http://localhost:8066/mini_buildd/api \
		http://localhost:8066/mini_buildd/api?command=getdputconf \
		http://localhost:8066/accounts/login/ \
		; do
		printf "\n=> Testing HTML: ${url}\n"
		wget --output-document=- "${url}" | tidy -output /dev/null
	done
}

mbd_repro()
{
	debrepro
}

mbd_check()
{
	local tests="pycodestyle pymisc pyflakes pylint css pydoctests repro"
	for t in ${tests}; do
		printf "=> mbd_${t}\n"
		mbd_${t}
	done
	printf "\nOK. Tests passed: %s\n" "${tests}"
}

mbd_checklive()
{
	_head "Checking basic src/mini-buildd-tool calls"

	${MBD_TOOL} status
	${MBD_TOOL} getkey
	${MBD_TOOL} getdputconf
	${MBD_TOOL} getsourceslist wheezy

	# Some (live) HTML checks.
	mbd_tidy
}

mbd_sfood()
{
	_check_prog sfood
	(
		pyenv
		sfood --internal-only src/mini_buildd/ | sfood-graph | dot -Tpdf >dependency.pdf
		evince --fullscreen dependency.pdf
	)
}

mbd_doc()
{
	python setup.py build_sphinx --source-dir=build/sphinx
}

mbd_installdjango()
{
	dpkg -s python-django | grep "^Version" || true
	sudo dpkg --install ../django-versions/python-django*${1}*.deb
	dpkg -s python-django | grep "^Version"
}

mbd_service()
{
	if sudo ischroot && [ -d /run/systemd/system ]; then
		# Seems we are in a chroot, and host is running systemd
		# The service will not be started in that case
		# For now, we really want to start the service anyway, so this is still usable in "traditional" chroots
		# (Rather use a container-based environment to test)
		sudo mv /lib/lsb/init-functions.d/40-systemd /lib/lsb/init-functions.d/40-systemd.DISABLED || true
		sudo /etc/init.d/mini-buildd ${1}
		sudo mv /lib/lsb/init-functions.d/40-systemd.DISABLED /lib/lsb/init-functions.d/40-systemd || true
	else
		service mini-buildd "${1}"
	fi
}

mbd_purge()
{
	_head "Purging all mini-buildd packages"
	mbd_service stop || true
	sudo dpkg --purge mini-buildd python-mini-buildd mini-buildd-common

	# Also test mini-buildd's internal sbuild key workaround
	sudo rm -v -f /var/lib/sbuild/apt-keys/*
}

mbd_install()
{
	_head "Checking changelog (must be unchanged)..."
	git diff-index --exit-code HEAD debian/changelog
	trap "git checkout debian/changelog" EXIT

	_head "Building snapshot..."
	gbp dch --snapshot --auto --ignore-branch
	gbp buildpackage --git-ignore-new --git-ignore-branch -us -uc

	_head "Installing snapshot..."
	cat ./devel.debconf.selections | sudo debconf-set-selections --verbose -
	sudo debi
}

mbd_update()
{
	export DEB_BUILD_OPTIONS+=" nocheck"
	mbd_install
	mbd_service restart
	printf "Ok, updated (NO checks).\n"
}

mbd_updatecheck()
{
	mbd_check
	mbd_install
	mbd_service restart
	sleep 10 && mbd_checklive  # Sleeping a while 1st to be sure the network service is ready

	printf "Ok, updated (checked).\n"
}

# python API has nicer support for this.
# Example call: MBD__PACKAGE="mbd-test-cpp" MBD__DIST="squeeze-test-unstable" [MBD__VERSION=1.2.3] ./devel wait4package
mbd_wait4package()
{
	local sleep=30
	printf "\nWaiting for ${MBD__PACKAGE}-${MBD__VERSION} to appear in ${MBD__DIST}:\n"
	while true; do
		# ${MBD_TOOL} show ${MBD__PACKAGE}
		if ${MBD_TOOL} show ${MBD__PACKAGE} 2>/dev/null | grep "^${MBD__DIST}\b.*${MBD__VERSION}"; then
			printf "\nOK, build\n"
			break
		else
			printf "*"
			sleep ${sleep}
		fi
	done
}

mbd_checkall()
{
	mbd_purge
	mbd_updatecheck
	mbd_pythonkeyringtestconfig

	time /usr/share/doc/mini-buildd/examples/auto-setup

	# Migration of test packages
	MBD__PACKAGE="mbd-test-cpp" MBD__DIST="jessie-test-unstable" mbd_wait4package
	${MBD_TOOL} migrate mbd-test-cpp jessie-test-unstable --confirm=migrate
	${MBD_TOOL} migrate mbd-test-cpp jessie-test-testing --confirm=migrate

	# Extra test packages when available
	[ ! -d ../test-packages/ ] || dput --unchecked --no-upload-log mini-buildd-$(hostname) ../test-packages/*.changes
}

sep=" "
for c in $(compgen -A function | grep '^mbd_'); do
	ACTIONS+="${sep}${c:4}"
	sep="|"
done
trap "printf '\nUsage: $(basename "${0}")${ACTIONS}[_arg1_arg2..] \n' >&2" ERR

[ "${*}" ]

for ACTION in ${*}; do
	func=$(printf "${ACTION}" | cut -d_ -f1)
	args=""
	if printf "${ACTION}" | grep -q "_"; then
		args=$(printf "${ACTION}" | cut -d_ -f2- | tr "_" " ")
	fi
	mbd_${func} ${args}
done
