#!/usr/bin/bash

# gp_release
# This is part of the Mer git-packaging suite

. ${GP_LIB:=/usr/share/gitpkg}/gp_common

git_tag_exists() { git show-ref --tags --quiet --verify -- "refs/tags/$1"; }

update_changes(){ # release oldpatchtag patchtag
    release=$1
    tag1=$2
    tag2=$3
    changes=${name}.changes
    newchanges=$(mktemp changes_XXXXX)
    date +"* %a %b %d %Y ${GIT_AUTHOR_NAME:-Your Name (Set \$GIT_AUTHOR_NAME)} <${GIT_AUTHOR_EMAIL:-Your Email Set \$GIT_AUTHOR_EMAIL}> - $release" > $newchanges
    cat <<EOF >> $newchanges
- Please enter a changelog message

#### Begin gitlog summary. This line and lines up to the next changelog entry will be removed.
# The following lines contain a [summary] entry.
EOF
    git log $tag1..$tag2 -- | egrep "^[[:space:]]*\[.*\]" | sed 's/\s*\(.*\)/- \1/' >> $newchanges
    echo >> $newchanges
    echo "#### Begin gitlog raw. This line and lines up to the next changelog entry will be removed.." >> $newchanges
    echo >> $newchanges
    git log $tag1..$tag2 -- >> $newchanges
    echo "#### this line will be removed" >> $newchanges
    cat $changes 2>/dev/null >> $newchanges
    cp $newchanges $newchanges.pre-edit
    ${EDITOR:-vi} $newchanges || fatal "EDITOR not set and vi not available"
    if diff --report-identical-files $newchanges $newchanges.pre-edit > /dev/null; then
	rm -f $newchanges.pre-edit $newchanges~ $newchanges
	fatal "No changes entered, aborting"
    fi

    sed -i '/^#### Begin gitlog/,/^#### this line will/d' $newchanges
    mv $newchanges $changes || fatal "Can't update changes file"
    # Tidy up emacs backup and old files
    rm -f $newchanges.pre-edit $newchanges~
}

usage() {
    cat <<EOF
    $ME [--rel[=<version-release>]] [--ver=<tag>]  [--edit] [--git-dir=<path>]

       Used to create a new release or prepare for an intermediate
       build. It modifies the _src, .changes, .yaml and .spec files.

       Version: and Release: values and patch lines will be updated in
       the yaml/spec. A changelog entry is prompted for (using git log
       as a base) and the git repo will be tagged.

       Normal usage is to update git; possibly to a new upstream or
       local release point, apply or rebase any patches and run as
       $ME --ver/--rel

       An alternative use is to *not* specify --ver or --rel but to
       use --git-dir; this will create a ready-to-build set of
       patches/tarballs (FIXME: it has the unwanted side-effect of
       changing the checkout state of <path>)

       --rel is used to create a new incremental release of a package
       when all that is changed is a patch to upstream code or
       packaging. It does not change the 'version' of a package or the
       main tag the package is built from.

       --ver is used to create a new version of a package (ie the
       <release> value is '1'). Usually this means there is a new
       upstream code/tag. If the upstream tag is not a simple version
       (eg it has text or underscores: 'vX_Y_Z') then the extended
       form of the --rel arg can be used to override the <version> and
       <release> values. If the provided value does not exist the tag
       is made at HEAD.


       --rel[=<version-release>]

         If --rel is used then a new package release will be created.

         If <version-release> is not specified then it is guessed from
         the packaging and uses perls string increment on
         <release>. This often works :)

         src HEAD when the command is run will be tagged as
         $DISTRO-<version-release> and packaging as
         pkg-$DISTRO-<version-release>. (Usually git checkout -f
         master would be run before running $ME --rel)

       --ver=<basetag>

         If --ver is provided then it identifies the new base tag from
         which patches are based and from where the tarball is
         built. If --rel=<version>-<release> is not given then
         <version> is assumed to be <basetag> and <release> is assumed to
         be "1". A $DISTRO-master branch will be made if not present.
         This is used for a new upstream release.

       --edit

         If --edit is provided then the final commit/tag step on the
         packaging branch is not made so that the spec/yaml can be
         edited manually. The commit/tag commands are printed for
         reference.

       If --rel is not given then the packaging branch pkg-$DISTRO is
       checked out, the spec/yaml is updated by appending
       .gitcount.sha1 to the Release: value (this tries to ensure rpm
       release values increment in a manner that allow rpm upgrades to
       work).
       ADDITIONALLY: a normal gp_mkpkg is used to prepare the tarball
       and patches when --rel is not given.

       --git-dir=<dir>
         The --git-dir option is ideal for working with a local OBS
         checkout and doing local OBS builds. It makes most sense when
         --rel is not specified
         Internally it sets GIT_DIR to <dir> and GIT_WORK_TREE to cwd

       $DISTRO is set either in ~/.gitpkg, the GITPKG_DISTRO
       environment variable or using --distro=<DISTRO>. If not
       specified 'mer' is used.

       If $PKG_DIR is set it is used instead of rpm/
       If $PKG_FILE is set it is used instead of the first matching yaml/spec file

