#!/bin/bash

function print_usage(){
    cat <<END
Usage: $0 [Options] <srpm> [scope1 [scope2 ...]]

    This command automates the release process from SRPM to bodhi

DESCRIPTION
    This program runs the build in koji build system, then submit successful
builds to bodhi the package update system.

    Scopes tells this program what branches to be build. 
The default scope (without specifing any scope) is all active branches
(the branches that currently accepts new builds) of this package. 
You can also specify something like "fedora" "epel7" "el6",
which basically means that all fedora active branches (including rawhide),
as well as EPEL 7 and EPEL 6, will be processed.

    If the scopes cover more than one branches, and this program will
also merge git branches for real build (without option "-s").
For example, if the scopes covers master, f24 and f23, it will firstly
import the SRPM on master branch and build, if successful, then it will
checkout the f24 and "git merge master", and so on. 

    The FedPkg git repos will be cloned as sub-directory of current directory
by default. The destination can be changed by either option '-d' or enviornment
FEDPKG_DIR. Note that this program will not resolve the merge conflict, it is
recommended to build with scratch-build first.

    Scratch build mode can be invoked with option '-s'.


ARGUMENTS
    <srpm>
	Source RPM to be build from.

    [scope1 [scope2 ...]]
        What should be build against.
	Valid values:
	    rawhide: Build rawhide.
	    fedora: Build actives fedora releases, including Rawhide.
	    fedora_1: Build the latest supported fedora releases.
	    This is one release eariler than rawhide.

	    fedora_2: Build the second latest supported fedora releases.
	    This is two releases eariler than rawhide.

	    f22 f21 ...: Build the specified fedora releases.

	    epel: Build the currently supported EPEL releases.

	    epel_1: Build the latest supported EPEL releases.

	    epel_2: Build the second latest supported EPEL releases.

	    epel7 el6 ... : The EPEL releases to be built.

        If scopes is not specified, then it will use the existing branch for koji.

OPTIONS
    -b <bugs>: The list of bug this update fixed. Split with ','.

    -d <work_dir>: The parent directory of a fedpkg module.

    -m <message>: Message used as commit message.
       If not specified, then use the latest changelog text.
       (i.e. rpm -qp --queryformat "%{ChangeLogText}" <srpm>)

    -s: Scratch build instead of real build.

    -t updateType: Update type. Valid values:
       [bugfix|security|enhancement|newpackage].
       Default: 
         newpackage: if this package does not exist in bodhi
         enhancement: if the latest change log item has "Enhancement:"
         bugfix: for everything else.

    srpm: SRPM file to be scratch-built with koji.

ENVIRONMENT
    FEDPKG_DIR
        The directory that this program should work on.
	If -d is not specified, this program will use this value as
	work directory.

    BODHI_USER
        Bodhi username. If not specified, it uses environment variable
       	LOGNAME.

EXIT_STATUS
    Success:
        EXIT_OK
 
    Fatal that should stop immediately:
        EXIT_FATAL_UNSPECIFIED: Unspecified fatal error, 
            usually indicate a bug in our scripts.

        EXIT_FATAL_INVALID_OPTIONS: Wrong options were given.

        EXIT_FATAL_MISSING_DEPENDENCY: Cannot find dependency.

        EXIT_FATAL_UNKNOWN_MODULE: Invalid or unknown module name.

        EXIT_FATAL_FAIL: Script detected that a fatal error occurred.

    Error that need to stop before next stage:
         EXIT_ERROR_FAIL: Script detected that an error occurred.

    Return value, should not print error messages:
         EXIT_RETURN_FALSE: Script should return false.
END
}

##=== Begin functions ===

function contains_element () {
    local e
    for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
    return 1
}

# is target been built in koji
# Valid target example: cmake-fedora-1.4.0-1.fc21
function is_target_built(){
    target=$1
    $KOJI_CMD buildinfo $target | grep -qcs -i "State: COMPLETE"
}

# is package exists in bodhi
# Valid target example: cmake-fedora
function is_package_new_in_bodhi(){
    package=$1
    if $CURL_CMD -s -f -X GET "https://admin.fedoraproject.org/pkgdb/api/package/?pkgname=${package}" > /dev/null; then
	return 1
    else
	return 0
    fi
}

function try_command(){
    if ! "$@" ;then
	ret=$?
	echo "cmake-fedora-fedpkg: Failed to $@" > /dev/stderr
	exit $ret
    fi
}

# is target in bodhi
# Valid target example: cmake-fedora-1.4.0-1.fc21
function is_target_in_bodhi(){
    local result=$($CURL_CMD -X GET "https://bodhi.fedoraproject.org/builds/?nvr=$1")
    if [[ -z "$result" ]]; then
	return 1
    elif grep -qcs -i '"total": 0,' <<< "$result" > /dev/null;then
	return 1
    fi
    return 0
}

