#!/bin/bash

# Copyright (C) 2011-2015 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
# Copyright (C) 2015      by Mihai Moldovan <ionic@ionic.de>
#
# This programme is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This programme is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

export PATH="${HOME}/bin:${PATH}"

GIT_USER="gituser"
GIT_HOSTNAME="git.mydomain.org"

RPMEMAIL="firstname.lastname@mydomain.org"
RPMFULLNAME="Firstname Lastname"
GPG_KEY=""
RPM_DISTS_SUPPORTED="fedora epel opensuse sle"
FEDORA_DISTROS="18,19,20,21,22,rawhide"
EPEL_DISTROS="6,7"
OPENSUSE_DISTROS="12.2,12.3,13.1,13.2"
SLE_DISTROS="11.2,11.3,12.0"
RPM_REPOS_BASE="/var/www/"
RPM_WANT_EXTRA_REPOS="0"
RPM_MOCK_CONFIG_DIR="/etc/mock"
RPM_EXTRA_REPO_MOCK_CONFIG_BASE=""
RPM_EXTRA_REPO_MOCK_CONFIG_FULL_NAME=""


COMPONENT_MAIN="main"
COMPONENT_NIGHTLY="nightly"
COMPONENT_BUNDLES="bundle-release1 bundle-release2"
REPOS_SERVER="packages.mydomain.org"
PACKAGES_WITHOUT_OTHERMIRROR="keyring"
GNUPGHOME="${HOME}/.gnupg"

OPENSUSE_DOWNLOAD_URL="http://download.opensuse.org/distribution/#VERSION#/repo/oss/suse/"
SLE_DOWNLOAD_URL="/srv/mirrors/non-public/sle/#VERSION#/rpms/"

test -z "${1}" && { echo "usage: $(basename "${0}") [<subpath>/]<git-project> {main,main/<codename>,nightly,nightly/<codename>} [<git-checkout>]"; exit 1; }

PREFIX="$(cut -d"-" -f1 <<< "$(basename "${0}")")"
test -f "${HOME}/.buildscripts/${PREFIX}.conf" && . "${HOME}/.buildscripts/${PREFIX}.conf" || { echo "${0} has no valid context prefix..." >&2; exit 1; }

: ${NO_DELAY:="no"}
: ${FORCE_BUILD:="no"}
: ${RPM_BUILD_FOR:="fedora:${FEDORA_DISTROS} epel:${EPEL_DISTROS} opensuse:${OPENSUSE_DISTROS} sle:${SLE_DISTROS}"}

# These parts are not user-serviceable.
TMP_MOCK_CFG_DIR=""
TMP_MOCK_CFG_FILE=""
TEMP_BASE="${HOME}/tmp/"
typeset -ag temp_cleanup
# End of non-user-serviceable part.

set -ex

# Cleans up temporary directories and files.
# RFC SHOULD be called by trap handlers.
cleanup () {
	# We always assume the temporary mock config file is below the temporary config directory.
	if [ -n "${TMP_MOCK_CFG_DIR}" ] && [ -e "${TMP_MOCK_CFG_DIR}" ]; then
		case "${TMP_MOCK_CFG_DIR}" in
			("${TEMP_BASE}"*)	;;
			(*)			echo "Warning: mock temporary config directory is not matching the temporary file base dir ${TEMP_BASE}. Not doing cleanup." >&2
						exit 1
						;;
		esac

		# Take care of the config file(s) first.
		typeset file=""
		for file in "${TMP_MOCK_CFG_DIR}/"*; do
			if [ ! -f "${file}" ]; then
				echo "Warning: mock temporary config file ${file} is not a regular file. Not unlinking." >&2
			else
				# Remove and ignore errors.
				rm -- "${file}" || :
			fi
		done

		# And only later of the directory itself.
		if [ -e "${TMP_MOCK_CFG_DIR}" ]; then
			if [ -d "${TMP_MOCK_CFG_DIR}" ]; then
				rmdir -- "${TMP_MOCK_CFG_DIR}" || echo "Warning: unable to remove mock temporary config directory ${TMP_MOCK_CFG_DIR}. Is it non-empty?" >&2
			else
				echo "Warning: mock temporary config directory ${TMP_MOCK_CFG_DIR} is not actually a directory. Not unlinking." >&2
			fi
		else
			echo "Warning: mock temporary config directory ${TMP_MOCK_CFG_DIR} does not exist." >&2
		fi
	elif [ -n "${TMP_MOCK_CFG_DIR}" ]; then
		echo "Warning: mock temporary config directory ${TMP_MOCK_CFG_DIR} defined but does not exist. Skipping cleanup." >&2
	else
		echo "Warning: mock temporary config directory not defined. Skipping cleanup." >&2
	fi

	# Cleanup the other temporary dirs afterwards.
	typeset temp_dir=""
	for temp_dir in "${temp_cleanup[@]}"; do
		if [ -n "${temp_dir}" ] && [ -d "${temp_dir}" ]; then
			rm -Rf -- "${temp_dir}"
		fi
	done
}

# Run cleanup() automatically.
trap cleanup ERR EXIT SIGTERM SIGINT SIGHUP SIGPIPE SIGALRM SIGUSR1 SIGUSR2

# FIXME: this should really be in a common.(sh) file!
function make_boolean () {
	typeset -l OPTION="${1}"

	case "${OPTION}" in
		("0"|"no"|"false"|"") OPTION="0";;
		(*) OPTION="1";;
	esac

	printf "${OPTION}"

	return 0
}

