#!/bin/bash

# $Id: validate.sh,v 1.5 2006/11/15 10:16:29 dennisvd Exp $

# Validate.sh  run validation test for PoC contributions

# Usage: validate <tar-or-zip-archive>

# Download and unpack

# This script will work with a local copy of the archive,
# so there is no need to download every time.

export PATH=/bin:/usr/bin


die()
{
    echo $@
    exec 4<&-
    if [ -r COMPLIANCE ]; then
	echo -e "\nCompliance report:\n"
	cat COMPLIANCE
    fi
    exit 1
}


if [ $# -ne 1 ]; then
    echo "Usage: $0 <tar-or-zip-archive>"
    echo
    echo "Either give a URL or a local file path"
    exit 1
fi

# First check well-formedness of the package name

# Sneaky bastards that inject newlines in their package names should
# be flogged to death. Smash newlines, tabs into spaces; sanitise.
url=$(echo "$1" | tr "[[:space:]]" " " )
if echo "$1" | grep -q '[[:space:]]'; then
    die "Error: spaces, tabs and newlines are not allowed in argument."
fi

# Check that the file is not a dot file
if basename "$1" | grep -q "^\."; then
    echo "Error: the distribution name "
    echo
    basename "$1"
    echo
    echo "starts with a period."
    echo
    exit 1
fi

# distname is filename without extension
# Extension is hard to define, as periods are supposed to
# separate parts of the version number.
# A period followed by a letter marks the start of the 'extension'.
distname=$(basename "$1" | sed -e 's/\.[a-zA-Z].*$//')
if [ -z "$distname" ]; then
    die "Error: distribution name is empty."
fi

# The distname is considered a concatenation of packagename
# and package version string, separated by a hyphen ('-').
# The rule is that the first hyphen that is followed by a digit
# is the separator.
#
# Three steps:
# 1. replace the separating '-' by a newline
# 2. escape dangerous symbols ($, " and \)
# 3. turn each part into a variable assignment
#

# Check if -<digit> is part of the pattern at all.
if ! echo "$distname" | grep -q -- '-[0-9]' ; then
    echo "Error: distribution name"
    echo 
    echo "        $distname"
    echo
    echo "Has no distinguishable version number."
    echo "Please use the format <package>-<version>,"
    echo "where <version> starts with a number."
    echo
    exit 1
fi

tmpdir=$(mktemp -d -p . validate.XXXXXX)
if [ $? -ne 0 ]; then
    echo "Error: could not create a temporary directory here."
    echo "Run this script someplace where you have write"
    echo "permissions"
fi

exec 4>$tmpdir/COMPLIANCE

# Compliance logging function.
# Usage: set testname to the name of the test before calling.
# One argument:
# 1: OK (compliant)
# 0: non-compliant
compliant=1
function comply()
{
    if [ "$1" -eq 1 ]; then
	printf "%-70s %s\n" "$testname:" OK | sed -e 's/   / \. /g' >&4
    else
	printf "%-70s %s\n" "$testname:" FAIL | sed -e 's/   / \. /g' >&4
	compliant=0
    fi
}

pkgname=$(echo "$distname" | sed -e 's/-[0-9].*$//')
pkgversion=$(echo "$distname" | sed -n -e \
's/-\([0-9]\)/ \1/ 
s/^.* //
p')

# 
# if [ -n "$nameversionsplit" ]; then
#     pkgname=$(echo "$nameversionsplit" | grep
#     eval "$nameversionsplit"
# else
#     die "Error: malformed distribution name."
# fi

echo "The package name is:    $pkgname"
echo "The package version is: $pkgversion"

# check for non-empty package name
if [ -z "$pkgname" ]; then
    die "Error: empy package name"
fi

# check that the name is all lowercase
testname="Package name is lowercase"
if echo $pkgname | grep -q '[A-Z]'; then
    echo "Warning: package name "
    echo
    echo "        " $pkgname 
    echo
    echo "contains upper case letter(s)."
    echo "This is against the recommendations for packaging."
    echo "Please consider using only lowercase letters."
    echo
    comply  0
else
    comply  1
fi

# check that the name consists only of letters, digits, -, _ and +
testname="Package name consist only of letters, digits, '-', '_' and '+'"
if ! echo $pkgname | grep -q -E '^[[:alnum:]_+-]*$'; then
    echo "Warning: package name "
    echo
    echo "        " $pkgname 
    echo
    echo "contains funny characters."
    echo "This is against the recommendations for packaging."
    echo "Please consider using only letters, digits, '-', '_', and '+'."
    echo
    comply  0
else
    comply  1
fi

# check that the package name is no longer than 20 characters
testname="Package name is short (at most 20 characters)"
if (( $(echo "$pkgname" | wc -m) > 21 )); then
    echo "Warning: package name"
    echo
    echo "        $pkgname"
    echo 
    echo "is too long. The recommended maximum is 20 characters."
    echo
    comply  0
else
    comply  1
fi

# check that the version number is like x.y.z
# Also allow just x, and x.y.
# These are all numbers, of course!
# Let's go easy and allow mixtures of digits and periods,
# with up to two periods.
testname="Package version is numbers and dots"
if ! echo $pkgversion | grep -q -E '^[.0-9]*$' ; then
    echo "Warning: the package version"
    echo
    echo "        $pkgversion"
    echo
    echo "contains nondigits."
    echo "This is against the recommendations for packaging."
    echo "Please consider sticking to numbers only."
    echo
    comply  0
else
    comply  1
    testname="Package number is like x.y.z"
    if ! echo $pkgversion | grep -q -E '^[0-9]+(\.[0-9]+){0,2}$'; then
	echo "Warning: the package version"
	echo
	echo "        $pkgversion"
	echo
	echo "is not well-formed. Please consider sticking to the"
	echo "recommended format for version numbers, x.y.z"
	echo "(where each of x, y and z is a nonnegative number)"
	echo
	comply  0
    else
	comply  1
    fi
fi

# Check the contents of the archive
# Does $1 look like a URL?

if echo "$1" | grep -q -E '^(https?|ftp)://'; then
    archive=$(basename "$1")
    wget -O "$archive" "$1" || die "Could not fetch archive."
else
    if [ ! -r "$1" ]; then
	echo "$1 is neither a URL nor a local file."
    else 
	archive="$1"
    fi
fi


echo "Going to extract the archive in $tmpdir"


# does it look like an archive?
magic=$(file -L --brief "$archive")

case "$magic" in
    (bzip2*) 
    bzcat "$archive" | (cd "$tmpdir" ; tar x)
    ;;
    (gzip*|compress*) 
    zcat "$archive" | (cd "$tmpdir" ; tar x)
    ;;
    (Zip*) 
    unzip -d "$tmpdir" "$archive"
    ;;
    (*tar*)
    (cd "$tmpdir" ; tar xf "../$archive")
    ;;
    (*) die "Don't know what kind of file this is supposed to be"
    ;;
esac

cd "$tmpdir"

tmpdir=$(pwd)

# Test that a directory has been created that is exactly like
# the distribution name.
testname="Package contains single directory by the same name"
if [ ! -d $distname ]; then
    echo "Warning: the archive does not contain a directory named"
    echo
    echo $distname
    echo
    echo "This is against the recommendations for packaging."
    echo
    comply 0
else
    comply 1
fi

topfiles=$(ls | wc -l)
distdir=$(find . -type d -maxdepth 1 -mindepth 1 | head -1)
testname="Package extracts in a subdirectory"
if [ $topfiles -gt 2 ]; then
    echo "Warning: the archive extracts into the current directory."
    echo "This is against recommendations,"
    echo
    comply 0
else
    if [ -z "$distdir" ]; then
	echo "Warning: the archive does not contain a single" \
	    "toplevel directory."
	comply 0
    else
	cd "$distdir" || die "Could not change directory to $distdir"
	comply 1
    fi
fi

# Now we are supposedly in the toplevel directory of the archive
testname="Contact information"
READMEFILE=
for i in README{,.txt} MAINTAINER{,.txt}; do
    if [ -r $i ]; then
	READMEFILE=$i
	break
    fi
done

if [ -z "$READMEFILE" ]; then
    echo "Warning: Missing contact information file"
    echo
    echo "    Supplying a file with contact information is"
    echo "    a recommendation. Such a file should be named"
    echo "    either README or MAINTAINER (with an optional .txt extension)"
    echo
    comply 0
else
    comply 1
    testname="README contains a contact email address"
    if ! grep -q -i -E '\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b' \
	$READMEFILE ; then
	echo "Warning: missing email address in the README."
	echo
	echo "    Supplying contact information such as an email address"
	echo "    in the README file is recommended."
	echo
	comply 0
    else
	comply 1
    fi
fi

# configure and make
testname="package contains a configure script"
if [ ! -r configure ]; then
    echo "Failure: missing configure script."
    echo
    echo "    Supplying a configure script is a REQUIREMENT."
    echo
    comply 0
    die
else
    comply 1
fi

prefix=/opt/vl-e/contrib/"$distname"
bindir=/opt/vl-e/contrib/"$distname"/bin
sbindir=/opt/vl-e/contrib/"$distname"/sbin
libdir=/opt/vl-e/contrib/"$distname"/lib
datadir=/opt/vl-e/contrib/"$distname"/share
mandir=/opt/vl-e/contrib/"$distname"/share/man
infodir=/opt/vl-e/contrib/"$distname"/share/info
includedir=/opt/vl-e/contrib/"$distname"/include
docdir=/opt/vl-e/contrib/doc/"$distname"
sysconfdir=/etc/opt/vl-e/contrib/"$distname"
localstatedir=/var/opt/vl-e/contrib/"$distname"

testname="The package configures"

sh ./configure --prefix=$prefix \
    --bindir=$bindir \
    --sbindir=$sbindir \
    --sysconfdir=$sysconfdir \
    --datadir=$datadir \
    --includedir=$includedir \
    --libdir=$libdir \
    --localstatedir=$localstatedir \
    --mandir=$mandir \
    --infodir=$infodir

if [ $? -ne 0 ]; then
    echo "Failure: configure step"
    comply 0
    die
else
    comply 1
fi

testname="There is a Makefile (after the configure steps)"
if [ ! -r Makefile ]; then
    comply 0
    die "Failure: no Makefile."
else
    comply 1
fi

testname="the package builds"
make
if [ $? -ne 0 ]; then
    comply 0
    die "Failure: make step"
else
    comply 1
fi

# make check

testname="The tests suite passes"
make check 
if [ $? -ne 0 ]; then
    comply 0
    die "Failure: make check"
else
    comply 1
fi

# make install

buildroot=$(mktemp -d -p "$tmpdir" buildroot.XXXXXX) || \
    die "Could not create temporary directory"

testname="The package installs"
make DESTDIR="${buildroot}" docdir=$docdir install

if [ $? -ne 0 ]; then
    comply 0
    die "Failure: make install"
else
    comply 1
fi

# Check the installed directories

# tar and remove the 'good' directories
olddir=`pwd`

cd $buildroot
tar cfz ../$pkgname-$pkgversion-packaged.tar.gz \
    ${prefix#/} \
    ${docdir#/} \
    ${sysconfdir#/} \
    ${localstatedir#/} 2> /dev/null

rm -rf ./$prefix \
    ./$docdir \
    ./$sysconfdir \
    ./$localstatedir

# clean up the skeleton. Remove the directories that are
# supposed/allowed to exist, but leave any files and other
# directories.    
rmdir -p opt/vl-e/contrib/doc 2> /dev/null
rmdir -p opt/vl-e/contrib 2> /dev/null
rmdir -p etc/opt/vl-e/contrib 2> /dev/null
rmdir -p var/opt/vl-e/contrib 2> /dev/null

# check for leftovers
testname="Files are installed in proper locations"
if (( $(find . | wc -l) > 1 )); then
    echo "Failure: files installed in illegal locations:"
    find .
    comply 0
else
    comply 1
fi

cd "$olddir"

cd "$tmpdir"

echo
echo "Installation package is $pkgname-$pkgversion-packaged.tar.gz"
echo

echo "Installation successful."
echo

exec 4<&-

if (( $compliant )); then
    echo "Congratulations; your package is found to be compliant!"
else
    echo "There were some compliance violations:"
    cat COMPLIANCE
fi