function is_update_enhancement(){
    echo $ChangeLogText | grep -qcs -e "Enhancement:"
}

## fedpkg_build <gitBranch> <firstBranch> [Options]
function fedpkg_build(){
    local optArray=()
    local gitBranch=$1
    local firstBranch=$2
    shift 2

    echo -n "Has $Build already been built in koji? ... " > /dev/stderr
    if is_target_built $Build ;then
	echo "yes, skip this." > /dev/stderr
    else
	echo "no, start building." > /dev/stderr
	if [[ $ScratchBuild -eq 1 ]];then
	    local kojiBranch=$($CMakeFedoraKojiCmd branch $gitBranch)
	    try_command ${FEDPKG_CMD} --dist $kojiBranch scratch-build --srpm "$Srpm" "$@"
	else
	    ## Real build
	    if [[ "$gitBranch" = "$firstBranch" ]];then
		try_command $GIT_CMD stash
		try_command $GIT_CMD fetch
		try_command $FEDPKG_CMD switch-branch $gitBranch
		try_command $FEDPKG_CMD pull
		try_command $FEDPKG_CMD import "$Srpm"
		try_command $FEDPKG_CMD commit -m "$ChangeLogText"
		try_command $FEDPKG_CMD push
		try_command $FEDPKG_CMD build
	    else
		try_command $FEDPKG_CMD switch-branch $gitBranch
		try_command $GIT_CMD merge $firstBranch
		try_command $FEDPKG_CMD push
		try_command $FEDPKG_CMD build
	    fi
	fi
    fi
}

##=== Variable Definition ===
ScriptDir=$(readlink -f `dirname $0`)
declare EXIT_OK=0
declare EXIT_FATAL_UNSPECIFIED=1
declare EXIT_FATAL_INVALID_OPTIONS=3
declare EXIT_FATAL_MISSING_DEPENDENCY=4
declare EXIT_FATAL_UNKNOWN_MODULE=5
declare EXIT_FATAL_FAIL=5
declare EXIT_ERROR_FAIL=20
declare EXIT_RETURN_FALSE=40

##=== Dependency Checking ===
for d in Modules cmake-fedora/Modules ${ScriptDir}/../Modules /usr/share/cmake/Modules;do
    if [ -r $d/CmakeFedoraScript.cmake ];then
	CMakeFedoraScriptCMake=$d/CmakeFedoraScript.cmake
    fi
done
if [ -z "${CMakeFedoraScriptCMake}" ];then
    echo "[Error] CmakeFedoraScript.cmake is not found" > /dev/stderr
    exit $EXIT_FATAL_MISSING_DEPENDENCY
fi

CMakeFedoraKojiCmd=${ScriptDir}/cmake-fedora-koji
if [ ! -x ${CMakeFedoraKojiCmd} ];then
    echo "[Error] cmake-fedora-koji is not found" > /dev/stderr
    exit $EXIT_FATAL_MISSING_DEPENDENCY
fi

CMakeFedoraPkgdbCmd=${ScriptDir}/cmake-fedora-pkgdb
if [ ! -x ${CMakeFedoraPkgdbCmd} ];then
    echo "[Error] cmake-fedora-pkgdb is not found" > /dev/stderr
    exit $EXIT_FATAL_MISSING_DEPENDENCY
fi

for cmd in curl fedpkg bodhi git koji rpm ;do
    CMakeFedoraScriptOptArray=(-D cmd=find_program verbose_level=1  )
    CMakeFedoraScriptOptArray+=( -D "names=$cmd")
    CmdPath=`cmake "${CMakeFedoraScriptOptArray[@]}" -P ${CMakeFedoraScriptCMake}`
    if [ $? -ne 0 ];then
	exit $EXIT_FATAL_MISSING_DEPENDENCY
    fi

    VarName=`tr a-z A-Z <<<$cmd`_CMD
    eval "$VarName=$CmdPath"
done

##=== Parameter Parsing ===
if [ $# = 0 ]; then
    print_usage
    exit $EXIT_FATAL_INVALID_OPTIONS
fi

: ${BODHI_USER:=$LOGNAME}
echo "BODHI_USER=$BODHI_USER"

WorkDir=${FEDPKG_DIR:-$PWD}
Msg=
BodhiOptArray=()
Bugs=
ScratchBuild=0
UpdateType=

while getopts "hb:d:m:st:" opt;do
    case $opt in
	h)
	    print_usage
	    exit $EXIT_OK
	    ;;
	b )
	    Bugs="$OPTARG"
	    ;;
	d )
	    WorkDir="$OPTARG"
	    ;;
	m )
	    Msg="$OPTARG"
	    ;;
	s )
	    ScratchBuild=1
	    ;;
	t )
	    UpdateType="$OPTARG"
	    ;;
	* )
	    ;;
	    
    esac
done
shift $((OPTIND-1)) 

Srpm=$1
shift