EOF
}

do_release() {
    verrel_regex="^[^-]+-[^-]+$"
    #  or  "^[a-zA-Z0-9.~_]+-[a-zA-Z0-9.~_]+$" ?

    while [[ $1 ]]; do
	case $1 in
	    --ver=*)
		newver=${1#*=}
		# if a plain --rel is given aswell (ie without
		# =ver-rel) that's ambiguous since --ver implies a
		# new version
		# so override to 'basetag' and we'll set ver=$newver and rel=1
		get_verrel=newver
		shift;;
	    --rel )
		# Now if --ver was given, don't override from packaging
		get_verrel=${get_verrel:-packaging}
		shift;;
	    --rel=* )
		verrel=${1#*=}
		if [[ $verrel =~ $verrel_regex ]]; then
		    ver=${verrel%-*}
		    rel=${verrel##*-}
		else
		    fatal "The --rel= option must provide <ver>-<rel>"
		fi
		# if $verrel is "" then we guess it later when the packaging is present
		shift ;;
	    --edit )
		manual_edit=true
		shift ;;
	    --git-dir=*)
		gitdir=${1#*=}
		if ! git  --git-dir $gitdir rev-parse --git-dir > /dev/null 2>&1; then
		    # not a git dir - try appending .git
		    if git --git-dir $gitdir/.git rev-parse --git-dir > /dev/null 2>&1; then
			gitdir=$gitdir/.git
		    else
			fatal "The --git-dir option must point to a git repo. \n" \
                            "$gitdir nor $gitdir/.git are a git directory"
		    fi
		fi
		export GIT_WORK_TREE=$(pwd)
		export GIT_DIR=$gitdir
		export GIT_INDEX_FILE=$(mktemp /tmp/gp.index_XXXXX)
		git read-tree --empty
		shift ;;
	    -V)
		_V=1
		_MKPKG_OPTS="-V"
		shift ;;
	    *-h*) usage;
		exit 0;;
	    * )
		echo "Error $1 not recognised"
		usage
		exit 1;;
	esac
    done

    git rev-parse --git-dir > /dev/null 2>&1 || 
	fatal "This is not a git repository; use $ME in a gitpkg git repo or use --git-dir."

    # store current git postion as we'll make patches to here from the
    # basetag. If they turn out to be the same ... no patches
    patchtag_sha1=$(git rev-parse HEAD)

    trap cleanup EXIT
    
    # Make a tag if none exists
    if [[ $newver ]] && ! git_tag_exists $newver; then
	git_tag_cleanly $newver
    fi

    get_pkgfile

    name=$(get_field_from_packaging Name)

    # Get ver-rel and increment it if we need to
    case $get_verrel in
	packaging )
	    ver=$(get_field_from_packaging Version)
	    rel=$(get_field_from_packaging Release)
	    oldrel=$rel
	    rel=$(perl -e"\$r='$rel';print ++\$r;")
	    log "Automagically guessed $ver-$oldrel -> $ver-$rel"
	    verrel=$ver-$rel
	    if ! [[ $rel ]] || [[ $rel == "1" ]]; then
		fatal "The value guessed for Release: is '$rel' and this is probably wrong.\n" \
                    "Please speciyf an exact value for --rel=\n" \
		    "Currently Version: $ver and Release: $oldrel"
	    fi
	    ;;
	newver )
	    # if we have a newver and no ver/rel then set them:
	    if ! [[ $verrel ]]; then
		ver=$newver
		rel=1
		verrel=$ver-$rel
	    fi
	    ;;
    esac

    # Update from _src
    IFS=: read type tarball basetag oldpatchtag rest < $pkg_dir/_src

    # TODO: verify tarball basetag oldpatchtag are sane
    # basetag should be ${DISTRO}-$ver
    # patchtag should be ${DISTRO}-$ver-$rel

    # If we're not given a --ver or a --rel then this is a trial
    # build and we just use a sha1. Otherwise make a tag
    if [[ $verrel ]]; then
	patchtag=${DISTRO}-$verrel
	if ! git_tag_exists $patchtag; then
	    git_tag_cleanly $patchtag $patchtag_sha1
	fi
    else
	patchtag=$patchtag_sha1
    fi

    # If --ver was used to set a new <basetag> then do so
    if [[ $newver ]]; then
	oldbase=$basetag
	basetag=$newver
    fi

    # Find the yaml or spec (or abort)
    get_pkgfile
    if [[ ! $pkgfile ]]; then
	fatal "No spec or yaml files found"
    fi

    # if basetag and patchtag have the same sha1 then there are no
    # patches to package.
    if [[ $type == git ]]; then
	tarball=$name-$ver.tar.gz
    fi
    if [[ $(git rev-parse $basetag 2>/dev/null) == $(git rev-parse $patchtag 2>/dev/null) ]]; then
	srcfile="${type}:${tarball}:${basetag}"
    else
	srcfile="${type}:${tarball}:${basetag}:${patchtag}"
	if ! git_branch_exists $DISTRO-master; then
	    # There are patches so we make mer-master branch and point
	    # it here
	    log "Reset $DISTRO-master branch to start at $patchtag"
	    git branch -f $DISTRO-master $patchtag
	fi
    fi

    # Update the version/release, the .changes and the list of patches
    # in the packaging
    if [[ $verrel ]]; then
	cd $pkg_dir
	# If this is a newver then it's a new upstream release: give a
	# set of changes from one ver to the next
	if [[ $newver ]]; then
	    update_changes $verrel $oldbase $newver
	else
	    update_changes $verrel $oldpatchtag $patchtag_sha1
	fi
	# Now update spec/yaml and _src
	echo $srcfile > _src
	sed -i -e"s/Version:.*/Version: $ver/" $pkgfile
	sed -i -e"s/Release:.*/Release: $rel/" $pkgfile

        # All done, use gp_mkpkg to update patch lists in yaml/spec etc
	gp_mkpkg --no-tarball --no-checkout $_MKPKG_OPTS

        # and commit
	if [[ $manual_edit ]]; then
	    git add -u .
	    cat <<EOF

