#!/bin/bash -e

# Includes
PATH="${PATH}:$(dirname "${0}"):/usr/local/share/ui-auto:/usr/share/ui-auto"
. ui-libopt.sh

#
# Tools
#
# Helper tool: Copy file and print code to restore
_ui_restore_file()
{
	local orig="$(pwd)/${1}"
	local copy=$(mktemp -t ui-auto-release-XXXXXXXX) || exit 9
	cp -a "${orig}" "${copy}"
	echo "mv \"${copy}\" \"${orig}\""
}

boolSet()
{
	[ "true" = "$1" ]
}

# Recommended line length is 64 (see below, upstream changes in Debian changelog).
ui_sep0()
{
	echo "----------------------------------------------------------------"
}

ui_sep1()
{
	echo "================================================================"
}

ui_pprint()
{
	local prefix="${1}"
	while read; do
		echo -e "${prefix}${REPLY}"
	done
}

ui_warn()
{
	echo -e "${1}" | ui_pprint "W: " >&2
}

# Extra warning function for the frowned-upon rsign workflow.
ui_warn_rsign()  # <confvar>
{
	if [ -n "${!1}" ]; then
		ui_warn "\n${1}=${!1} (in ~/.ui-auto.conf).\n\n\
WARNING: It's DISCOURAGED to use this remote signing workflow, as it\n\
requires the usually untrusted (build) machine to have access to the\n\
trusted signing machine. It's more secure to connect from the\n\
trusted machine only.\n\
See also: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=855320, debsign(1)\n"
	fi
}

ui_err()
{
	echo -e "${1}" | ui_pprint "E: " >&2
}

ui_ask()
{
	local question="${1}"
	local default="${2}"
	local fatal="${3}"

	# Default default is "yes"
	if [ -z "${default}" ]; then
		default="Y"
	fi

	# Get answer
	local answer
	if ui_opt_given A; then
		answer=${default}
	else
		read -p "=>${fatal} QUESTION: ${1} (y/n) [${default}]? " answer
		if [ -z "${answer}" ]; then
			answer=${default}
		fi
	fi

	[ "${answer}" = "y" -o "${answer}" = "Y" ]
}

ui_read()
{
	local v="${1}"   # Variable to set
	local i="${2}"   # Info
	local d="${3}"   # Default value

	# If empty, set default
	if [ -z "${!v}" ]; then
		eval "${v}=\"${d}\""
	fi

	if ui_opt_given A; then
		return 0
	else
		local value
		read -p "=> INPUT: ${i} [${!v}]? " value

		# If non-empty, set given value
		if [ -n "${value}" ]; then
			eval "${v}=\"${value}\""
		fi
	fi
}

ui_commit()
{
	# May be "add" or "diff"
	local pre=${1}
	local message=${2}
	local file=${3}

	# In snapshot mode, we never check in
	if ui_opt_given s; then
		echo "Snapshot mode: Skipping commit of \"${file}\"."
		return 0
	fi

	while true; do
		# This will either prepare the new file for addition ("add"), or show the changes to commit ("diff")
		ui_sep0
		wd_info
		ui_sep0
		ui-auto-uvc ${pre} "${file}" || true
		if ui_ask "Commit \"${file}\" (say 'n' to open the file in ${EDITOR})"; then
			ui-auto-uvc -m"${message}" commit "${file}"
			return 0
		else
			${EDITOR} "${file}" || true
		fi
	done
	return 1
}

# onExit functions; when using this, you should protect arguments
# using \", for example
#   onExitAdd rm -v \"${ONEXITFILE}\"
onExitAdd()
{
	# singleton for poor guys
	if [ -z "${ONEXITFILE}" ]; then
		ONEXITFILE=$(mktemp -t "ui-auto-release-onexit-XXXXXXXX")
		if ui_opt_given K; then
			onExitAdd echo \"Temporary kept: ${ONEXITFILE}\"
		else
			onExitAdd rm -f \"${ONEXITFILE}\"
		fi
	fi
	echo "${@}" >>"${ONEXITFILE}"
}
onExit()
{
	# Eval all lines in reverse
	[ -z "${ONEXITFILE}" ] || tac "${ONEXITFILE}" | ( while read; do eval "${REPLY}"; done )
}
trap "onExit" EXIT


# Wrapper around mktemp: Uses eval, so we do not have to use a
# subshell which would not allow us to call onExit* funcs, or
# hard-exit the program.
#
# Adds error handling, handles "-K" option and the user-configured
# tmpdir.
#
# Synopsis in functions:
#  local tmp
#  ui_mktemp "tmp" [-d] [DIR]
ui_mktemp()
{
	local var="${1}"
	local arg="${2}"
	if [ -n "${3}" ]; then
		local tmpDir="${3}"
	else
		local tmpDir="${ui_auto_userpref_tmpdir}"
	fi
	local temp
	if temp=$(mktemp ${arg} ${tmpDir}/ui-auto-release-temp-XXXXXXXX); then
		if ui_opt_given K; then
			onExitAdd echo \"Temporary kept: ${temp}\"
		else
			onExitAdd rm -rf \"${temp}\"
		fi
		# Set variable; may be global or local
		eval "${var}=\"${temp}\""
		return 0
	fi
	ui_err "Fatal: Temporary creation error in '${tmpDir}'.\nPlease check system and/or 'ui_auto_userpref_tmpdir' setting in '~/.ui-auto.conf'."
	exit 5
}

