#!/bin/bash

# copyright (C) 2007, Novell Inc.
# License: GPL v2 or later
# Authors:  Susanne Oberhauser  froh@novell.com
# Author:   Martin Mohring      martin.mohring@opensuse.org

usage="${0} [--help]

Prepare and update an open build server package to the current
repository code level.

\$PWD is a directory with all files that need to go to the package.

The distribution and build server specifics are read from ./.distrc

LASTSTEP= one of
  checkout      create a local osc working directory like this
                   osc \${OSCOPTS} co \${PROJECT} \${PACKAGE}

  tar           update the tar ball _unless_ MAINTENANCE is set

                currently works only with svn.  creates a tar ball
                like this:
                   \${PACKAGE}-\$(svnversion \${SVNDIR//:/_}).tar.bz2

                reference that from the spec file like this:
                   %define svnversion updated_by_script
                   Source:         %{name}-%{svnversion}.tar.bz2  

  diff          compare \$PWD with what is in obs

  update        update the obs working directory with what is in \$PWD

  localbuild    build on the local machine, like this:
                   'osc \${OSCOPTS} build \\
                        \${BUILDOPTS} \${PROJECT} \${PACKAGE} \${ARCH}'

  upload        'osc \${OSCOPTS} ci' to the build server


--help          display this message"

###################################################################
# be scrupulous
set -o nounset
set -o errexit
set -o pipefail

function verbose {
    ${VERBOSE:+$*}
}
function debug {
    ${DEBUG:+$*}
}

debug verbose set -x
###################################################################

source ./.distrc

# check the environment
: ${PROJECT:?PROJECT unset. set in .distrc or environment.}
: ${PACKAGE:?PACKAGE unset. set in .distrc or environment.}
: ${TARGET:?TARGET unset. set in .distrc or environment.}
: ${ARCH:=i586}
: ${SPECFILE:=*.spec}
: ${TARNAME:=$PACKAGE}

: ${SVNDIR:?SVNDIR unset. set in .distrc or environment.}
: ${BUILDOPTS:=}

: ${OSCOPTS:=}

: ${MAINTENANCE:=}

: ${EXCLUDES:=}
# be smart: the following files are hardly ever part of a package.
# after checkpout, there will also be a directory with the project name in $PWD
: ${CANONICAL_EXCLUDES:=svn-commit*.tmp \#*\# .\#* distribute .distrc *~ .*~ .svn .osc RCS .cvs . .. $PROJECT}

: ${LASTSTEP=localbuild}

###################################################################
# command line

# At this time, there is no command line arguments, if there is one,
# give help on usage, then exit.

if [ $# != 0 ]
then
    echo "$usage

Environment:
"

    for var in \
	PROJECT PACKAGE TARGET ARCH SPECFILE \
	EXCLUDES CANONICAL_EXCLUDES \
	LASTSTEP OSCOPTS BUILDOPTS \
	MAINTENANCE \
	DEBUG VERBOSE
    do
      echo "$var=${!var:-null or unset}"
    done
    exit 1
fi

###################################################################
# check what needs to be done

# defaults, something means yes, null means no

: ${DO_CHECKOUT:=no}
: ${DO_TAR:=yes}
: ${DO_DIFF:=yes}
: ${DO_ASK=yes} 
: ${DO_UPDATE:=no}
: ${DO_BUILD:=no}
: ${DO_CHECKIN:=no}


case $LASTSTEP in
    checkout)
	DO_CHECKOUT=y
	unset DO_DIFF DO_TAR DO_ASK DO_UPDATE DO_BUILD DO_CHECKIN
	;;
    tar)
	# self detect wether checkout is needed
	DO_TAR=y
	unset DO_UPDATE DO_ASK DO_BUILD DO_CHECKIN
	;;
    diff)
	# self detect wether checkout is needed
	DO_TAR=y
	DO_DIFF=y
	unset DO_UPDATE DO_ASK DO_BUILD DO_CHECKIN
    	;;
    update)
	# self detect wether checkout is needed
	DO_TAR=y
	DO_DIFF=y
	DO_ASK=y #default is ask
	DO_UPDATE=y
	# leave DO_ASK allone here, needs to be unset explicitely
	unset DO_BUILD DO_CHECKIN
    	;;
    localbuild)
	# self detect wether checkout is needed
	DO_TAR=y
	DO_DIFF=y
	DO_ASK=y
	DO_UPDATE=y
	DO_BUILD=y
	unset DO_CHECKIN
	;;
    upload)
	# self detect wether checkout is needed
	DO_TAR=y
	DO_DIFF=y
	DO_ASK=y
	DO_UPDATE=y
	DO_BUILD=y
	DO_CHECKIN=y
	;;
    *)
	echo "unknown LASTSTEP"
	exit 1