if [[ -z $Srpm ]];then
    print_usage
    exit $EXIT_FATAL_INVALID_OPTIONS
else
    Srpm=`readlink -f $Srpm`
fi

if [[ ! -r "$Srpm" ]];then
    echo "[Fatal] Failed to read $Srpm" > /dev/stderr
    exit $EXIT_FATAL_INVALID_OPTIONS
fi

Name=`$RPM_CMD -qp --queryformat "%{NAME}" $Srpm`
echo "Name=$Name" > /dev/stderr

## Nvr here does not include release tag,
##  (e.g. cmake-fedora-2.0.0-1)
Nvr=`$RPM_CMD -qp --queryformat "%{NAME}-%{VERSION}-%{RELEASE}" $Srpm | sed -e 's/\.fc[0-9]*$//' | sed -e 's/\.el[0-9]*$//'`

if [[ $ScratchBuild -eq 0 ]];then
    ## Variable that required by real build
    if [[ -n "$Bugs" ]];then
	BodhiOptArray+=(--bugs $Bugs)
    fi

    ChangeLogText=$($RPM_CMD -qp --queryformat "%{ChangeLogText}" $Srpm)
    echo "ChangeLogText=$ChangeLogText" > /dev/stderr

    if [[ -z "$Msg" ]];then
	Msg=$ChangeLogText
    fi

    if [[ -z "$UpdateType" ]];then
	if  is_package_new_in_bodhi $Name; then
	    UpdateType=newpackage
	elif is_update_enhancement; then
	    UpdateType=enhancement
	else
	    UpdateType=bugfix
	fi
    fi
    echo "UpdateType=$UpdateType" > /dev/stderr
fi

##=== Determine Branch To Build ===
ScopeBranchArray=(`$CMakeFedoraKojiCmd git-branch $@ | xargs ` )
PackageBranchArray=(`$CMakeFedoraPkgdbCmd $Name`)

declare -a ActionBranchArray=()

if [ -z "$PackageBranchArray" ];then
    ## Cannot found package in PkgDb, maybe a new package?
    if [ $ScratchBuild -eq 0 ];then
	## Failed as the non-scratch build requires a existing package
	echo "[ERROR] Failed to find package $Name in PkgDB for a real build." > /dev/stderr
	exit $EXIT_FATAL_FAIL
    fi
    ActionBranchArray=(${ScopeBranchArray[@]})
else
    ## Interset between scope and branches that package has
    for activeBranch in "${PackageBranchArray[@]}"; do
	if contains_element "${activeBranch}" "${ScopeBranchArray[@]}";then
	    ActionBranchArray+=(${activeBranch})
	fi
    done
fi

echo -n "Branches to process:"
(IFS=' ' echo "${ActionBranchArray[@]}")

GitBranches=${#ActionBranchArray[@]}
if [[ $GitBranches -gt 1 && $ScratchBuild -eq 0 ]];then
    ## More than one branches for real build, thus need git branches
    GitNeedMerge=1
else
    GitNeedMerge=0
fi

if [[ $ScratchBuild -eq 0 ]];then
    if [[ ! -w "$WorkDir" ]];then
	if ! mkdir -p "$WorkDir"; then
	    echo "$WorkDir is not writable." > /dev/stderr
	    exit $EXIT_FATAL_INVALID_OPTIONS
	fi
    fi

    cd "$WorkDir"
    if [[ ! -x "$Name" ]] ;then
	try_command $FEDPKG_CMD clone "$Name"
    fi
    echo "WorkDir=$WorkDir" > /dev/stderr
    cd "$Name"
fi

##=== Build ===
BodhiPushList=
First=

for b in "${ActionBranchArray[@]}";do
    if [[ -z "$First" ]];then
	First=$b
    fi

    KojiBuildInfoSuffix=$($CMakeFedoraKojiCmd koji-buildinfo-suffix $b)
    Build="$Nvr.$KojiBuildInfoSuffix"

    try_command fedpkg_build "$b" "$First"

    if [[ $ScratchBuild -eq 0 ]];then
	BodhiBranch=$($CMakeFedoraKojiCmd bodhi-branch $b)

	if [[ -n "$BodhiBranch" ]];then
	    echo -n "Has $Build already in bodhi? ... " > /dev/stderr
	    if is_target_in_bodhi $Build ; then
		echo "yes, skip this." > /dev/stderr
	    else
		echo "no, will push it." > /dev/stderr
		BodhiPushList+=" $Build"
	    fi
	fi
    fi
done

if [[ $ScratchBuild -eq 0 ]];then
    if [[ -n "$BodhiPushList" ]];then
	try_command ${BODHI_CMD} -n "${BodhiOptArray[@]}" -t $UpdateType -u $BODHI_USER -N "$ChangeLogText" -R testing $BodhiPushList
    else
	echo "Nothing to push to bodhi." > /dev/stderr
    fi
fi