#
# Sequence handling
#
SEQUENCE_ALL=(+vccheck +autonews +autochangelog +bs_prep +release_hook -bs_dist +bs_distcheck +tags +upload +notify +debian_tarball -debian_package)
SEQUENCE=(${SEQUENCE_ALL[@]})
SEQUENCE_SNAPSHOT="-vccheck -tags -notify"
SEQUENCE_DEBIAN="+debian_package"
updateSequence()
{
	local items="${1}"
	local item
	for item in ${items}; do
		local i
		local found=false
		for ((i=0; i < ${#SEQUENCE[*]}; ++i)); do
			if [ "${item:1}" = "${SEQUENCE[$i]:1}" -o "${item:1}" = "ALL" ]; then
				SEQUENCE[$i]="${item:0:1}${SEQUENCE[$i]:1}"
				found=true
				[ "${item:1}" = "ALL" ] || break
			fi
		done
		if ! ${found}; then
			ui_opt_error "No such sequence item: ${item}"
			exit 5
		fi
	done
}

printSequence()
{
	local printStatus="${1}"
	for s in ${SEQUENCE[@]}; do
		if [ "${s:0:1}" = "+" ]; then
			echo -n "${s:1}"
			if [ "${printStatus}" = "status" ]; then
				local status="sequent_${s:1}_status"
				echo -n "(${!status})"
			fi
			echo -n " "
		fi
	done
}

printSequenceResults()
{
	echo
	ui_sep1
	echo "Sequence results: $(printSequence status)"
	ui_sep1
}

#
# Configuration tools
#
CONF_CTER=0
conf_add()
{
	CONF_NAME[${CONF_CTER}]="${1}"
	CONF_TYPE[${CONF_CTER}]="${2}"
	CONF_MODE[${CONF_CTER}]="${3}"
	CONF_SHORT[${CONF_CTER}]="${4}"
	CONF_DEFAULT[${CONF_CTER}]="${5}"
	CONF_LONG[${CONF_CTER}]="${6}"

	CONF_CTER=$((CONF_CTER+1))
}

conf_print_item()
{
	local i="${1}"
	local quiet="${2}"

	if [ "${CONF_NAME[$i]}" = "header" ]; then
		${quiet} || echo -e "$(ui_sep0)\n [${CONF_TYPE[$i]}]\n\n${CONF_MODE[$i]}\n$(ui_sep0)" | ui_pprint "# "
	else
		${quiet} || echo "# ${CONF_NAME[$i]}(${CONF_MODE[$i]},${CONF_TYPE[$i]}): ${CONF_SHORT[$i]}"
		if [ -n "${CONF_LONG[$i]}" ]; then
			${quiet} || echo -e "\n${CONF_LONG[$i]}" | ui_pprint "# "
		fi
		echo "${CONF_NAME[$i]}=\"${CONF_DEFAULT[$i]}\""
	fi
	${quiet} || echo
}

conf_print()
{
	for ((i=0; i < ${CONF_CTER}; ++i)) do
		conf_print_item ${i} ${1}
	done
}

conf_find()
{
	local i
	for ((i=0; i < ${CONF_CTER}; ++i)) do
		local n="${CONF_NAME[$i]}"
		if [ "${n}" = "${1}" ]; then
			return 0
		elif [[ "${1}" =~ "_deb_" ]]; then
			# For _deb_ vars, cope for dist diversifications.
			local d
			for d in stable unstable; do
				if [ "${n}_${d}" = "${1}" -o "${n}_${d}_snapshot" = "${1}" ]; then
					return 0
				fi
			done
		fi
	done
	return 1
}

# Check that all mandatory config items are configured
conf_check()
{
	local f="${1}"

	local res=0
	local i
	for ((i=0; i < ${CONF_CTER}; ++i)) do
		if [ "${CONF_NAME[$i]}" != "header" ]; then
			local m=${CONF_MODE[$i]:0:1}
			local d=${CONF_MODE[$i]:2}
			if [ "${m}" = "m" ]; then
				# Ignore if we are in Debian Only Mode, and the variable is non-deb
				if boolSet "${ui_release_debianonly}" && ! [[ "${CONF_NAME[$i]}" =~ "_deb_" ]]; then
					continue;
				elif [ -n "${d}" ]; then
					# Mandatory at: Either "m@-d" or "m@ui_auto_release_vc_loc"
					if [ "-" = "${d:0:1}" ]; then
						# "-d": Mandatory if option set; ignore if not given
						if ! ui_opt_given "${d:1:1}"; then
							continue;
						fi
					else
						# "var": Mandatory if option var set; ignore if empty
						if [ -z "${!d}" ]; then
							continue;
						fi
					fi
				fi
				# Ok, this var seems to be mandatory now
				local n="${CONF_NAME[$i]}"
				if [ -z "${!n}" ]; then
					ui_err "Mandatory (${CONF_MODE[$i]}) config '${n}' not configured."
					res=1
				fi
			fi
		fi
	done

	# Check that configured variables are actually known
	local v
	for v in $(grep -e "^[^#][^[:space:]]\+=" ${f} | cut -d= -f1); do
		if ! conf_find "${v}"; then
			ui_err "Unknown variable in \"${f}\": '${v}'. Typo error?"
			res=1
		fi
	done

	return ${res}
}

#
# Status check
#
status_check()
{
	local errs=0

	conf_check "$(ui_opt_get f)"

	# Forbid version *.0.*
	if [ ${package_version_major} -eq 0 -a ${package_version_minor} -eq 0 ] 2>/dev/null; then
		ui_err "Versions 0.0.* are forbidden; start projects with 0.1.0 (unstable)."
		return 1
	fi

	# VC
	ui-auto-uvc check_installation

	# BS
	boolSet "${ui_release_debianonly}" || ui-auto-ubs check_installation

	# Debian tests
	if ui_opt_given d; then
		ui_check_installed "debchange --version --force-distribution"
		ui_check_installed "dput --help"
	fi
}

# Info about current wd
wd_info()
{
	echo "WD=$(pwd)"
	echo "VC=$(ui-auto-uvc -s)/$(ui-auto-uvc path)"
}

#
# Config info
#
config_info_vc()
{
cat <<EOF
VC location        : ${package_vc}/${ui_release_vc_loc}
VC tag             : ${package_vc}/${ui_release_vc_loc_tags}/${package_version_tag}
VC branch          : ${package_vc}/${ui_release_vc_loc_branches}/${package_branch_tag}
EOF
}

config_download_loc()
{
	printf "%s" "${ui_release_download_loc}"
	if ${ui_release_download_loc_append_package}; then
		printf "/%s" "${package}"
	fi
}

config_info_tarball()
{
	echo "Release tarball    : ${package_tarball_abs}"
	[ -z "${ui_release_upload_loc}" ]   || echo "Upload location    : ${ui_release_upload_loc} [${package}/${package_dist}]"
	[ -z "${ui_release_download_loc}" ] || echo "Download location  : $(config_download_loc) [${package_dist}]"
	[ -z "${package_tarball_deb_abs}" ] || echo "Debian tarball     : ${package_tarball_deb_abs} [commit=${ui_release_deb_orig_commit}]"
}

config_info_debian()
{
cat <<EOF
Debian package     : ${package_deb} in ${package_dir_deb}
Package vc location: ${ui_release_deb_vc}/${ui_release_deb_vc_loc}/${ui_release_deb_vc_tag}
Build command      : [pkgdir=${ui_release_deb_pkg_loc}] ${package_deb_buildpackage}
Changelog entries  : ${ui_release_deb_clentries}
Targets            : dist=${ui_release_deb_dist} revapx=${ui_release_deb_revapx} dput=${ui_release_deb_dput}
EOF
}

config_info_overview()
{
	wd_info
	if ! boolSet "${ui_release_debianonly}"; then
		if [ "${ui_release_snapsep}" = "~" ]; then
			local versioning_info="Set new version *after* release."
		else
			local versioning_info="Set new version *before* release"
		fi

		cat <<EOF
$(ui_sep1)
                     Configuration Overview
$(ui_sep1)
Package            : ${package} (using "$(ui-auto-ubs -s)")
Version            : ${package_version} (${package_dist})
Snapshot policy    : sep='${ui_release_snapsep}': ${versioning_info}

$(config_info_vc)

Release flags      : autochangelog=${ui_release_autochangelog} autonews=${ui_release_autonews} release_hook=${package_hook}

$(config_info_tarball)
EOF
	[ -z "${ui_release_notify}" ] || echo -e "\nNotifications to   : ${ui_release_notify}"
	fi

	# Debian Package
	if ui_opt_given d; then
		ui_sep0
		config_info_debian
	fi
	ui_sep0
	echo "Sequence to run    : $(printSequence)"
	ui_sep1
}

#
# Sequents
#
sequent_vccheck_info()
{
	cat <<EOF
Run a version system specific check whether the
working dir is in sync with server. See 'ui-auto-uvc
-h', command 'check_sync'.
EOF
}

sequent_vccheck()
{
	ui-auto-uvc check_sync
}

# ARGS: FILE VERSION OPTIONS=header|invert
do_getnews()
{
	local file="${1}"
	local regex="^${package}-${2}"
	local options="${3}"

	has_option()
	{
		local option="$1"
		if echo "${options}" | grep --quiet "${option}"; then
			return 0
		else
			return 1
		fi
	}

	# Get pure (w/o header) news; optionally output header
	local news_pure
	ui_mktemp news_pure
	local topRelease=$(grep --line-number "^${package}-" "${file}" | head -n1 | cut -d: -f1)
	if [ $((topRelease)) -gt 1 ]; then
		sed "1,$((topRelease-1))d" "${file}" >"${news_pure}"
		if has_option header; then
			sed -n "1,$((topRelease-1))p" "${file}"
		fi
	else
		cat "${file}" >"${news_pure}"
	fi

	# Compute start/end of requested release
	local start=$(($(grep --line-number "${regex}" "${news_pure}" | head -n1 | cut -d: -f1)))
	if [ ${start} -lt 1 ]; then
		# Not found
		if has_option invert; then
			cat "${news_pure}"
		else
			ui_err "No release for ${regex}."
			return 1
		fi
	else
		local tmp
		ui_mktemp tmp
		sed "1,$((start))d" "${news_pure}" >"${tmp}"
		local length=$(($(grep --line-number "^${package}-" ${tmp} | head -n1 | cut -d: -f1)-1))
		if [ ${length} -eq -1 ]; then
			# no next release -- use file length
			length=$(($(cat "${tmp}" | wc -l)))
		fi
		local end=$((start+length))

		if has_option invert; then
			sed "${start},${end}d" "${news_pure}"
		else
			sed -n "${start},${end}p" "${news_pure}"
		fi
	fi
}

sequent_autonews_info()
{
	cat <<EOF
Automatically handle NEWS file.

This lets you edit the NEWS section for this release in the
configured editor \"${EDITOR}\", and finally checks in via VC.
EOF
}

sequent_autonews()
{
	local new_news
	ui_mktemp new_news

	# News header
	cat <<EOF >"${new_news}"
Summaries of important changes how ${package} is used or behaves.
See ChangeLog for more details.

Downloads:
  o Generic: $(config_download_loc)
  o Current: $(config_download_loc)/${package_dist}/${package_tarball}
$(ui_sep1)
EOF
	# New release line
	local release_line="${package}-${package_version} (${package_dist}) ($(date --rfc-2822)):"

	local existing_news=$(do_getnews NEWS "${package_version}")
	if [ -n "${existing_news}" ]; then
		local existing_head=$(echo "${existing_news}" | head -n1)
		if ! echo "${existing_head}" | grep --quiet --ignore-case "unreleased"; then
			ui_warn "Already released?\nRe-editing existing NEWS section w/o UNRELEASED tag: ${existing_head}."
			ui_warn "Please double-check your package version ${package_version} and NEWS file before continuing."
			ui_ask "Continue (will re-edit ${existing_head})"
		fi
		echo "${existing_news}" | sed "s/^${package}-.*/${release_line}/" >>"${new_news}"
	else
		# Auto NEWS

		# If in snapshot mode, auto-restore NEWS on exit
		if ui_opt_given s; then
			local news_restore=$(_ui_restore_file NEWS) || exit 9
			onExitAdd "${news_restore}"
		fi

		# Ponder release type
		local human_readable_release_type="Unknown"
		if [ "${package_version_tag}" = "none" ]; then
			human_readable_release_type="Snapshot"
		elif [ "${package_branch_tag}" != "none" -o ${package_version_patch} -eq 0 ]; then
			human_readable_release_type="Initial"
		else
			human_readable_release_type="Patched"
		fi

		# Ponder last released values from current NEWS
		local last_release_version=""
		local last_release_date=""
		local last_release_date_ymd=""
		while read; do
			last_release_date="$(printf "%s" "${REPLY}" | cut -d'(' -f3 | cut -d')' -f1)"
			if [ "${last_release_date^^}" != "UNRELEASED" ]; then
				last_release_version="$(printf "%s" "${REPLY}" | cut -d' ' -f1 | rev | cut -d'-' -f1 | rev)"
				if ! last_release_date_ymd="$(date --date="${last_release_date}" +%Y%m%d)"; then
					ui_warn "Date string parsing failed. Falling back to NEWS file modification time (usually incorrect)."
					last_release_date_ymd="$(date --reference="NEWS" +%Y%m%d)"
				fi
				break
			fi
			last_release_version=""
		done < <(grep "^${package}-.\+ (.\+) (.\+)" "NEWS")
		printf "I: Last release info: %s/%s/%s" "${last_release_version}" "${last_release_date}" "${last_release_date_ymd}"

		# Ponder command for Changes
		local since_tag="NO_AUTOMATIC_SINCE_TAG_FOUND"
		if [ -n "${last_release_version}" ]; then
			since_tag="$(printf "${package_cap}_${last_release_version}" | tr "." "_")"
			if [ "$(ui-auto-uvc -s)" = "svn" ]; then
				# Extrawurscht for SVN; does not really work with tags, so we need the revision from the remote TAG URL
				since_tag=$(svn info "${ui_release_vc_loc_tags}/${since_tag}" | grep "^Last Changed Rev:" | cut -d":" -f2- | tr -d "[:space:]")
			fi
			local auto_news_cmd="ui-auto-uvc news ${since_tag}"
		else
			local auto_news_cmd="printf PLEASE_ADD_CHANGES_HERE"
		fi

		printf "\n"
		printf "N: CREATING automated 'Changes' section from VC changes ('first-line summary')\n"
		printf "N: The default is merely guessed from the NEWS file.\n"
		printf "N: Should work fine though for standard cases, but you can FIX it if needed now.\n"
		printf "N: We will just continue if this fails (but you need to deal with its output, fixing the file by editing manually).\n"
		ui_read auto_news_cmd "Command to get NEWS-like Changes from VC"

		cat <<EOF  >>"${new_news}"
${release_line}

${human_readable_release_type} ${package_dist} release.

Changes since ${last_release_version}:

$(${auto_news_cmd} || printf "AUTONEWS COMMAND '%s' FAILED.\n\nPLEASE_FIX_OR_ADD_CHANGES_MANUALLY_HERE" "${auto_news_cmd}")
$(ui_sep0)
EOF
	fi
	do_getnews NEWS "${package_version}" invert >>"${new_news}"
	cat "${new_news}" >NEWS

	ui_commit diff "${package_ci_msg}: News for ${package_version}." "NEWS"
}

sequent_autochangelog_info()
{
	cat <<EOF
Automatically create the ChangeLog file from VC checkin
messages.

This is done via 'ui-auto-uvc changelog', and then the ChangeLog
is checked in via VC.
EOF
}

sequent_autochangelog()
{
	# If in snapshot mode, auto-restore ChangeLog on exit
	if ui_opt_given s; then
		local changelog_restore=$(_ui_restore_file ChangeLog) || exit 9
		onExitAdd "${changelog_restore}"
	fi

	ui-auto-uvc changelog >ChangeLog
	ui_commit diff  "${package_ci_msg}: ChangeLog updated from version control." "ChangeLog"
}

do_check_old_tarball()
{
	local tarball="${1}"
	if [ -e "${tarball}" ]; then
		ui_warn "Already released?\nFound: ${tarball}."
		ui_ask "Continue (will remove tarball)" "N"
		rm -v -f "${tarball}"
	fi
}

sequent_bs_prep_info()
{
	cat <<EOF
Prepare the project's build system.

Runs 'strap c' (clean and strap) and 'configure' via
'ui-auto-ubs'.
EOF
}

sequent_bs_prep()
{
	ui-auto-ubs strap c
	ui-auto-ubs configure
}

sequent_release_hook_info()
{
	cat <<EOF
Run the release hook if configured for this project.

See 'ui-auto-release -P' on how to configure a release hook.
EOF
}

sequent_release_hook()
{
	ui_release_hook
}

tool_tarball()
{
	# Pre error handling
	do_check_old_tarball "${package_tarball_abs}"

	# Release sequence
	ui-auto-ubs ${1}
	mv -v "${package_tarball}" "${package_tarball_abs}"

	# Post error handling
	if [ ! -f "${package_tarball_abs}" ]; then
		ui_err "Tarball not found: ${package_tarball_abs}"
		return 1
	fi

	# Handle -R option
	if ui_opt_given R; then
		onExitAdd rm -v -f \"${package_tarball_abs}\"
	fi
}

sequent_bs_dist_info()
{
	cat <<EOF
Create the release tarball *without* checks using 'ui-auto-ubs'.

$(config_info_tarball)
EOF
}

sequent_bs_dist()
{
	tool_tarball dist
}

sequent_bs_distcheck_info()
{
	cat <<EOF
Create the release tarball with checks using 'ui-auto-ubs'.

$(config_info_tarball)
EOF
}

sequent_bs_distcheck()
{
	tool_tarball distcheck
}

sequent_tags_info()
{
	cat <<EOF
Add release and branch tags to VC ('none'=no tag will be added):

$(config_info_vc)
EOF
}

sequent_tags()
{
	ui-auto-uvc check_sync
	[ "${package_version_tag}" = "none" ] || ui-auto-uvc -m"${package_ci_msg}: Release tag for ${package_version}" \
		tag    "${package_version_tag}" "${ui_release_vc_loc_tags}"
	[ "${package_branch_tag}" = "none" ]  || ui-auto-uvc -m"${package_ci_msg}: Branch forking at ${package_version}" \
		branch "${package_branch_tag}" "${ui_release_vc_loc_branches}"
}

dist_ok()
{
	case ${1} in
		stable|unstable)
			return 0
			;;
		*)
			ui_err "Unknown distribution type \"${1}\": Please use 'stable' or 'unstable'."
			return 1
	esac
}

sequent_upload_info()
{
	cat <<EOF
Upload the release tarball, optionally signing the file.

Will leave a file called 'TARBALL.upload' with upload
information locally:

$(config_info_tarball)
EOF
}

sequent_upload()
{
	local uploadfile="${package_tarball_abs}.upload"

	if [ -e "${uploadfile}" ]; then
		ui_warn "Already uploaded: ${uploadfile} says:\n$(cat ${uploadfile})"
		ui_ask "Continue (will overwrite)" "N"
	fi

	local sigfile=""
	while [ "${sigfile}" = "" ]; do
		if [ -f "${package_tarball_abs}.sig" ]; then
			sigfile="${package_tarball_abs}.sig"
		else
			if ! boolSet "${ui_auto_userpref_nosign}" && ui_ask "GPG-Sign the tarball"; then
				sigfile="${package_tarball_abs}.sig"

				_sign()
				{
					[ -n "${1}" ] || return 1
					local sign_command=${1//%s/${sigfile}}
					sign_command=${sign_command//%t/${package_tarball_abs}}
					if ! ${sign_command}; then
						ui_warn "Signing command \"${sign_command}\" failed."
						return 2
					fi
				}
				if ! _sign "${ui_auto_userpref_sign}"; then
					ui_warn_rsign ui_auto_userpref_rsign
					if ! _sign "${ui_auto_userpref_rsign}"; then
						sigfile="none"
						ui_ask "Continue (will omit sigfile)"
					fi
				fi
			else
				sigfile="none"
			fi
		fi
	done

	local reldir="${package}/${package_dist}"
	local tmpdir
	ui_mktemp tmpdir -d
	echo "Building upload directory in ${tmpdir}..."
	local subdir="${tmpdir}/${reldir}"
	mkdir --parents "${subdir}"
	cp -a "${package_tarball_abs}" "${subdir}/"
	[ "${sigfile}" = "none" ] || cp -a "${sigfile}" "${subdir}/"

	echo "Copying via scp to ${ui_release_upload_loc}..."
	if scp -r ${tmpdir}/* "${ui_release_upload_loc}"; then
		echo "$(date): Uploaded to ${ui_release_upload_loc}/${reldir}." | tee -a "${uploadfile}"

		# Handle -R option for all targets we know by now
		if ui_opt_given R; then
			onExitAdd rm -v -f \"${uploadfile}\"
			onExitAdd rm -v -f \"${package_tarball_abs}.sig\"
		fi
	else
		ui_err "Uploading ${package_tarball_abs} failed.\nFor non-snapshots, you may retry later using '-Q -ALL,+upload'."
		return 1
	fi

	return 0
}

sequent_notify_info()
{
	cat <<EOF
Send notification email to

\"${ui_release_notify}\",

optionally adding more recipients.
EOF
}

sequent_notify()
{
	local notify_line="[ui-auto] ${package}-${package_version} (${package_dist}) released."
	echo
	ui_sep1
	echo ${notify_line}
	ui_sep1
	echo
	echo "About to mail the good news to   : ${ui_release_notify}"
	ui_read notify_add "Add recipients (,-sep, no spaces):"
	if do_getnews NEWS "${package_version}" header | mail -s"${notify_line}" "${ui_release_notify},${notify_add}"; then
		echo "Ok, announced to: ${ui_release_notify},${notify_add}"
	else
		ui_err "Error announcing to: ${ui_release_notify},${notify_add}"
		return 1
	fi
}

sequent_debian_tarball_info()
{
	cat <<EOF
Create the Debian tarball ('orig.tar.gz') for use with Debian
packages.

If enabled, this will also try to commit the tarball (note that
this is not recommended, and to make it work, the directory MUST
be a vc working directory):

$(config_info_tarball)
EOF
}

sequent_debian_tarball()
{
	do_check_old_tarball "${package_tarball_deb_abs}"

	mkdir -p $(dirname "${package_tarball_deb_abs}")
	cp -v "${package_tarball_abs}" "${package_tarball_deb_abs}"

	# Handle -R option
	if ui_opt_given R; then
		onExitAdd rm -v -f \"${package_tarball_deb_abs}\"
	fi

	# Orig commit
	if ${ui_release_deb_orig_commit}; then
		echo "Auto-committing \"${package_tarball_deb_abs}\"..."
		(
			cd $(dirname "${package_tarball_deb_abs}")
			ui_commit add "${package_ci_msg}: New upstream ${package_version}." "${package_tarball_deb}"
		)
		[ $? -eq 0 ] || return 1
	fi
}

sequent_debian_package_info()
{
	cat <<EOF
Generate a Debian package, und upload it via dput.

Basically, this will vc-boostrap the configured location for
Debian-Packaging, edit the Debian changelog, build the package
and upload it via dput.

Commits Debian changelog changes unless in snapshot mode.

In interactive mode, you will be presented the Debian changelog
in your configured editor=\"${EDITOR}\" where you can add manual
changes to the changelog (or to Debian packaging if needed):

$(config_info_debian)
EOF
}

sequent_debian_package()
{
	incDebianVersion()
	{
		local v="${1}"
		chk()
		{
			local sep="${1}"
			local regex=".\+\\${sep}[[:digit:]]\+$"
			if echo "${v}" | grep -q "${regex}"; then
				local pre=$(echo "${v}" | rev | cut -d"${sep}" -f2- | rev)
				local no=$(echo "${v}" | rev | cut -d"${sep}" -f1 | rev)
				echo -n "${pre}${sep}$((no+1))"
				return 0
			fi
			return 1
		}
		chk '-' || chk '.' || chk '+' || echo -n "${1}+1"
	}

	# Bootstrap Debian package and enter build directory
	cd "${package_dir_deb}"
	local buildDir
	ui_mktemp buildDir -d "${package_dir_deb}"
	ui-auto-uvc -S "${ui_release_deb_vc}" checkout "${ui_release_deb_vc_loc}/${ui_release_deb_vc_tag}" "${buildDir}"
	cd "${buildDir}"

	# Sanity check that we actually have checked out a matching Debian package VC
	local vc_deb_name=$(grep "^Source" debian/control | cut -d" " -f2-)
	if [ "${vc_deb_name}" != "${package_deb}" ]; then
		ui_err "Fatal: Can't import orig: Source package name (${vc_deb_name}) does not match actual name (${package_deb})."
		ui_err "Fatal: Maybe you got a wrong Debian VC location in your project's '.ui.auto.conf'?"
		exit 1
	fi

	# Parse current changelog top section
	local current_version=$(dpkg-parsechangelog | grep "^Version" | cut -d" " -f2-)
	local current_dist=$(dpkg-parsechangelog | grep "^Distribution" | cut -d" " -f2-)

	# Compute version: 4 cases (debian only=0|1, snapshot=0|1)
	# Common fancy snapshot version string
	local snaprev="0$(ui_opt_get S)$(ui-auto-uvc version)"
	# Much shorter, but not guaranteeed to sort corectly
	local snaprev_short="0$(ui-auto-uvc version)"
	if boolSet "${ui_release_debianonly}"; then
		# DEBIAN ONLY MODE: We never have a new upstream
		local message="New Debian package based on ${current_version} (${package_dist})"
		if ui_opt_given s; then
			# Snapshot: Fancy snapshot versioning
			if [ "${current_dist}" = "UNRELEASED" ]; then
				# Reuse cl section; must be smaller than the actual release later => "~"
				local version="${current_version}~${snaprev}"
			else
				# New cl section; must be greater than the former release => "."
				local version="${current_version}.${snaprev}"
			fi
		else
			seemsBackport()
			{
				[[ ! "${current_dist}" =~ "~" ]] && [[ "${ui_release_deb_revapx}" =~ "~" ]]
			}
			# Release: On unreleased, we just use the current version, else inc first
			if [ "${current_dist}" = "UNRELEASED" ] || seemsBackport; then
				local version="${current_version}"
			else
				local version=$(incDebianVersion "${current_version}")
			fi
		fi
	else
		# STANDARD MODE: We always have a new upstream version
		local message="New upstream ${package_version} (${package_dist})"
		if ui_opt_given s; then
			# Snapshot: We can use the short Debian revision, as upstream snaphot version already sorts fine
			local version="${package_version}-${snaprev_short}"
		else
			# Release: We have a new upstream, so default is just 1.
			local version="${package_version}-1"
		fi
		# Import orig tarball if configured
		if [ -n "${ui_release_deb_dbuild_import}" ]; then
			${ui_release_deb_dbuild_import//%o/${package_tarball_deb_abs}}
		fi
	fi
	version="${version}${ui_release_deb_revapx}"

	local debchange_arg="--newversion ${version}"
	if [ "${current_dist}" = "UNRELEASED" ]; then
		# Re-use not-yet releases section; unfortunately we cannot
		# change version of existing section via debchange
		sed --in-place "0,/${current_version}/s//${version}/" debian/changelog
		debchange_arg="--append"
		message="${message}: Reusing UNRELEASED section formerly versioned ${current_version}"
	fi

	local autoname="Automated changes by $(basename $0)"

	# Start or update top level section
	DEBFULLNAME="${autoname}" \
		debchange --force-bad-version --force-distribution \
		--distribution="${ui_release_deb_dist}" \
		${debchange_arg} "${message}"

	# On autonews, add upstream NEWS
	if boolSet "${ui_release_autonews}"; then
		# Some ridiculous cosmetic care about the format in Debian changelogs:
		# - debchange splits incoming messages at 68 chars
		# - we can't tell debchange that we want "one entry", so we want
		#   to prefix all entries with "UP: " (4 chars) to mark upstream
		#   changes.
		# I.e., we need to fold at 68-4=64 chars; hence, this is also
		# the recommended line length for NEWS files.
		do_getnews "${package_dir}/NEWS" | head --lines=-1 | fold --width=64 | ui_pprint "UP: " |
		(
			while read; do
				DEBFULLNAME="${autoname}" debchange --append "${REPLY}"
			done
		)
	fi

	# Add all project-configured changelog entries
	echo -e "${ui_release_deb_clentries}" |
	(
		while read; do
			DEBFULLNAME="${autoname}" debchange --append "${REPLY}"
		done
	)

	# Append as normal user; this will also change the finalize
	# line back to the actual user so wen can sign etc.
	debchange --append "ui-auto-release run by $(id -u -n)@$(hostname -f)."
	# When not in auto-mode, open editor, so the actual user may do changes manually
	if ! ui_opt_given A; then
		debchange --append
	fi

	# Checkin change when not in snapshot mode
	ui_commit diff "${package_ci_msg}: Auto Debian package changes." debian/changelog

	# Re-read changelog's top-level version in case it was changed manually
	version=$(dpkg-parsechangelog | grep "^Version" | cut -d" " -f2-)

	echo "Running \"${package_deb_buildpackage}\" in \"$(pwd)\"..."
	${package_deb_buildpackage}

	# Build success. Find .changes file, and set find changes (find as we cannot know what arch the upload is)
	local changes=$(find ${package_dir_deb}/${ui_release_deb_pkg_loc} -type f -name "${package_deb}_${version}_*.changes")
	if [ -z "${changes}" ]; then
		ui_err "Fatal: buildpackage run ok, but no changes found in ${package_dir_deb}/${ui_release_deb_pkg_loc}"
		exit 1
	fi

	local chbase=$(basename "${changes}")
	echo "Debian package built: ${chbase}."

	# Try hard to actually sign the changes file
	if ! ${ui_auto_userpref_debsign} "${changes}"; then
		ui_warn_rsign ui_auto_userpref_debrsign
		if [ -n "${ui_auto_userpref_debrsign}" ]; then
			${ui_auto_userpref_debrsign} "${changes}" || true
		fi
	fi

	# Local files built: Handle local removal mode now
	if ui_opt_given R; then
		# Uhh -- ugly but should work
		local files="${chbase} $(basename "${chbase}" changes)upload $(grep --max-count=1 "^Files:" -A100 ${changes} | grep "^ .\+" | rev | cut -d" " -f1 | rev)"
		for f in ${files}; do
			onExitAdd rm -v -f \"${package_dir_deb}/${ui_release_deb_pkg_loc}/${f}\"
		done
	fi

	# Handle upload via dput
	if ui_ask "Upload Debian package \"${chbase}\" to \"${ui_release_deb_dput}\" (${ui_release_deb_dist})"; then
		while ! dput ${ui_auto_userpref_dput_options} "${ui_release_deb_dput}" "${changes}"; do
			ui_warn "dput failed (see above). You may change dput options and destination now and retry.\n\\
Use user configuration ~/.ui-auto.conf, option 'ui_auto_userpref_dput_options' to configure permanently."
			if ui_opt_given A; then
				# Automatic mode: error
				return 1
			else
				# Interactive: Retry
				ui_read ui_auto_userpref_dput_options "dput options (use 'S' to skip uploading)"
				[ "${ui_auto_userpref_dput_options}" != "S" ] || break
				ui_read ui_release_deb_dput "dput destination"
			fi
		done
	fi
}

autoDetectBootstrapVC()
{
	# subshell so we don't change anything
	(
		# Cd to the directory of the conffile, so we can compute the vc
		# system if s.th. like -f x/y/xy.conf was used outside project dir
		# (ui-auto-uvc -s)
		cd $(dirname "$(ui_opt_get f)")
		echo -n "$(ui-auto-uvc -s)/$(ui-auto-uvc path)"
	) 2>/dev/null
}

# Check if the given conffile is in the current directory.
conffile_is_local()
{
	[ "$(readlink --canonicalize "$(pwd)")" = "$(readlink --canonicalize "$(dirname "$(ui_opt_get f)")")" ]
}

do_vc_bootstrap()
{
	# Long function ;(, but the only use is to compute whether we need
	# to check for VC sync before bootstrapping.
	needVcCheck()
	{
		local vcs="${1}"

		# Don't check on -C (explicit checkout), or on "local" vc
		if ui_opt_given C || [ "${vcs}" == "local" ]; then
			return 1
		else
			# Don't check if we are in a different dir than the config file (i.e., outside the project dir).
			if ! conffile_is_local; then
				return 1
			fi
		fi
		# Per default, do check
		return 0
	}

	local vcs=$(echo "${1}" | cut -d/ -f1)
	local vcpath=$(echo "${1}" | cut -d/ -f2-)
	local vcCheck="${2}"

	# Ask about uncommitted changes before strapping (but not for
	# "local" vc system or if we strap explicitly (-C)).
	if needVcCheck "${vcs}" && ! ui-auto-uvc check_sync; then
		ui_ask "Checkout mode(${vcs}): Above local changes will not be in release. Ignore" "N"
	fi

	# Create and work in temporary directory
	local buildDir
	ui_mktemp buildDir -d
	cd "${buildDir}"

	echo "VC bootstrapping [${vcs}] ${vcpath} in ${buildDir}..."

	# Create checkout and work there
	ui-auto-uvc -S "${vcs}" checkout "${vcpath}" "checkout"
	cd checkout
	ui_sep0
}

# Parse/set _deb_ vars that are subject to diversification by appending "stable|unstable[_snapshot]"
parse_deb_vars()
{
	for n in vc_tag orig_commit dbuild dbuild_options dbuild_import dist revapx clentries dput; do
		# Set default
		local v="ui_release_deb_${n}"
		local value="${!v}"

		# Set dist config if configured
		v="${v}_${package_dist}"
		[ -z "${!v}" ] || value="${!v}"

		# Set dist snapshot config if needed/configured
		if ui_opt_given s; then
			v="${v}_snapshot"
			[ -z "${!v}" ] || value="${!v}"
		fi

		# Overwrite global
		eval "ui_release_deb_${n}=\"${value}\""
	done
}

# Info on a sequent
sequentInfo()
{
	ui_sep1
	( wd_info && echo && sequent_${1}_info 2>/dev/null ) | ui_pprint "Sequent ${1}: "
}

# Run a sequent
sequentRun()
{
	ui_sep1
	echo "=> Sequent \"${sequent}\":"
	ui_sep1
	sequent_${1}
}

# Ponder global variables
ponder_package_globals()
{
	package_vc="$(ui-auto-uvc -s)"

	if [ "function" = "$(type -t ui_release_hook)" ]; then
		package_hook="true"
	else
		package_hook="false"
	fi

	# Actually parse package name and major/minor/patch versions
	eval $(ui-auto-ubs parse)

	# Snapshot mode: Patch package version
	if ui_opt_given s; then
		if ui_opt_given c; then
			TMP_SNAP="$(ui_opt_get S)$(ui-auto-uvc version)"
		else
			TMP_SNAP="$(ui_opt_get S)$(hostname)"
		fi
		# Patch build system, and re-read vars
		BS_RESTORE=$(ui-auto-ubs patch "${package_version}${ui_release_snapsep}${TMP_SNAP}") || exit 9
		onExitAdd ${BS_RESTORE}
		eval $(ui-auto-ubs parse)
	fi

	# Set package's dist
	package_dist=""
	if boolSet "${ui_release_manualdist}"; then
		package_dist=$(ui_opt_get T)
	else
		# Sanity check only
		if ui_opt_given T; then
			ui_err "Auto dist mode: You can't use -T here.\nIf you really want to switch your project\
 to manual mode,\nadd 'ui_release_manualdist=true' to .ui-auto.conf."
			exit 9
		fi
		# Standard convention: EVEN(MINOR) == stable.
		if [ $((package_version_minor % 2)) -eq 0 ]; then
			package_dist="stable"
		else
			package_dist="unstable"
		fi
	fi
	dist_ok "${package_dist}"

	# Set package's tags/branches to add on release
	package_branch_tag="none"
	package_version_tag="none"
	if ! ui_opt_given s; then
		package_version_tag=$(echo ${package_cap}_${package_version} | tr "." "_")
		if [ "${package_dist}" = "stable" -a ${package_version_patch} -eq 0 ]; then
			# Seems to be an initial stable release like '0.2.0': Automatically add a stable branch
			package_branch_tag="${package_cap}_${package_version_major}_${package_version_minor}_PATCHES"
		fi
	fi

	# Global package tarball vars
	package_dir=$(pwd)
	package_tarball="${package}-${package_version}.tar.gz"
	package_tarball_abs=$(readlink --canonicalize-missing "${STOREDIR}/${package_tarball}")
}

# Ponder global Debian variables
ponder_debian_globals()
{
	# package_dist used used for Debian mode too; let's try to set it if unset yet (debian only mode)
	if [ -z "${package_dist}" ]; then
		package_dist=$(ui_opt_get T)
		dist_ok "${package_dist}"
	fi

	# Debian package name; default is package name
	if [ -n "${ui_release_deb_name}" ]; then
		package_deb="${ui_release_deb_name}"
	else
		package_deb="${package}"
	fi

	# Set package_dir_deb
	if boolSet "${ui_release_debianonly}"; then
		# In Debian-only mode, we must be called from DST, and the debdir is always ..
		package_dir_deb=$(readlink --canonicalize-missing "..")
		echo "I: Debian only mode: Setting debdir to parent: ${package_dir_deb}"
	else
		USER_PROJECT_DEBDIR=$(echo "${package}" | tr "-" "_")_debdir
		if [ -n "${!USER_PROJECT_DEBDIR}" ]; then
			# Per project user-configured debdir
			package_dir_deb="${!USER_PROJECT_DEBDIR}"
		elif [ -n "${ui_auto_userpref_debdir}" ]; then
			# Global user-configured debdir
			package_dir_deb="${ui_auto_userpref_debdir}/${package_deb}"
		else
			# Nothing configured! Use default.
			package_dir_deb="${STOREDIR}/ui-auto-release-debian/${package_deb}"
		fi
	fi
	package_dir_deb_abs=$(readlink --canonicalize-missing "${package_dir_deb}")

	package_tarball_deb="${package_deb}_${package_version}.orig.tar.gz"
	package_tarball_deb_abs="${package_dir_deb_abs}/${ui_release_deb_orig_loc}/${package_tarball_deb}"

	# Debian per-dist vars with defaults
	parse_deb_vars
	package_deb_buildpackage="${ui_release_deb_dbuild} -sa -us -uc ${ui_auto_userpref_dbuild_options} ${ui_release_deb_dbuild_options} $(ui_opt_get D)"

	# Auto-override options per buildtool used
	case "${package_deb_buildpackage}" in
		*svn-buildpackage*)
			package_deb_buildpackage="${package_deb_buildpackage} \
				--svn-override=origDir=$(dirname "${package_tarball_deb_abs}"),buildArea=${package_dir_deb_abs}/${ui_release_deb_pkg_loc}"
			echo "[svn-bp] origDir option overridden: ${package_deb_buildpackage}"
			;;
	esac
}

#
# Start processing
#

# Release configuration
conf_add header "ui-auto-release configuration" "\
Needed if you want to use ui-auto-release.

Note: You can also add a 'release hook' function (for very special
requirements) here; for autotools, this is called between ./configure
and distcheck. Example:

function ui_release_hook()
{
  echo 'Release hook stub (doing nothing).'
}"

conf_add ui_release_debianonly bool o "Debian only mode" "false" "\
Set this to true if your .ui-auto.conf is for Debian package
building only.

In a nutshell, this flag makes it possible to use the sequent
'debian_package' to automatically produce packages for Debian
native packages [1] or Debian packages you maintain but not
controlling upstream [2]. Never use this flag in a 'real
upstream' software package.

You may skip all configuration but the Debian configuration below.

For use case [1], you can just add a '.ui-auto.conf' in the top
level source tree like in an ordinary project.

For use case [2], it's recommended to maintain the
'.ui-auto.conf' under debian/ to avoid any possible conflicts
with upstream."

conf_add ui_release_vc_loc string m "Version control system location (for syntax, see vcpath docs in 'ui-auto-uvc -H')"
conf_add ui_release_vc_loc_tags string m "Version control system tags location  (for syntax, see vcpath docs in 'ui-auto-uvc -H')"
conf_add ui_release_vc_loc_branches string m "Version control system branches location (for syntax, see vcpath docs in 'ui-auto-uvc -H')"

conf_add ui_release_upload_loc string o "Scp location where to upload." "" "\
If not set, upload is skipped with a warning.

Example: 'tarballs@my.tarballs.org:public_html'"
conf_add ui_release_download_loc string m@ui_release_upload_loc "Download location." "" "\
A URL base (i.e. w/o 'PROJECT/stable|unstable') from where to download
tarballs.  This will be used for notifications to create convenience
download URLs.

Example: 'http://my.tarballs.org/~tarballs'"
conf_add ui_release_download_loc_append_package bool o "Append package name to download location (disable for SF)." "true"

conf_add ui_release_manualdist bool o "Manually set distribution type (stable,unstable) on release." "false" "\
Setting this flag means you have to use the type each time you release
using the -T options, and you won't get automatic branches after
initial stable releases.

Per default, ui-auto-release uses the MAJOR.MINOR.PATCH with MINOR
even=stable and MINOR odd=unstable convention to automate this. If you
don't like this, enable manualdist."

conf_add ui_release_snapsep string o "Snapshot separator." "." "\
In snapshot mode, this string goes between the current project's
version and the snapshot appendix.

Practically, to get versioning for snapshots right, you have
these two options, implicating on your policy on when to set the
project's version:

'.': Set new version before you release.
'~': Set new upcoming version _after_ you release."

conf_add ui_release_autochangelog bool o "Auto-generated ChangeLog from VC on release." "false"
conf_add ui_release_autonews string o "Automatically handle NEWS sections, and edit NEWS on release." "false"
conf_add ui_release_notify string o "EMail addresses to notify on releases (comma-separated)." " " "\
When empty, EMail notify is skipped; when set, mails are sent to all
(comma separated) email addresses given. In interactive mode you also
may give more addresses on the command line. If you want to send
notifies, but give addresses manually each time, set this to one space
(' ')."

# Debian configuration
conf_add header "ui-auto-release Debian configuration" "\
Needed only if you want ui-auto-release to automatically deal with
Debian packages on project releases.

Killer feature is to be able to automatically produce 'upstream
snapshot Debian packages' in one go, even unattended in a nightly
cron job."

conf_add ui_release_deb_name string o "Name of Debian (source) package" "" "\
Set this only in case the Debian (source) package name differs from
the (upstream) project name."

conf_add ui_release_deb_orig_loc string o "Directory to put auto-generated Debian tarballs." "" "\
Relative to the Debian directory (see user specific ~/.ui-auto.conf on
how to configure this, default would be
'../ui-auto-release-debian/PACKAGE'). If this is set to non-empty (use
'.' for top level), a 'Debian orig' tarball will be put there in each
release run (even without -d). Usually, this is just 'tarballs'."

conf_add ui_release_deb_pkg_loc string m@-d "Where Debian packages live after building." "build-area" "\
Relative to the hardcoded Debian directory (see above)."

conf_add ui_release_deb_vc string m@-d "Version control system used for Debian packaging." "svn" "\
See 'ui-uvc -h' for supported systems. This is needed as we have to
boostrap the Debian packaging from VC."

conf_add ui_release_deb_vc_loc string m@-d "Version control system location (vc specific)"

# Debian configuration: Dist diversifications
conf_add header "ui-auto-release Debian configuration: Distribution diversification" "\
Each of the following variables may be given as

 NAME                  : DEFAULT.
 NAME_unstable         : For unstable; falls back to DEFAULT.
 NAME_unstable_snapshot: For unstable snapshots; falls back to unstable, then DEFAULT.
 NAME_stable           : For stable; falls back to DEFAULT.
 NAME_stable_snapshot  : For stable snapshots; falls back to stable, then DEFAULT.

We explain the DEFAULTs only here; meaning is the same."

conf_add ui_release_deb_vc_tag string m@-d "VC tag to use." "" "\
Example for svn-*: 'trunk'"

conf_add ui_release_deb_orig_commit string o "Whether to check-in orig tarballs via VC." "false" "\
With some setups, orig tarballs are checked into VC for convenience;
you may automate this with release by enabling this.

NOTE: You must have a proper layout with VC support for the tarball
directory beforehand, else this will fail!"

conf_add ui_release_deb_dbuild_import string o "Command to import new upstream tarball." "" "\
Set this only if you need to import new upstreams to version control
with your *-buildpackage tool or setup (for svn-*, 'mergeWithUpstream'
mode is recommended which obsoletes this).

If you add this, %o will be replaced with the new orig tarball.

Example for svn: 'svn-upgrade %o'"

conf_add ui_release_deb_dbuild string m@-d "Command to build the Debian package" "" "\
Example for svn-*: 'svn-buildpackage -rfakeroot -S'"
conf_add ui_release_deb_dbuild_options string o "Additional *-buildpackage options."
conf_add ui_release_deb_dist string m@-d "Distribution to use (Debian changelog)."
conf_add ui_release_deb_revapx string o "Revision appendix (Debian changelog)" "" "\
Will be appended to the Debian revision generated automagically from
the Debian VC."
conf_add ui_release_deb_clentries string o "Pre-configured changelog entries (use \n to seperate them)." "" "\
Only use this if you have special needs."
conf_add ui_release_deb_dput string m@-d "Upload target for dput"


# ui-auto-env configuration
conf_add header "ui-auto-env|shell|update configuration" "\
Needed only if you want support to develop directly from version
control working directories with several inter-dependent projects."

conf_add ui_env_library_paths string o "Library paths (were .so/.a files live)" "" "\
Example: 'src/mylib/.libs src/extralib/.libs'"
conf_add ui_env_include_paths string o "Include paths (root dirs so that the include synaptic work)" "" "\
Example: 'src'"
conf_add ui_env_program_paths string o "Program paths (were executables live)"
conf_add ui_env_m4_macro_paths string o "Paths to m4 macros the project wants to export"


# Options
ui_opt_init "Release an 'ui-auto-enabled' project." \
	"Notes: Unless in manual checkout mode (-C), always run from
the package's top-level source tree. Unless in automatic mode
(-A), ui-auto-release prints status information and prompts you
before doing anything. All modes may be combined
arbitrarily. See '-P' for configuration documentation."

ui_opt_add "c" "Checkout mode: Work in a temporary fresh VC checkout (not wd)."
ui_opt_add "C:" "Checkout mode with explicit VC information; syntax 'VCS/VCPATH'."  "" "c" "\
- Please see 'ui-auto-uvc --help' an explanation on available VCSes and VCPATH syntax.
- Use 'local/'pwd'' (pwd in backticks) explicitly to just get a copy of your current project wd to work in." \
"expert"

ui_opt_add "s"  "Snapshot mode: Do a snapshot release." "" "" "\
- Auto-generates snapshot versions.
- Omit vc checkins.
- Omit sequents: ${SEQUENCE_SNAPSHOT}."
ui_opt_add "S:" "Snapshot mode with manual version appendix." "snapshot$(date -u -d "@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y%m%d%H%M%S)" "s" "\
- Example: '~rc1' when your version is already bumped to the upcoming stable." \
"expert"

ui_opt_add "d"  "Debian   mode: Do a subsequent Debian package release." "" "" "\
- Taints sequence: ${SEQUENCE_DEBIAN}."
ui_opt_add "D:" "Debian mode with extra options for the *-buildpackage run." " " "d" "" "expert"

ui_opt_add "Q:" "SeQuence tainting: Manually add (+) or remove (-) sequents." "" "" "\
Meta sequent 'ALL' may be used to affect all sequents.
Available sequents: ${SEQUENCE_ALL[*]}" \
"expert"

ui_opt_add "A"  "Automatic (non-interactive) mode: Assume defaults for all questions." "" "" "" "expert"
ui_opt_add "T:" "Manually set the distribution type: Either 'stable' or 'unstable'." "" "" "" "expert"

ui_opt_add "f:" "Project config file location." "./.ui-auto.conf" "" "" "expert"
ui_opt_add "F:" "User config file location." "$(ui_reproducible_builds_home)/.ui-auto.conf" "" "" "expert"

ui_opt_add "n" "No action mode: Don't actually run sequents, just show what we would do." "" "" "" "expert"
ui_opt_add "R" "Remove local release files on exit." "" "" "" "expert"
ui_opt_add "K" "Keep temporary files and directories." "" "" "" "expert"

ui_opt_add "p" "Print sample project configuration with default values." "" "" "" "expert"
ui_opt_add "P" "Like -p, but verbosely (i.e., with documentation)." "" "" "" "expert"
ui_opt_add "I" "Print all sequent docs." "" "" "" "expert"

ui_opt_parse "$@"

# Extra actions
if ui_opt_given p; then
	conf_print true
	exit 0
elif ui_opt_given P; then
	conf_print false
	exit 0
elif ui_opt_given I; then
	for s in ${SEQUENCE_ALL[@]}; do
		sequentInfo "${s:1}"
	done
	exit 0
fi

# Sanity check on root user
if [ "$(id -u)" = "0" ]; then
	ui_opt_error "You are root, go away"
fi

# Set user global configuration defaults
ui_auto_userpref_tmpdir="/tmp"
ui_auto_userpref_nosign=false
ui_auto_userpref_sign="gpg --sign --detach --armor --output=%s %t"
ui_auto_userpref_rsign=""
ui_auto_userpref_dbuild_options=""
ui_auto_userpref_debdir=""
ui_auto_userpref_debsign="debsign"
ui_auto_userpref_debrsign=""


# Read user configuration (if exists).
[ ! -e "$(ui_opt_get F)" ] || . "$(ui_opt_get F)"
ui_warn_rsign ui_auto_userpref_rsign
ui_warn_rsign ui_auto_userpref_debrsign

# VC bootstrap first if needed
STOREDIR=$(pwd)/..
if ui_opt_given c; then
	# STOREDIR: On excplit checkout (-C) or when the given conffile is not in wd, store to current directory.
	if ui_opt_given C || ! conffile_is_local; then
		STOREDIR=$(pwd)
	fi

	# Strap explicitly, or automatically
	if ui_opt_given C; then
		do_vc_bootstrap "$(ui_opt_get C)"
	else
		do_vc_bootstrap "$(autoDetectBootstrapVC)"
	fi
fi

# Set project default values
eval $(conf_print true)

# Read given project configuration (must exist).
set +e
if ! . "$(ui_opt_get f)"; then
	ui_err "Error reading project config. Possible causes:"
	ui_err "* You are not in a project directory."
	ui_err "* You have no config file yet: Check '-p|-P' for templates."
	ui_err "* You used checkout mode (-c), but your config is not in VC? Check in, or [expert] use '-f \$(pwd)/$(ui_opt_get f)'."
	exit 100
fi
set -e

# Implicitly set "-d" (Debian Mode) on Debian Only Mode
if boolSet "${ui_release_debianonly}"; then
	ui_opt_set d "true"
fi

# For EDITOR, fall back to VISUAL, the vi.
if [ "${EDITOR}" = "" ]; then
	if [ "${VISUAL}" = "" ]; then
		EDITOR="vi"
	else
		EDITOR="${VISUAL}"
	fi
fi
# Set VISUAL to EDITOR to force our preference for EDITOR
VISUAL=${EDITOR}

# Ponder global variables
boolSet "${ui_release_debianonly}" || ponder_package_globals
ponder_debian_globals

# Checkin message
package_ci_msg="[ui-auto-release run by $(id -n -u)@$(hostname -f)]"

#
# Compute actual sequence for this run
#

# Options -s, -d
! ui_opt_given s || updateSequence "${SEQUENCE_SNAPSHOT}"
! ui_opt_given d || updateSequence "${SEQUENCE_DEBIAN}"

# Config news, changelog
boolSet "${ui_release_autonews}"      || updateSequence "-autonews"
boolSet "${ui_release_autochangelog}" || updateSequence "-autochangelog"
${package_hook}                       || updateSequence "-release_hook"
[ -n "${ui_release_upload_loc}" ]     || updateSequence "-upload"
[ -n "${ui_release_notify}" ]         || updateSequence "-notify"
[ -n "${ui_release_deb_orig_loc}" ]   || updateSequence "-debian_tarball"
# Debian only mode is special, only run debian_package
! boolSet "${ui_release_debianonly}"  || updateSequence "-ALL +debian_package"

# Last, with option to overwrite: Option -Q
! ui_opt_given Q || updateSequence "$(ui_opt_get Q | tr ',' ' ')"

# Status check
status_check

# Overview
config_info_overview

# Show sequence, and ask about how to go on
ui_read CONTINUE "A=Continue in automatic mode, c|C=Continue?" "C"
case ${CONTINUE} in
	c|C)
		;;
	A)
		echo "Setting automatic mode..."
		ui_opt_set A "true"
		;;
	*)
		exit 0
		;;
esac

# Always print sequence results
onExitAdd printSequenceResults

# Run sequence
for sequent in $(printSequence); do
	sequentInfo "${sequent}"

	if ui_ask "Run sequent \"${sequent}\""; then
		while true; do
			# Run all sequents in a subshell; this means nothing will (and
			# can!) taint other sequents by setting global vars. Also, using
			# "-e", this means error handling is enabled in the subshell, but
			# we can test the result of the subshell ("if sequentRun; ..." will
			# DISABLE "-e" error handling).
			# For bash 4.0, we need workarounds with set -+e
			set +e
			(
				set -e
				if ui_opt_given n; then
					echo "No action mode: Would run sequent \"${sequent}\""
				else
					sequentRun "${sequent}"
				fi
			)
			RET=$?
			set -e
			if [ ${RET} -eq 0 ]; then
				# Success, continue with next sequent
				eval "sequent_${sequent}_status=ok"
				break
			else
				# Failure; lets see what we can do
				ui_sep0
				ui_err "Sequent \"${sequent}\" failed. Please try to fix errors above."

				# Always fail in auto-mode, else ask
				if ui_opt_given A; then
					ANSWER="C"
				else
					read -p "=> Sequent \"${sequent}\": (R)etry, (I)gnore or (C)ancel? [R] " ANSWER
				fi
				ANSWER=$(echo "${ANSWER}" | tr "[ric]" "[RIC]")
				case ${ANSWER} in
					C)
						eval "sequent_${sequent}_status=ERR"
						ui_err "Sequence \"$(printSequence)\"\nfailed at sequent \"${sequent}\"."
						exit 3
						;;
					I)
						eval "sequent_${sequent}_status=ERR-IGN"
						ui_warn "Ignoring failed sequent \"${sequent}\"."
						break
						;;
				esac
			fi
		done
	else
		# Skip, continue with next sequent
		ui_warn "Skipping sequent \"${sequent}\"."
		eval "sequent_${sequent}_status=SKIP"
	fi
done

exit 0