GUIDE: The packaging has been prepared. You may now manually edit the packaging.

When you are done, commit and tag the changes:

cd $pkg_dir
git commit -am"Release $patchtag"
git tag pkg-$patchtag
EOF
	else
	    git commit -am"Release $patchtag"
	    git_tag_cleanly pkg-$patchtag
	fi
    else
	echo $srcfile > $pkg_dir/_src
	sha1=$(git rev-parse --short $patchtag_sha1)
	gitcount=$(count_commits ${basetag} ${patchtag})
	cd $pkg_dir
	sed -i -re"s/Release:\s*(\S*)/Release: \1.$gitcount.$sha1/" $pkgfile
	# Get version from packaging and then build dummy release with it.
	get_info_from_packaging
	release="$ver-1.$gitcount.$sha1"
	# Now add a dummy changelog entry
	changes=${name}.changes
	newchanges=$(mktemp changes_XXXXX)
	date +"* %a %b %d %Y ${GIT_AUTHOR_NAME:-Your Name (Set \$GIT_AUTHOR_NAME)} <${GIT_AUTHOR_EMAIL:-Your Email Set \$GIT_AUTHOR_EMAIL}> - $release" > $newchanges
	echo -e "- git commits from $oldpatchtag to $sha1" >> $newchanges
	git log ${oldpatchtag}..$patchtag_sha1 --oneline -- | sed -e 's/^/   /' >> $newchanges
	echo >> $newchanges
	cat $changes >> $newchanges
	mv $newchanges $changes || fatal "Can't update changes file"

        # All done, use gp_mkpkg to update patch lists in yaml/spec etc
	gp_mkpkg --no-checkout $_MKPKG_OPTS

	cat <<EOF
The tarball and patches have been prepared for building.
EOF
    fi
}

do_release $*
# Don't try to undo all tags/changes
trap - EXIT