esac

# make sure no means no:  set 'no' to null
for var in DO_CHECKOUT DO_DIFF DO_ASK DO_UPDATE DO_BUILD DO_CHECKIN
do
  case X${!var:-} in
      Xy|XY|Xyes|XYes|XYES)
	  eval $var=yes
	  ;;
      Xn|XN|Xno|XNo|XNO|X)
	  eval $var=
	  ;;
      *)
	  echo "please set $var to (y|Y|yes|Yes|YES) or (n|N|no|No|NO|).
  Current value: <${!var}>"
	  exit 1
	  ;;
  esac
done

export DO_CHECKOUT DO_DIFF DO_ASK DO_UPDATE DO_BUILD DO_CHECKIN

###################################################################
# do what needs to be done
###################################################################
# checkout if the package is not there

if test ! -d "${PROJECT}/${PACKAGE}/.osc"; then
    DO_CHECKOUT=true
fi

if [ ${DO_CHECKOUT} ]; then
    osc ${OSCOPTS} co "${PROJECT}" "${PACKAGE}"
fi

###################################################################
# update the tar ball

# TODO: check oscupstream for this

if [ ${MAINTENANCE} ]; then
    echo "MAINTENANCE mode, skipping tar ball update."
fi
if [ ${DO_TAR} -a ! ${MAINTENANCE} ]; then

    # Actually the method to create the tar ball and figure the
    # version information depends on the SCM and the working style of
    # the project.

    # TODO: I think factoring this into a helper function that creates
    # the tar ball and echoes the revision, and then have just one
    # variable in .distrc that controls the export method will be a
    # smart thing to do.

    # Could also do a svn export and parse the revsion from the
    # output.

    # For the time being: create the tar ball from a checked out
    # working copy.

    SVNVERSION=$( svnversion ${SVNDIR} )

    # Get rid of the colons in favour of underbars: Svnversion can
    # give a REV1:REV2 label.  tar then interprets REV1 as hostname to
    # connect to.
    SVNVERSION=${SVNVERSION//:/_}

    FULL_VERSION=${VERSION:+$VERSION.}${SVNVERSION}

    : ${TARFILE:=${TARNAME}-${VERSION}.${SVNVERSION}}

    if [ ! -f ${TARFILE}.tar.bz2 ] ; then

	# add the tar file to the file system, so svn status knows about it.
	touch ${TARFILE}.tar.bz2
	tar cvjf ${TARFILE}.tar.bz2 \
	    --exclude-from <( svn status --no-ignore ${SVNDIR} | grep -E "^(\?|I)" | cut -c8-) \
	    --exclude=".svn" \
	    ${SVNDIR}
    fi

    # remove old tar balls if they still are there.
    comm -23 \
	<( ls -1 ${TARNAME}-*.tar.bz2 | sed -e"s,${TARNAME}-\(.*\)\.tar\.bz2,\1," | sort -u ) \
	<( echo $FULL_VERSION )  |
    while read ver; do
	# Don't ask.  It will annoy soon.
	rm -v ${TARNAME}-${ver}.tar.bz2
    done

    # The version information in the specfile is only updated in the
    # copy in the osc build directory.  This allows the tar ball to
    # contain a locally unmodified file

fi

###################################################################
# check the package file list
EXCLUDES=$(
    echo ${EXCLUDES} ${CANONICAL_EXCLUDES} |
    tr --squeeze "[:blank:]" "\n" |
    sort -u )

debug echo EXCLUDES="${EXCLUDES}"

PACKAGE_FILES=$( comm -13 <(echo "${EXCLUDES}") <(ls -1d * .*) )

debug echo PACKAGE_FILES="$PACKAGE_FILES"

DEPRECATED_FILES=$( debug set -x; comm -13 \
	<( cat <( echo "$PACKAGE_FILES" ) <( echo "${CANONICAL_EXCLUDES}")  |
	   tr --squeeze "[:blank:]" "\n" |
	   sort -u ) \
	<( cat <( find "${PROJECT}/${PACKAGE}"  -maxdepth 1 -type f -printf "%f\n" ) <( echo "$EXCLUDES" ) |
	   tr --squeeze "[:blank:]" "\n" |
	   sort -u  ) )

debug echo DEPRECATED_FILES="$DEPRECATED_FILES"

###################################################################
# compare what's in svn to what is in the osc working copy
if [ ${DO_DIFF} ]; then
    echo "You are about to update the following files in the osc working copy:"
    for f in $PACKAGE_FILES; do
        # ignore errors from diff.
	diff -Nu "${PROJECT}/${PACKAGE}/$f"  $f || true
    done;
    
    echo "You are about to remove the following files from the osc working copy:"
    for f in $DEPRECATED_FILES; do
	test ! -f "${PROJECT}/${PACKAGE}/$f" ||
	diff -Nu "${PROJECT}/${PACKAGE}/$f"  $f || true
    done
fi

if [ ${DO_ASK} ]; then
    echo "Update the local working copy?
  Interrupt (^c) to stop here.
  Hit RETURN to update the local working copy."
    read
fi

###################################################################
# update the osc working copy
if [ ${DO_UPDATE} ]; then
    for f in $PACKAGE_FILES; do
	if \
	    test -f $f && (
		! test -f ${PROJECT}/${PACKAGE}/$f ||
    		! cmp --quiet "$f" "${PROJECT}/${PACKAGE}/$f"
	    )
	then
	    cp -vdpP "$f" "${PROJECT}/${PACKAGE}/$f"
	fi
    done

    pushd ${PROJECT}/${PACKAGE}
    if [ "${DEPRECATED_FILES}" ] ; then rm -vf $DEPRECATED_FILES ; fi

    # update version information in the specfile unless in maintenance
    # mode
    for spf in ${SPECFILE}
    do
 	if [ ! ${MAINTENANCE} ]
 	then
 	    sed --in-place -e "s/^%define svnversion updated_by_script.*/%define svnversion ${FULL_VERSION}/" ${spf}
 	fi
    done
    popd
fi

###################################################################
# build the package and check in

pushd "${PROJECT}/${PACKAGE}"

if [ ${DO_BUILD} ]; then
    for spec in ${SPECFILE}
    do
	osc ${OSCOPTS} build ${BUILDOPTS} "${TARGET}" "${ARCH}" "${spec}"
	# TODO: somehow smartly deal with the results of different
	# builds in case of several spec files.  Currently the last
	# one survives, the other ones are lost.  Only relevant in
	# case of several spec files in one package
    done
fi


if [ ${DO_CHECKIN} ]; then
    if [ ${DO_ASK} ]; then
	echo "Check in to the build server?
  Interrupt (^c) to stop here.
  Hit RETURN to check in to the build server."
	    read
    fi
    osc ${OSCOPTS} ci
fi

popd
# EOF