set_vars() {
	mkdir -p -- "${TEMP_BASE}"
	chmod 2770 "${TEMP_BASE}"

	# first argv is the name of the Git project
	PROJECT_PATH="${1}"
	PROJECT_PATH="${PROJECT_PATH/%.git/}"
	PROJECT="$(basename "${PROJECT_PATH}")"

	# grab repository component area from command line (2nd argv) or guess it
	ARGV2_COMPONENT="$(cut -d"/" -f1 <<< "${2}/")"
	ARGV2_CODENAME="$(cut -d"/" -f2 <<< "${2}/")"
	COMPONENT="${ARGV2_COMPONENT:-${COMPONENT:-$COMPONENT_NIGHTLY}}"
	CODENAMES="${ARGV2_CODENAME:-${CODENAMES}}"
	[ -n "${ARGV2_CODENAME}" ] && FORCE_BUILD="yes" || true
	DATE="${DATE:-$(date +%Y%m%d)}"
	if [ "x${COMPONENT}" = "x${COMPONENT_MAIN}" ]; then
		CHECKOUT="${3:-build-main}"
	elif [ "x${COMPONENT}" = "x${COMPONENT_MAIN}-test" ]; then
		CHECKOUT="${3:-build-main-test}"
		COMPONENT="maintest"
	elif grep -qs "${COMPONENT}" <<< "${COMPONENT_RELEASES}"; then
		CHECKOUT="${3:-build-$COMPONENT}"
	elif [ "x${COMPONENT}" = "x$COMPONENT_NIGHTLY" ]; then
		CHECKOUT="${3:-master}"
	else
		echo "error: no such package component area for this Git project. Aborting..."
		exit 1
	fi
	# the DATE might be given as ,,today'' from the command line
	[ "x${DATE}" = "xtoday" ] && DATE="$(date +%Y%m%d)"

	# setting paths
	PROJECT_DIR="${HOME}/build/${COMPONENT}/${PROJECT}"
	PKGDIST="${HOME}/pkg-dist/${COMPONENT}/${PROJECT}"

	# lock file
	LOCK_FILE="${PROJECT_DIR}/../.${PROJECT}.lock"

	# creating paths
	mkdir -p -- "${PROJECT_DIR}"
	mkdir -p -- "${PKGDIST}"

	return 0
}

# Check that mock version is at least the given version number.
# Returns 0 if the mock version is greater or equal the specified input,
# 1 otherwise.
check_mock_version_atleast () {
	typeset MAJOR="${1:?"Error: no major version passed to ${FUNCNAME}()."}"
	typeset MINOR="${2:?"Error: no minor version passed to ${FUNCNAME}()."}"
	typeset PATCH="${3:?"Error: no patch version passed to ${FUNCNAME}()."}"

	# Check input parameters for sanity.
	typeset SANITY_CHECK_MAJOR="$(sed -e 's/^\([0-9][0-9]*\)$//' <<< "${MAJOR}")"
	typeset SANITY_CHECK_MINOR="$(sed -e 's/^\([0-9][0-9]*\)$//' <<< "${MINOR}")"
	typeset SANITY_CHECK_PATCH="$(sed -e 's/^\([0-9][0-9]*\)$//' <<< "${PATCH}")"

	if [ -n "${SANITY_CHECK_MAJOR}" ] || [ -n "${SANITY_CHECK_MINOR}" ] || [ -n "${SANITY_CHECK_PATCH}" ]; then
		echo "Error: input parameters of ${FUNCNAME}() are not pure integers and failed sanity check." >&2
		exit 1
	fi

	typeset MOCK_VER="$(mock --version)"

	# Sanitize ${MOCK_VER}:
	# Only take the first line into account.
	MOCK_VER="$(head -n 1 <<< "${MOCK_VER}")"

	# Only accept a string that has number.number.number somewhere.
	MOCK_VER="$(grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' <<< "${MOCK_VER}")"

	if [ -z "${MOCK_VER}" ]; then
		echo "Error: the reported mock version can not be handled by ${FUNCNAME}()." >&2
		exit 1
	fi

	# The reason for this weird [0-9][0-9]* construct is that POSIX BRE does only specify *
	# as a special character. POSIX ERE supports + as a special character, but sed is
	# specified by POSIX to only support BRE. GNU sed supports a \+ special character in
	# POSIX BRE standard mode, but this is a GNU extension.
	typeset MOCK_VER_MAJOR="$(sed -e 's/^\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*/\1/' <<< "${MOCK_VER}")"
	typeset MOCK_VER_MINOR="$(sed -e 's/^[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*/\1/' <<< "${MOCK_VER}")"
	typeset MOCK_VER_PATCH="$(sed -e 's/^[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\)/\1/' <<< "${MOCK_VER}")"

	if [ -z "${MOCK_VER_MAJOR}" ] || [ -z "${MOCK_VER_MINOR}" ] || [ -z "${MOCK_VER_PATCH}" ]; then
		echo "Error: unable to parse mock version in ${FUNCNAME}()." >&2
		exit 1
	else
		typeset ret="1"
		if [ "${MOCK_VER_MAJOR}" -gt "${MAJOR}" ]; then
			ret="0"
		elif [ "${MOCK_VER_MAJOR}" -eq "${MAJOR}" ]; then
			if [ "${MOCK_VER_MINOR}" -gt "${MINOR}" ]; then
				ret="0"
			elif [ "${MOCK_VER_MINOR}" -eq "${MINOR}" ]; then
				if [ "${MOCK_VER_PATCH}" -gt "${PATCH}" ] || \
				   [ "${MOCK_VER_PATCH}" -eq "${PATCH}" ]; then
					ret="0"
				fi
			fi
		fi

		return "${ret}"
	fi
}

