#!/bin/bash

# This hook is run for the jhbuild module only, and checks to see if the
# modulesets being committed conform to the schema. This aims to prevent
# people accidentally breaking buildbots and continuous integration by
# breaking the moduleset XML with typos.
#
# Note that modulesets are validated against the DTD from the same git
# revision to allow for the DTDs to change over time (as long as the
# modulesets are updated in lockstep).
#
# TODO: This currently doesn't validate all modulesets if the DTD is
# updated. This might be useful (although DTD updates are more likely to
# be reviewed than moduleset updates), but would be tricky to implement
# while still allowing the common commit model:
#  1. Commit updates to the DTD (and parsing code) as one commit.
#  2. Update all modulesets as a second commit.

BINDIR=/home/admin/bin/git

GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)

# Use the directory name with .git stripped as a short identifier
absdir=$(cd $GIT_DIR && pwd)
projectshort=$(basename ${absdir%.git})

check_moduleset() {
    rev=$1
    path=$2
    mode=$3

    case "$path" in
        *.moduleset)
            ;;
        *)
            return
            ;;
    esac

    basename=`basename $path`

    # Extract the DTD file from the current version of the repository,
    # so that we're always validating modulesets against a similarly-versioned DTD
    dtd_file=`mktemp pre-receive-check-jhbuild-modulesets.XXXXXX`
    git cat-file blob "$rev:modulesets/moduleset.dtd" > $dtd_file

    # Parse the moduleset and check for errors
    result=`git cat-file blob "$rev:$path" | xmllint --noout --dtdvalid $dtd_file -`
    success=$?

    # Tidy up
    rm $dtd_file > /dev/null 2>&1

    if [ $success -gt 0 ]; then
        cat <<EOF >&2
---
The following moduleset (.modules) file appears to be invalid.$branch_message

$path

The results of the validation follow. Please correct the errors on the line numbers mentioned and try to push again.

$result

To check this locally before attempting to push again, you can use the following command:

make check-modulesets

After making fixes, modify your commit to include them, by doing:

git add $basename
git commit --amend

If you have any further problems or questions, please contact the GNOME Desktop Development mailing list <desktop-devel-list@gnome.org>. Thank you.
---
EOF
        exit 1
    fi
}

check_jhbuild_modulesets() {
    oldrev=$1
    newrev=$2
    refname=$3

    # Only run on the jhbuild module
    if [ "$projectshort" != "jhbuild" ] ; then
        exit 0
    fi

    branchname=${refname#refs/heads/}
    if [ "$branchname" = "$refname" ] ; then
        # not a branch update
        return 0
    fi

    branch_message=
    if [ "x$branchname" != "master" ] ; then
        branch_message=" (When updating branch '$branchname'.)"
    fi

    if expr $newrev : "^0\+$" > /dev/null 2>&1; then
        # Branch deletion, nothing to check
        return 0
    fi

    if ! git diff-tree --name-only -r $oldrev $newrev | grep -q -v '\(LINGUAS\|ChangeLog\|.po\)$' ; then
        # Looks like something a translator would do, exempt it from the check
        return 0
    fi

    if expr $oldrev : "^0\+$" > /dev/null 2>&1; then
        # Branch creation: validate all moduleset files
        git ls-tree -r $newrev | (
            while read mode objtype sha path ; do
                if [ $objtype = blob ] ; then
                    check_moduleset $newrev $path $mode
                fi
            done
        )
    else
        # Branch update
        git diff-tree -r $oldrev $newrev | (
            while read srcmode destmode srcsha destsha status srcpath destpath ; do
                if [ $status = 'D' ] ; then
                    continue # deleted
                fi

                # destpath only present for copies/renames
                if [ x"$destpath" = x ] ; then
                    destpath=$srcpath
                fi

                # Strip colon from the source mode
                srcmode=${srcmode#:}

                check_moduleset $newrev $destpath $destmode
            done
        )
    fi
}

if [ $# = 3 ] ; then
    check_jhbuild_modulesets $@ || exit 1
else
    while read oldrev newrev refname; do
        check_jhbuild_modulesets $oldrev $newrev $refname || exit 1
    done
fi
