#!/bin/sh -efu
#
# Copyright (C) 2009  Paul Wolneykien <manowar@altlinux.org>
#
# This file 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 2 of the License, or
# (at your option) any later version.
#
# This program 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.
#

. gear-sh-functions
. girar-client-sh-functions

show_help()
{
	cat <<EOF
Usage: $PROG [Options] [<branch name>]

$PROG makes a new release branch and tag modifying the package release
version accordingly to the build policy. Control of the remote build
task can be done with the use of the corresponding options.

$PROG uses the git configuration file. The following variables are read:

 * girar.remote, corresponding to --remote;

Options:
  -R,--remote=NAME    girar server alias, defaults to git.alt;
  -o,--origin=NAME    use NAME instead of 'origin' to set up git remote;
  -M,--merge[=NAME]   merge branch with the master branch (default: \`master');
  -b,--build          create and run a remote build task, implies -t;
  -t,--task[=NUM[:SUBNUM]] create a remote build task or add entry to a task NUM,
                      optionally the subtask id can be specified;
  -C,--committer=DATA information about the committer (i.e. full name <email>);
  -m,--message=MSG    changelog message;
  -d,--dry-run        output information, do not make any changes (implies -v);
  -p,--push           push modifications to the remote repository;
  -f,--force          ignore warnings and continue;
  -T,--test           create and run a remote test build task, implies -b;
  -c BUGS,
  --closes=BUGS       add "closes" clause to the changelog with BUGS as bug
                      number list;

  -q,--quiet          try to be more quiet;
  -v,--verbose        print a message for each action;
  -V,--version        print program version and exit;
  -h,--help           show this text and exit.

Report bugs to http://bugs.altlinux.ru/

EOF
	exit
}

print_version()
{
	cat <<EOF
$PROG version $PROG_VERSION
Written by Paul Wolneykien <manowar@altlinux.org>

Copyright (C) 2009 Paul Wolneykien <manowar@altlinux.org>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF
	exit
}

current_branch()
{
	git branch | sed -n -e 's/^\*[[:space:]]*\(.*\)$/\1/p'
}

TEMP=`getopt -n $PROG -o R:,o:,M::,b,t::,C:,m:,d,p,f,T,c:,q,v,V,h \
             -l remote:,origin:,merge::,build,task::,committer:,message:,dry-run,push,force,test,closes:,quiet,verbose,version,help -- "$@"` ||
	show_usage
eval set -- "$TEMP"

origin="origin"
make_task=
task_number=
run_task=
committer=
dry_run=
changelog_message=
push=
force=
test_only=
master_branch=
closes=
while :; do
	case "$1" in
		-R|--remote) shift; girar_remote="$1";;
		-o|--origin) shift; origin="$1";;
		-M|--merge) shift; master_branch="${1:-master}";;
		-b|--build) push=-p; make_task=-t; run_task=-b;;
		-t|--task) shift; push=-p; make_task=-t; task_number="$1";;
		-C|--committer) shift; committer="$1";;
		-m|--message) shift; changelog_message="$1";;
		-d|--dry-run) dry_run=-d; verbose=-v;;
                -p|--push) push=-p;;
		-f|--force) force=-f;;
		-T|--test) test_only=-T; push=-p; make_task=-t; run_task=-b;;
		-c|--closes) shift; closes="$closes$1";;
		-q|--quiet) quiet=-q;;
		-v|--verbose) verbose=-v;;
		-V|--version) print_version;;
		-h|--help) show_help;;
		--) shift; break;;
		*) fatal "unrecognized option: $1";;
	esac
	shift
done

if [ -n "$closes" ]; then
        closes="$(echo "$closes" | sed -e 's/,[^[:space:]]/, /g' -e 's/[[:space:]]\+/ /g')"
fi

new_task=

subtask_number=
if [ "${task_number%%:*}" != "$task_number" ]; then
	subtask_number="${task_number##*:}"
	task_number="${task_number%%:*}"
fi

branch_name=
branch_suffix=
if [ $# -gt 0 ]; then
	branch_name="$1"
	case "$branch_name" in
		c6) branch_suffix="M55C";;
                [a-z][0-9]*) branch_suffix="M$(echo ${branch_name#[a-z]}0${branch_name%[0-9]*} | tr [:lower:] [:upper:])";;
		sisyphus*) branch_suffix=;;
		*) branch_suffix="M$(echo "$branch_name" | sed -e 's/\.//g')";;
	esac
	branch_suffix="$branch_suffix"
else
	branch_name="sisyphus"
	branch_suffix=
fi

verbose "Branch name: $branch_name"
verbose "Branch suffix: $branch_suffix"

rules="${RULES-}"
main_tree_id='HEAD'
specfile=

cleanup_handler()
{
	[ -z "$workdir" ] || rm -rf -- "$workdir"
	[ "$(current_branch)" = "$orig_branch" ] || git checkout "$orig_branch"
	if [ "$1" -ne 0 ]; then
	    if [ -n "$new_task" ] && [ "$new_task" != "no" ]; then
		message "Remove task $task_number from the $girar_remote"
		run_remote_command task rm "$task_number"
	    fi
	fi
}

install_cleanup_handler cleanup_handler
workdir="$(mktemp -dt "$PROG.XXXXXXXXXX")"

orig_branch="$(current_branch)"
if [ "$orig_branch" != "$branch_name" ]; then
	if [ -z "$(git branch | sed -n -e "s/^\*\?[[:space:]]*\($branch_name\)$/\1/p")" ]; then
		if [ -n "$dry_run" ]; then
			message "Would create branch $branch_name"
		else
			git branch "$branch_name"
			git checkout "$branch_name"
		fi
	else
		if [ -n "$master_branch" ] && ! git diff --quiet "$master_branch" "$branch_name"; then
			if [ -n "$dry_run" ]; then
				message "Would merge branch $master_branch into $branch_name"
			else
				git checkout "$branch_name"
				git merge "$master_branch"
			fi
		else
			git checkout "$branch_name"
		fi
	fi
fi

find_specfile

[ -n "$specfile" ] || fatal "Spec file not found"
verbose "Working spec file: $specfile"
eval $(gear --describe | while read n v r; do echo "package_name='$n'; package_version='$v'; package_release='$r'"; done)

[ -n "$package_version" ] || fatal "Unable to get the package version"
package_version_plain="$package_version"
[ -n "$package_release" ] || fatal "Unable to get the package release"
package_epoch="$(sed -n -e "s/^Epoch:[[:space:]]\+\(.*\)$/\1/p" "$specfile")"
if [ -n "$package_epoch" ]; then
	package_version="$package_epoch:$package_version"
fi

version_pattern="^\* ... ... .. .... .* <.*@.*> \([^-]\+-.*\)$"
last_version="$(cat $specfile | sed -n -e "/$version_pattern/ {s/$version_pattern/\1/p; q}")"

if [ "$package_version-$package_release" != "$last_version" ]; then
	if [ -z "$force" ]; then
		verbose "Package version: $package_version-$package_release"
		verbose "Last changelog entry version: $last_version"
		fatal "Warning! Package version doesn't match last changelog entry. Fix or use --force to ignore this warning."
	fi
fi

branch_release_version=
if [ -n "$branch_suffix" ] && [ "${last_version%%*.$branch_suffix.*}" = "$last_version" ]; then
	branch_release_pattern="^\* ... ... .. .... .* <.*@.*> [^-]\+-.*\.$branch_suffix\.\([0-9]\+\)$"
	last_branch_release="$(cat $specfile | sed -n -e "/$branch_release_pattern/ {s/$branch_release_pattern/\1/p; q}")"
	if [ -n "$last_branch_release" ]; then
		branch_release_version="$(( $last_branch_release + 1 ))"
	else
		branch_release_version=1
	fi
	package_release="${package_release%%.M*}"
	release_pref="${package_release%%[0-9]*}"
        release_num="$(echo "$package_release" | sed -e 's/^.*alt\([0-9]\+\).*$/\1/')"
        release_suf="${package_release##$release_pref$release_num}"
        [ -n "$release_suf" ] && release_suf="$release_suf."
	release_num=$(( $release_num - 1 ))
        [ -n "$branch_suffix" ] && branch_suffix=".$branch_suffix"
	package_release="$release_pref$release_num$release_suf$branch_suffix.$branch_release_version"
fi

verbose "Package version: $package_version-$package_release"

if [ -z "$committer" ]; then
        committer="$(git config --get user.name) <$(git config --get user.email)>"
fi
if [ -z "$committer" ]; then
	committer_pattern="^\* ... ... .. .... \(.*\) \(<.*@.*>\) .*$"
	committer="$(cat $specfile | sed -n -e "/$committer_pattern/ {s/$committer_pattern/\1 \2/p; q}")"
	[ -n "$committer" ] || fatal "Unable to get information about the last committer"
fi
verbose "Package committer: $committer"

if [ "$last_version" != "$package_version-$package_release" ]; then
	[ -z "$changelog_message" ] && changelog_message="Release $branch_release_version for the branch $branch_name"
        [ -n "$closes" ] && changelog_message="$changelog_message (closes: $closes)"
	verbose "Commit comment message: $changelog_message"
	changelog_entry="* $(LANG=C; date +'%a %b %d %Y') $committer $package_version-$package_release"
	verbose "Changelog entry: $changelog_entry"
	if [ -z "$dry_run" ]; then
		sed -i -e "s/^\(Release:[[:space:]]\+\).*$/\1$package_release/" \
	       	       -e "/^%changelog/ a${changelog_entry}\n- ${changelog_message}.\n" "$specfile"
		verbose "Commit spec file"
		git add "$specfile"
		git commit -q -m "$changelog_message" "$specfile"
	fi
else
	verbose "Files are untouched"
	if [ -z "$changelog_message" ]; then
		changelog_message="Release of v$package_version-$package_release"
	fi
	verbose "Tag annotation: $changelog_message"
fi

if [ -n "$dry_run" ] && [ -n "$test_only" ]; then
	verbose "TEST only mode on"
fi

tag_name="v$package_version_plain-$package_release"
verbose "Release tag: $tag_name"
if [ -z "$dry_run" ]; then
	verbose "Tag and sign the branch"
	git tag -a -s $([ -n "$force" ] && echo -f) -m "$changelog_message" -u "${committer%<*@*>*}" "$tag_name"
fi

if [ -z "$dry_run" ] && [ -n "$push" ]; then
	verbose "Push the $branch_name to the $origin remote"
	git push -q "$origin" "$branch_name" $([ -n "$force" ] && echo --force)
	verbose "Push the tags to the $origin remote"
	git push -q --tags "$origin" $([ -n "$force" ] && echo --force)

    if [ -n "$make_task" ]; then
	remote_path="$(git remote -v | sed -n -e "s/^$origin[[:space:]]\+$girar_remote:\(.*\.git\)[[:space:]]\+(push)$/\1/p")"
	[ -n "$remote_path" ] || fatal "Unable to figure out push path of the remote $origin at $girar_remote server"
	if [ -z "$task_number" ]; then
	    verbose "Make new remote task at the $girar_remote server"
	    task_number="$(run_remote_command task new "$branch_name" 2>&1 | sed -n -e '/^new task/ {s/^.*#\([0-9]\+\):.*$/\1/p; q}')"
	    new_task=yes
	fi
	[ -n "$task_number" ] || fatal "No task number"
	verbose "Task number: $task_number"
	[ -z "$subtask_number" ] || verbose "Subtask number: $subtask_number"
	verbose "Add a build tag for "$remote_path" "$tag_name" to the task $task_number"
	run_remote_command task add "$task_number" $subtask_number repo "$remote_path" "$tag_name"
	if [ -n "$run_task" ]; then
	    if [ -z "$test_only" ]; then
	            verbose "Run task $task_number"
		    run_remote_command task run "$task_number"
	    else
	            verbose "Run task $task_number in TEST mode"
		    run_remote_command task run --test-only "$task_number"
	    fi
	fi
    fi
fi