# Repeats an input string.
# Returns the repeated input string.
repeat_str () { # INPUT COUNT
	typeset INPUT="${1:?"Error: no input string passed to ${FUNCNAME}()."}"
	typeset COUNT="${2:?"Error: no count passed to ${FUNCNAME}()."}"

	typeset ret=""
	typeset -i i=0
	while [ "${i}" -lt "${COUNT}" ]; do
		ret="${ret}$(printf "${INPUT}")"
		i=$(($i + 1))
	done

	printf "${ret}"

	return 0
}

# Creates a custom mock config file given a base config (taken from
# /etc/mock/), the build component and the target specification ("full" or "base").
# Base refers to adding x2go-extras only, while full means also
# adding the full X2Go upstream repository with all published files.
#
# Calling this function in a subshell is an error, as it NEEDS to modify
# variables in global scope!
create_mock_config () { # MOCK_BASE CUSTOM_REPO COMPONENT TARGET
	typeset MOCK_BASE="${1:?"Error: no mock base config passed to ${FUNCNAME}()."}"
	typeset COMPONENT="${2:?"Error: no component (X2Go release group) passed to ${FUNCNAME}()."}"
	typeset TARGET="${3:?"Error: no target (full or base) passed to ${FUNCNAME}()."}"

	# Check argument sanity.

	# Append .cfg is not already specified.
	typeset TMP_REGEX='^.*\.cfg$'
	if [[ ! "${MOCK_BASE}" =~ ${TMP_REGEX} ]]; then
		MOCK_BASE="${MOCK_BASE}.cfg"
	fi

	typeset DISTRO=""
	typeset VERSION=""
			   # distribution -   version    - arch
	TMP_REGEX='^([[:alpha:]]+)-([[:alnum:]_]+)-[[:alnum:]_]+\.cfg$'
	if [[ ! "${MOCK_BASE}" =~ ${TMP_REGEX} ]]; then
		echo "Error: MOCK_BASE parameter not well formed. Must be: 'distro-version-arch.cfg'." >&2
		exit 1
	else
		DISTRO="${BASH_REMATCH[1]}"
		VERSION="${BASH_REMATCH[2]}"
	fi

	typeset CUSTOM_REPO="${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-${DISTRO}.repo"

	typeset CFG_FILE=""
	TMP_REGEX='/'
	for CFG_FILE in MOCK_BASE CUSTOM_REPO; do
		# Must be plain file.
		if [[ "${!CFG_FILE}" =~ ${TMP_REGEX} ]]; then
			echo "Error: (implicit) ${CFG_FILE} parameter must not be a path but a simple file name." >&2
			exit 1
		fi

		# Must exist and be readable.
		if [ ! -f "${RPM_MOCK_CONFIG_DIR}/${!CFG_FILE}" ] || [ ! -r "${RPM_MOCK_CONFIG_DIR}/${!CFG_FILE}" ]; then
			echo "Error: ${CFG_FILE} parameter must exist, be a regular file and readable." >&2
			exit 1
		fi
	done

	# Rename "main" to "release" and "heuler" to "nightly".
	if [ "${COMPONENT}" = "main" ]; then
		COMPONENT="release"
	elif [ "${COMPONENT}" = "heuler" ]; then
		COMPONENT="nightly"
	fi

	# Note: there is no way to check for the component's validity,
	# as LTS releases have "random" names assigned to them.

	if [ "${TARGET}" != "base" ] && [ "${TARGET}" != "full" ]; then
		echo "Error: TARGET parameter must be either full or base." >&2
		exit 1
	fi

	# Create temporary directory for our soon-to-be temporary mock config file,
	# if it does not already exist.
	if [ -z "${TMP_MOCK_CFG_DIR}" ]; then
		TMP_MOCK_CFG_DIR="$(mktemp -d --tmpdir="${TEMP_BASE}" "${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-mock-$(repeat_str "X" "24")")"
		if [ "$?" -ne "0" ]; then
			echo "Error: creating mock temporary config directory failed. Aborting." >&2
			exit 1
		fi
	fi

	TMP_MOCK_CFG_FILE="$(mktemp --tmpdir="${TEMP_BASE}" "$(basename "${TMP_MOCK_CFG_DIR}")/${MOCK_BASE%.cfg}-${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-${COMPONENT}-${TARGET}.$(repeat_str "X" "24").cfg")"
	if [ "$?" -ne "0" ]; then
		echo "Error: creating mock temporary config file failed. Aborting." >&2
		exit 1
	fi

	# Save old input field separator value and set it to newline only.
	typeset OLDIFS="${IFS}"
	IFS="$(printf '\n')"

	# Fetch the requested lines from ${CUSTOM_REPO} and store them in arrays.
	typeset -a extra_repo
	typeset -a full_repo
	typeset -i FETCH_EXTRA_SECTION=0
	typeset -i FETCH_FULL_SECTION=0
	typeset TMP_REGEX_EXTRA="^[[:space:]]*\[[[:space:]]*${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-extras-${DISTRO}[[:space:]]*\][[:space:]]*\$"
	typeset TMP_REGEX_FULL="^[[:space:]]*\[[[:space:]]*${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-${COMPONENT}-${DISTRO}[[:space:]]*\][[:space:]]*\$"
	typeset TMP_REGEX_OTHER='^[[:space:]]*\[.*\][[:space:]]*$'

	typeset line=""
	while read line; do
		if [[ "${line}" =~ ${TMP_REGEX_EXTRA} ]]; then
			FETCH_EXTRA_SECTION=1
			FETCH_FULL_SECTION=0
		elif [[ "${line}" =~ ${TMP_REGEX_FULL} ]]; then
			FETCH_FULL_SECTION=1
			FETCH_EXTRA_SECTION=0
		elif [[ "${line}" =~ ${TMP_REGEX_OTHER} ]]; then
			FETCH_FULL_SECTION=0
			FETCH_EXTRA_SECTION=0
		fi

		# Change some values like $releasever or gpgcheck.
		if [ "${FETCH_EXTRA_SECTION}" -eq "1" ] || [ "${FETCH_FULL_SECTION}" -eq "1" ]; then
			TMP_REGEX='^[[:space:]]*gpgcheck[[:space:]]*=[[:space:]]*1[[:space:]]*$'
			if [[ "${line}" =~ ${TMP_REGEX} ]]; then
				line="${line/1/0}"
			fi

			TMP_REGEX='^[[:space:]]*enabled[[:space:]]*=[[:space:]]*0[[:space:]]*$'
			if [[ "${line}" =~ ${TMP_REGEX} ]]; then
				line="${line/0/1}"
			fi

			case "${line}" in
				(*'$releasever'*)	line="${line/'$releasever'/${VERSION}}"
							;;
				(*)			;;
			esac
		fi

		if [ "${FETCH_EXTRA_SECTION}" -eq "1" ]; then
			extra_repo+=("${line}")
		elif [ "${FETCH_FULL_SECTION}" -eq "1" ]; then
			full_repo+=("${line}")
		fi
	done < "/etc/mock/${CUSTOM_REPO}"

	typeset -i REPO_START=0
	TMP_REGEX='^[[:space:]]*config_opts\['"'"'yum\.conf'"'"'\][[:space:]]*=[[:space:]]*"""[[:space:]]*$'
	typeset TMP_REGEX_END='^[[:space:]]*"""[[:space:]]*$'

	while read line; do
		if [[ "${line}" =~ ${TMP_REGEX} ]]; then
			REPO_START=1

			# Pass-through.
			echo "${line}" >> "${TMP_MOCK_CFG_FILE}"
		elif [ "${REPO_START}" -eq "1" ] && [[ "${line}" =~ ${TMP_REGEX_END} ]]; then
			# Time to insert whatever is required.
			printf '\n' >> "${TMP_MOCK_CFG_FILE}"

			# Extras repo.
			typeset -i i=0
			for ((i = 0; i < ${#extra_repo[@]}; ++i)); do
				echo "${extra_repo[${i}]}" >> "${TMP_MOCK_CFG_FILE}"
			done

			# Full repo if required.
			if [ "${TARGET}" = "full" ]; then
				printf '\n' >> "${TMP_MOCK_CFG_FILE}"

				typeset -i i=0
				for ((i = 0; i < ${#full_repo[@]}; ++i)); do
					echo "${full_repo[${i}]}" >> "${TMP_MOCK_CFG_FILE}"
				done
			fi

			echo "${line}" >> "${TMP_MOCK_CFG_FILE}"
		elif [ "${REPO_START}" -eq "0" ] && [[ "${line}" =~ ${TMP_REGEX_END} ]]; then
			echo "Error: Parsing mock base config file failed: unexpected end of yum configuration, no start found." >&2
			exit 1
		else
			# Pass-through.
			echo "${line}" >> "${TMP_MOCK_CFG_FILE}"
		fi
	done < "/etc/mock/${MOCK_BASE}"

	# Reset input field separator to original value.
	IFS="${OLDIFS}"

	return 0
}

# Fetches extras repositories, if requested.
# Takes the build system type (suse or redhat -- implicitly means OBS or mock),
# the distribution, its version (either a real number or a codename),
# the build component (X2Go release group), the package, the architecture
# and an optional boolean value that determines whether to add any additional
# repositories at all.
#
# Edits either OTHERMIRROR for type == suse or MOCK_CHROOT_CONFIG for type == redhat.
# It is an error to execute this function in a subshell, as it MUST edit global variables.
get_extra_repository () {
	typeset TYPE="${1:?"Error: no type passed to ${FUNCNAME}()."}"
	typeset DIST="${2:?"Error: no distribution passed to ${FUNCNAME}()."}"
	typeset CODENAME="${3:?"Error: no codename (distro 'version') passed to ${FUNCNAME}()."}"
	typeset COMPONENT="${4:?"Error: no component (X2Go release group) passed to ${FUNCNAME}()."}"
	typeset PACKAGE="${5:?"Error: no package passed to ${FUNCAME}()."}"
	typeset ARCH="${6:?"Error: no architecture passed to ${FUNCNAME}()."}"
	typeset WANT_EXTRA="$(make_boolean "${7}")"

	# Note: we always add the extras repo, because that's defined as "packages missing from the main repository",
	# unless explicitly overridden via ${WANT_EXTRA}.
	case "${TYPE}" in
		"suse")
			# FIXME: make this package repository consistent with our main ones.
			OTHERMIRROR=""
			if [ "${WANT_EXTRA}" -eq "1" ]; then
				OTHERMIRROR="--repo http://${REPOS_SERVER}/${DIST}/${CODENAME}/extras"
				if [ -z "${PACKAGE_WITHOUT_OTHERMIRROR}" ] || [ "${PACKAGE_WITHOUT_OTHERMIRROR}" != "${PACKAGE}" ]; then
					OTHERMIRROR="${OTHERMIRROR} --repo http://${REPOS_SERVER}/${DIST}/${CODENAME}/${COMPONENT}/${ARCH}"
				fi
			fi
			;;
		"redhat")
			# Always use -r.
			MOCK_CHROOT_CONFIG="-r "

			# Itsy-bitsy problem here: mock versions prior to 1.2.0 are buggy in the sense that
			# they *always* prepend /etc/mock/ and append .cfg to any chroot file specified
			# via -r, even if the argument is an absolute path.
			# We have to work around that by specifying ../..//PATH/TO/MOCK-CONFIG and leaving
			# out the ".cfg" part.
			# Find out if we're using a buggy version.
			check_mock_version_atleast "1" "2" "0" && typeset MOCK_BUGGY="0" || typeset MOCK_BUGGY="1"

			[ "${MOCK_BUGGY}" -eq "1" ] && MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}../../"

			if [ "${WANT_EXTRA}" -eq "1" ]; then
				typeset WANT="base"
				if [ -z "${PACKAGE_WITHOUT_OTHERMIRROR}" ] || [ "${PACKAGE_WITHOUT_OTHERMIRROR}" != "${PACKAGE}" ]; then
					WANT="full"
				fi

				create_mock_config "${DIST}-${CODENAME}-${ARCH}" "${COMPONENT}" "${WANT}"

				# Remove the .cfg extension again... and maybe add it back later.
				MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}${TMP_MOCK_CFG_FILE%.cfg}"

			else
				MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}${RPM_MOCK_CONFIG_DIR}/${DIST}-${CODENAME}-${ARCH}"
			fi

			# Add .cfg extension.
			[ "${MOCK_BUGGY}" -eq "0" ] && MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}.cfg"
			;;
		*)
			echo "Error: unknown type passed to ${FUNCNAME}()" >&2
			echo "Valid values: suse, redhat." >&2
			exit 1
			;;
	esac

	return 0
}

prepare_workspace() {
	# make sure our local working copy is up to date...
	if [ -d "${PROJECT_DIR}/.git" ]; then
		cd "${PROJECT_DIR}" && git reset --hard
		git checkout --force "${CHECKOUT}" || git checkout --force -b "${CHECKOUT}"
		git pull origin "${CHECKOUT}"
		git fetch origin upstream:upstream || true
		git fetch origin pristine-tar:pristine-tar || true
		# and again, get the ${CHECKOUT} refspec in pure state
		git reset --hard
		git clean -df
	else
		cd "$(dirname "${PROJECT_DIR}")"
		git clone "git://${GIT_HOSTNAME}/${PROJECT_PATH}.git"
		cd "${PROJECT}"
		git checkout --force "${CHECKOUT}" || git checkout --force -b "${CHECKOUT}"
		git fetch origin upstream:upstream
		git fetch origin pristine-tar:pristine-tar || true
		git clean -df
	fi

	GIT_OBJECT_ID="$(git rev-parse --verify HEAD)"
	cd "${PROJECT_DIR}"

	if [ "x${ARGV2_CODENAME}" != "x" ]; then
		if grep -qs "${ARGV2_CODENAME}" <<< "${FEDORA_DISTROS}"; then
			RPM_BUILD_FOR="fedora:${ARGV2_CODENAME}"
		elif grep -qs "${ARGV2_CODENAME}" <<< "${EPEL_DISTROS}"; then
			RPM_BUILD_FOR="epel:${ARGV2_CODENAME}"
		fi
	fi
	return 0
}

# Cleans up the pkgdist directory used for building packages.
# Does not take parameters.
# Does not "return" anything.
clear_pkgdist() {
	# Do NOT spawn a subshell here. Functions like get_extra_repository() need to
	# change global variables in the main process.
	typeset -a rpm_build_for_arr
	typeset OLDIFS="${IFS}"
	IFS=" "
	read -a rpm_build_for_arr <<< "${RPM_BUILD_FOR}"
	IFS="${OLDIFS}"

	typeset line=""
	for line in "${rpm_build_for_arr[@]}"; do
		l_DIST="$(cut -d":" -f1 <<< "${line/: /:}" | tr [:upper:] [:lower:])"
		l_CODENAMES="${CODENAMES:-$(cut -d":" -f2- <<< "${line/: /:}" | sed -e 's/,/ /g' | tr [:upper:] [:lower:])}"
		grep -qs "${l_DIST}" <<< "${RPM_DISTS_SUPPORTED}" && {
			for l_CODENAME in ${l_CODENAMES}; do

				test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break

				# Yes, "SRPM" is technically not an architecture.
				for l_ARCH in x86_64 i386 SRPM; do
					if [ "x${SKIP_ARCH}" != "x${l_ARCH}" ]; then
						mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}"
						rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/"*
						rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SRPMS/${PROJECT}-"*.src.rpm
						rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*.rpm
					fi
				done
			done
		}
	done
	return 0
}

build_packages() {
	# Do NOT spawn a subshell here. Functions like get_extra_repository() need to
	# change global variables in the main process.
	typeset -a rpm_build_for_arr
	typeset OLDIFS="${IFS}"
	IFS=" "
	read -a rpm_build_for_arr <<< "${RPM_BUILD_FOR}"
	IFS="${OLDIFS}"

	typeset line=""
	for line in "${rpm_build_for_arr[@]}"; do
		l_DIST="$(cut -d":" -f1 <<< "${line/: /:}" | tr [:upper:] [:lower:])"
		l_CODENAMES="${CODENAMES:-$(cut -d":" -f2- <<< "${line/: /:}" | sed -e 's/,/ /g' | tr [:upper:] [:lower:])}"
		grep -qs "${l_DIST}" <<< "${RPM_DISTS_SUPPORTED}" && {
			for l_CODENAME in ${l_CODENAMES}; do

				test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break

				# FIXME: Builds currently break without this. This should really be merged/transformed into an arch loop.
				typeset l_ARCH=""

				# create rpmbuild subdirectories
				mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES"

				# tar upstream sources from our Git clone
				TEMP_DIR="$(mktemp -d --tmpdir="${TEMP_BASE}")"
				temp_cleanup+=("${TEMP_DIR}")
				mkdir -p -- "${TEMP_DIR}/${PROJECT}"
				chmod -Rf -- 2770 "${TEMP_DIR}"

				cd "$PROJECT_DIR"
				git clone "${PROJECT_DIR}" "${TEMP_DIR}/${PROJECT}/"
				cd "${TEMP_DIR}"
				GITREV="$(cd "${PROJECT}" && gitrevno && cd - 1>/dev/null)"

				# create git changelog immediately prior to building the SRPM package
				pushd "${PROJECT}" >/dev/null 2>&1 && \
				git --no-pager log --since "2 years ago" --format="%ai %aN (%h) %n%n%x09*%w(68,0,10) %s%d%n" > ChangeLog.gitlog && \
				popd >/dev/null 2>&1

				rm -Rf -- "${PROJECT}/.git"
				mv -- "${PROJECT}/${PROJECT}.spec" .

				UPSTREAM_VERSION="$(grep -E -- "^Version:.*" "${PROJECT}.spec" | awk '{ print $2 }')"
				PKG_RELEASE="$(grep -E -- "^Release:.*" "${PROJECT}.spec" | awk '{ print $2 }')"
				PKG_SRCRELEASE="$(sed -e 's/%{?dist}//' <<< "${PKG_RELEASE}")"

				IS_NOARCH="$(grep -qsE -- "^BuildArch:.*noarch\$" "${PROJECT}.spec" && echo "yes" || echo "no")"

				if [ "${COMPONENT}" = "${COMPONENT_NIGHTLY}" ]; then
					IS_RELEASE="0"
				else
					IS_RELEASE="1"
				fi
				sed -i "${PROJECT}.spec" -e "s/%{?dist}/.${IS_RELEASE}.git${DATE}.${GITREV}.${COMPONENT}%{?dist}/"

				# apply patches from debian/patches/* so that they end up in the tarball
				# ... esp. relevant for NX (redistributed)
				if [ -f "${PROJECT}/debian/patches/series" ]; then
					( cd "${PROJECT}" && QUILT_PATCHES=debian/patches quilt push -a && rm -Rf -- .pc/; )
				fi

				grep -E "^Source[1-9]+:.*" "${PROJECT}.spec" | sed "s/%{name}/${PROJECT}/" | awk '{ print $2 }' | while read source_file; do
					find "${PROJECT}/rpm/${source_file}" -maxdepth 0 1> /dev/null && cp -- "${PROJECT}/rpm/${source_file}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/" && continue
					find "${PROJECT}/${source_file}" -maxdepth 0 1> /dev/null && cp -- "${PROJECT}/${source_file}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/"
				done

				mv -- "${TEMP_DIR}/${PROJECT}" "${TEMP_DIR}/${PROJECT}-${UPSTREAM_VERSION}"
				tar -czf "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}-${UPSTREAM_VERSION}.tar.gz" "${PROJECT}-${UPSTREAM_VERSION}"

				cp -- "${PROJECT}.spec" "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES"

				if [ "x${l_DIST}" = "xfedora" ] || [ "x${l_DIST}" = "xepel" ]; then
					while [ -d ~mock/"${l_DIST}-${l_CODENAME}-x86_64" ]; do
						echo "Waiting for some other build to finish..."
						sleep 30
					done
					rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log"

					# Obtain packages from our RPM repository.
					get_extra_repository "redhat" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "x86_64" "${RPM_WANT_EXTRA_REPOS}"
					if mock --buildsrpm \
					          ${MOCK_CHROOT_CONFIG} \
					          --resultdir="${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS" \
					          --spec "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}.spec" \
					          --sources "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/"; then
						cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS/build.log"
						rm -Rf -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS/build.log"
					else
						cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS/build.log"
						exit 1
					fi
				fi

				# clean up the Git clone from the temp folder
				cd && rm -Rf -- "${TEMP_DIR}/${PROJECT}"

				# modify changelog for this build
				### TODO: add changelog entry for this automatic build

				mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/"{x86_64,i386,SRPM}

				if [ "x${SKIP_ARCH}" != "xx86_64" ] || [ "${IS_NOARCH}" = "yes" ]; then
					if [ "x${l_DIST}" = "xopensuse" ] || [ "x${l_DIST}" = "xsle" ]; then
						BUILD_RESULT="/home/abuild/rpmbuild/"
						if [ "x${l_DIST}" = "xopensuse" ]; then
							DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${OPENSUSE_DOWNLOAD_URL}")"
						elif [ "x${l_DIST}" = "xsle" ]; then
							DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${SLE_DOWNLOAD_URL}")"
							if [ "${l_CODENAME}" = "11.2" ] || [ "${l_CODENAME}" = "11.3" ]; then
								BUILD_RESULT="/usr/src/packages"
							fi
						fi
						while ps ax | grep -E "build.*/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/" | grep "sudo obs"; do
							echo "Waiting for some other build to finish..."
							sleep 30
						done

						# Obtain packages from our RPM repository.
						get_extra_repository "suse" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "x86_64" "${RPM_WANT_EXTRA_REPOS}"
						if sudo obs-build \
						             --nosignature \
						             ${OTHERMIRROR} \
						             --repo "${DOWNLOAD_URL}" \
						             --root "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/" \
						             --clean \
						             "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}.spec"; then
							mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"

							# -r parameter to read: Backslashes may NOT escape any characters!
							# -d '': specifies the delimiter to be used - as '' resolves to an empty string followed
							#        by a NUL character, the delimiter is set to this very NUL (\000) character.
							find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/${BUILD_RESULT}/RPMS/" -type f \( -iname '*.rpm' -and -not -iname '*.src.rpm' \) -print0 | while read -r -d '' rpmfile; do
								cp "${rpmfile}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"
							done
							rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"*.rpm

							# also copy and sign source RPM's
							# For information on why this weird -print0 | read -r -d '' construction works,
							# refer to the first instance of this in this script.
							find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/${BUILD_RESULT}/SRPMS/" -type f -iname '*.rpm' -print0 | while read -r -d '' rpmfile; do
								cp "${rpmfile}" "$PKGDIST/${l_DIST}/${l_CODENAME}/SRPM/"
							done
							rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm
						else
							exit 1
						fi
					else
						rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log"
						while [ -d ~mock/"${l_DIST}-${l_CODENAME}-x86_64" ]; do
							echo "Waiting for some other build to finish..."
							sleep 30
						done

						# Obtain packages from our RPM repository.
						get_extra_repository "redhat" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "x86_64" "${RPM_WANT_EXTRA_REPOS}"

						# For information on why this weird -print0 | read -r -d '' construction works,
						# refer to the first instance of this in this script.
						find "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SRPMS/" -type 'f' -iname "${PROJECT}-${UPSTREAM_VERSION}-${PKG_SRCRELEASE}.${IS_RELEASE}.git${DATE}.${GITREV}.${COMPONENT}.*.src.rpm" -print0 | while read -r -d '' srpm; do
							if mock ${MOCK_CHROOT_CONFIG} --resultdir="${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64" "${srpm}"; then
								# copy and later sign source RPM
								cp "${srpm}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"

								# Clean up source RPM files. We copy them manually.
								find "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64" -type 'f' -iname '*.src.rpm' -exec rm -f -- '{}' \;

								if [ "${l_DIST}" = "epel" ] && [ "${l_CODENAME}" = "5" ]; then
									# References:
									# /usr/lib/rpm/macros
									# http://adminotes.blogspot.fr/2011/12/centos-6-rpm-sign-problem-v4-signatures.html
									RPMMACRO_V3SIGN="%__gpg_sign_cmd %{__gpg} /usr/bin/gpg --force-v3-sigs --digest-algo=sha1 --batch --no-verbose --no-armor --passphrase-fd 3 --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}"
									rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"*.rpm
									rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm
								else
									rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"*.rpm
									rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm
								fi
								cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log"
							else
								cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log"
								exit 1
							fi
						done
					fi
				fi
				if [ "x${SKIP_ARCH}" != "xi386" ] && [ "${IS_NOARCH}" != "yes" ]; then
					if [ "x${l_DIST}" = "xopensuse" ] || [ "x${l_DIST}" = "xsle" ]; then
						BUILD_RESULT="/home/abuild/rpmbuild/"
						if [ "x$l_DIST" = "xopensuse" ]; then
							DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${OPENSUSE_DOWNLOAD_URL}")"
						elif [ "x$l_DIST" = "xsle" ]; then
							DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${SLE_DOWNLOAD_URL}")"
							if [ "${l_CODENAME}" = "11.2" ] || [ "${l_CODENAME}" = "11.3" ]; then
								BUILD_RESULT="/usr/src/packages"
							fi
						fi
						while ps ax | grep -E "build.*/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/" | grep "sudo obs"; do
							echo "Waiting for some other build to finish..."
							sleep 30
						done

						# Obtain packages from our RPM repository.
						get_extra_repository "suse" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "i386" "${RPM_WANT_EXTRA_REPOS}"
						if linux32 sudo obs-build \
						                    --nosignature \
						                    ${OTHERMIRROR} \
						                    --repo "${DOWNLOAD_URL}" \
						                    --root "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/" \
						                    --clean \
						                    "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}.spec"; then
							mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"

							# For information on why this weird -print0 | read -r -d '' construction works,
							# refer to the first instance of this in this script.
							find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/${BUILD_RESULT}/RPMS/" -type 'f' \( -iname '*.rpm' -and -not -iname '*.src.rpm' \) -print0 | while read -r -d '' rpmfile; do
								cp "${rpmfile}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"
							done
							rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm

							# copy and later sign source RPM's, if needed (that is, not already generated by x86_64/noarch code above)
							SEARCH_SRPM="$(find "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM" -type 'f' -iname "*.src.rpm" -print)"
							if [ -z "${SEARCH_SRPM}" ]; then
								# For information on why this weird -print0 | read -r -d '' construction works,
								# refer to the first instance of this in this script.
								find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/${BUILD_RESULT}/SRPMS/" -type 'f' -iname '*.src.rpm' -print0 | while read -r -d '' rpmfile; do
									cp "${rpmfile}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"
								done
								rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm
							fi
						else
							exit 1
						fi
					else
						while [ -d ~mock/"${l_DIST}-${l_CODENAME}-i386" ]; do
							echo "Waiting for some other build to finish..."
							sleep 30
						done
						rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/build.log"

						# Obtain packages from our RPM repository.
						get_extra_repository "redhat" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "i386" "${RPM_WANT_EXTRA_REPOS}"

						# For information on why this weird -print0 | read -r -d '' construction works,
						# refer to the first instance of this in this script.
						find "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SRPMS/" -type 'f' -iname "${PROJECT}-${UPSTREAM_VERSION}-${PKG_SRCRELEASE}.${IS_RELEASE}.git${DATE}.${GITREV}.${COMPONENT}.*.src.rpm" -print0 | while read -r -d '' srpm; do
							if nice mock ${MOCK_CHROOT_CONFIG} --resultdir="${PKGDIST}/${l_DIST}/${l_CODENAME}/i386" "${srpm}"; then
								# only copy and sign source RPM if necessary
								SIGN_SRPM="0"
								if [ ! -e "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/$(basename "${srpm}")" ]; then
									cp "${srpm}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"
									SIGN_SRPM="1"
								fi

								# Clean up source RPM files. We copy them manually.
								find "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386" -type 'f' -iname '*.src.rpm' -exec rm -f -- '{}' \;

								if [ "${l_DIST}" = "epel" ] && [ "${l_CODENAME}" = "5" ]; then
									RPMMACRO_V3SIGN="%__gpg_sign_cmd /usr/bin/gpg --force-v3-sigs --digest-algo=sha1 --batch --no-verbose --no-armor --passphrase-fd 3 --no-secmem-warning -u \"%_gpg_name\" -sbo %{__signature_filename} %{__plaintext_filename}"
									rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm
									[ "x${SIGN_SRPM}" = "x1" ] && rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm
								else
									rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm
									[ "x$SIGN_SRPM" = "x1" ] && rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm
								fi
								cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/build.log"
							else
								cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/build.log"
								exit 1
							fi
						done
					fi
				fi
			done
		}
	done
	return 0
}

# Uploads the build packages.
# Has no parameters.
# Does not "return" any value.
upload_packages() {
	# Do NOT spawn a subshell here. Functions like get_extra_repository() need to
	# change global variables in the main process.
	typeset -a rpm_build_for_arr
	typeset OLDIFS="${IFS}"
	IFS=" "
	read -a rpm_build_for_arr <<< "${RPM_BUILD_FOR}"
	IFS="${OLDIFS}"

	typeset line=""
	for line in "${rpm_build_for_arr[@]}"; do
		l_DIST="$(cut -d":" -f1 <<< "${line/: /:}" | tr [:upper:] [:lower:])"
		l_CODENAMES="${CODENAMES:-$(cut -d":" -f2- <<< "${line/: /:}" | sed -e 's/,/ /g' | tr [:upper:] [:lower:])}"
		for l_CODENAME in ${l_CODENAMES}; do

			test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break

			# Yes, "SRPM" is technically not an architecture.
			for l_ARCH in x86_64 i386 SRPM; do
				if [ "x${SKIP_ARCH}" != "x${l_ARCH}" ]; then
					# create remote directories in archive
					0</dev/null ssh "${REPOS_SERVER}" "mkdir -p -- ${RPM_REPOS_BASE}/${l_DIST}/${l_CODENAME}/${COMPONENT}/${l_ARCH}/rpms/${PROJECT}"
					0</dev/null ssh "${REPOS_SERVER}" "mkdir -p -- ${RPM_REPOS_BASE}/${l_DIST}/${l_CODENAME}/${COMPONENT}/${l_ARCH}/repodata"

					# remove rpm packages of the same name (pattern)
					0</dev/null ssh "${REPOS_SERVER}" "rm -f -- ${RPM_REPOS_BASE}/${l_DIST}/${l_CODENAME}/${COMPONENT}/${l_ARCH}/rpms/${PROJECT}/*.rpm"

					# copy (s)rpms into repo
					cd "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}"
					scp *.rpm "${REPOS_SERVER}:${RPM_REPOS_BASE}/${l_DIST}/${l_CODENAME}/${COMPONENT}/${l_ARCH}/rpms/${PROJECT}/" || true

					CREATEREPO_OPTS=""
					if [ "${l_DIST}" = "epel" ] && [ "${l_CODENAME}" = "5" ]; then
						CREATEREPO_OPTS="-s sha"
					fi
					0</dev/null ssh "${REPOS_SERVER}" "cd ${RPM_REPOS_BASE}/${l_DIST}/${l_CODENAME}/${COMPONENT}/${l_ARCH}/ && createrepo ${CREATEREPO_OPTS} ."

					0</dev/null ssh "${REPOS_SERVER}" "cd ${RPM_REPOS_BASE}/${l_DIST}/${l_CODENAME}/${COMPONENT}/${l_ARCH}/repodata &&  rm -f -- repomd.xml.asc && gpg -a -u ${GPG_KEY} --detach-sign repomd.xml" 1>/dev/null 2>/dev/null
					0</dev/null ssh "${REPOS_SERVER}" "cd ${RPM_REPOS_BASE}/${l_DIST}/${l_CODENAME}/${COMPONENT}/${l_ARCH}/repodata && test -e repomd.xml.key || gpg -a --output repomd.xml.key --export ${GPG_KEY}"
				fi
			done
		done
	done
	return 0
}

wait_for_lock() {
	while [ -f "${LOCK_FILE}" ]; do
		pid="$(head -n1 "${LOCK_FILE}")"
		if ! ps "${pid}" 1>/dev/null; then
			rm -f -- "${LOCK_FILE}"
		else
			echo "PROJECT directory is locked, sleeping for 10 seconds..."
			sleep 10
		fi
	done
}

lock_workspace() {
	wait_for_lock
	echo "${$}" > "${LOCK_FILE}"
}

unlock_workspace() {
	rm -f -- "${LOCK_FILE}"
}

delay_build() {
	sleep $[ ( ( $RANDOM % 10 )  + 1 ) * 10 ]s
}


### MAIN ###
set_vars "$@" && {
	if [ "x$(basename "${0}")" = "x${PREFIX}-build-rpm-package" ] || [ "x$(basename "${0}")" = "x${PREFIX}-build+upload-rpm-package" ]; then
		FORCE_BUILD="$(make_boolean "${FORCE_BUILD}")"
		NO_DELAY="$(make_boolean "${NO_DELAY}")"

		# This indentation may look weird, but is actually needed because bash expects either a semicolon or a newline
		# between (the last) command and a closing curly brace.
		cd "${PROJECT_DIR}" && {
			pkgneedsbuild "${CHECKOUT}" || [ "${FORCE_BUILD}" -eq "1" ]
		} && {
			if [ "${FORCE_BUILD}" -eq "1" ] && [ "${NO_DELAY}" -eq "0" ]; then
				delay_build
			fi
			lock_workspace
			prepare_workspace && {
				unlock_workspace
				clear_pkgdist
				build_packages
			}
			unlock_workspace
		}
	fi
	if [ "x$(basename "${0}")" = "x${PREFIX}-upload-rpm-package" ] || [ "x$(basename "${0}")" = "x${PREFIX}-build+upload-rpm-package" ]; then
		upload_packages
	fi
}
