blob: 1f67bc82f5ef694670c5e6c023c52700aeea1ceb [file] [log] [blame]
#!/bin/bash
#
# SVN Syntax Check Hook Script
# Copyright (c) 2007, Lucas Nealan <lucas@sizzo.org>, Facebook Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# --------------------------------------------------------------------
#
# This script provides language independent syntax checking
# functionality intended to be invoked from a subversion pre-commit
# hook.
#
# Invocation: /path/to/syntax-check $1 $2
# or: source syntax-check
#
# Requires bash 3.x or higher.
#
FPATTERN="\.\(php\|phpt\)$"
FLANG="PHP"
SYNTAX_CMD="php"
SYNTAX_ARGS="-l"
# address to email notifications of syntax errors
NOTIFY_SYNTAX="user@domain.tld"
# adderss to notify for syntax script failures
NOTIFY_ERROR="user@domain.tld"
# log of syntax errors, must be writable by svn server users
SYNTAX_LOG="/tmp/syntax.log"
BYPASSPW="change_this_syntax_bypass_password_please"
[ -z "$REPOS" ] && REPOS="$1"
[ -z "$TXN" ] && TXN="$2"
[ -z "$MODE" ] && if [ -n "$3" ]; then MODE="$3"; else MODE="-t"; fi
[ -z "$SVNLOOK" ] && SVNLOOK=svnlook
[ -z "$LOG" ] && LOG=`$SVNLOOK log $MODE "$TXN" "$REPOS"`
[ -z "$DIFF" ] && DIFF=`$SVNLOOK diff $MODE "$TXN" "$REPOS"`
[ -z "$AUTHOR" ] && AUTHOR=`$SVNLOOK author $MODE "$TXN" "$REPOS"`
[ -z "$CHANGEDFILES" ] && CHANGEDFILES=`$SVNLOOK changed $MODE "$TXN" "$REPOS"`
[ -n "$SYNTAXENABLED" ] && SYNTAXENABLED="1"
syntaxclean() {
[ -d $1 ] && rm -rf $1
}
syntaxexit() {
echo $1 >> /dev/stderr
echo $1 >> $SYNTAX_LOG
# save working dir and debug files
if [ -d $WORKING ]; then
[ -n "$DIFF" ] && echo $"$DIFF" > $WORKING/patch
[ -n "$LOG" ] && echo $"$LOG" > $WORKING/svnlog
[ -n "$CHANGED" ] && echo $"$CHANGED" > $WORKING/changed
mv $WORKING $WORKING.failed
fi
[ -n "$NOTIFY_ERROR" ] && echo "$1 ($WORKING.failed)" | mail -s "syntax commit failed" $NOTIFY_ERROR
# clean working dir if specified
[ -n $2 ] && syntaxclean $2
exit 1
}
function strlpad() {
STR="$1"
CHAR=$2
LEN=$3
L=${#STR}
D=$((LEN-L))
echo "$STR""`printf '%'$D's'| tr \ \"$CHAR\"`"
}
function errormessage() {
echo
HEADER=`strlpad "Error" "-" 76`
echo "|--$HEADER|" >&2
SPACES="`strlpad '' ' ' 78`"
echo "|$SPACES|" >&2
ERROR=`strlpad "$1" " " 77`
echo "| $ERROR|" >&2
echo "|$SPACES|" >&2
LINE=`strlpad "" "-" 78`
echo "|$LINE|" >&2
echo
}
if [ "$SYNTAXENABLED" == "1" ]; then
# allow selective bypass of syntax check for commits
[[ "$LOG" =~ "$BYPASSPW" ]] && return;
# get changed file list and count
NUMMATCHCHANGED=0
if [ -n "$CHANGEDFILES" ]; then
MATCHCHANGED=`echo $"$CHANGEDFILES" | grep "$FPATTERN"`
[ -n "$MATCHCHANGED" ] && NUMMATCHCHANGED=`echo $"$MATCHCHANGED" | wc -l`
fi
# make sure matched files were changed
if [ $NUMMATCHCHANGED -gt 0 ]; then
# create temporary working directory
WORKING=/tmp/$(basename $0).$$
[ -d $WORKING ] && rm -rf $WORKING
mkdir $WORKING || syntaxexit "failed to create temp dir for syntax check: $WORKING"
cd $WORKING
# export changed files (no dirs) from local repo (speed)
IFS=$'\n'
for LINE in $MATCHCHANGED; do
IFS=' '
WORDS=($LINE)
FSTATUS=${WORDS[0]}
FNAME=${WORDS[1]}
# only export modified and deleted files. new files wont exist in repo yet
if [ "$FSTATUS" == "U" ] || [ "$FSTATUS" == "UU" ] || [ "$FSTATUS" == "A" ]; then
TMPFNAME=${FNAME//\//.}
$SVNLOOK cat $MODE "$TXN" "$REPOS" $FNAME > $TMPFNAME
file `which $SYNTAX_CMD` || syntaxexit "unablet to find systax command binary: $SYNTAX_CMD"
SYNTAXERROR=`$SYNTAX_CMD $SYNTAX_ARGS $TMPFNAME 2> $WORKING/$TMPFNAME.STDERR`
SYNTAXRETURN=$?
[ -s "$WORKING/$TMPFNAME.STDERR" ] && SYNTAXWARNING=`cat $WORKING/$TMPFNAME.STDERR`
if [ "$SYNTAXRETURN" -ne 0 ] || [ -n "$SYNTAXWARNING" ]; then
[ -n "$SYNTAXWARNING" ] && SYNTAXERROR=$SYNTAXWARNING
# cleanup HTML out of PHP parse error so a human can read it
if [ "$FLANG" == "PHP" ]; then
SYNTAXERROR=`echo $SYNTAXERROR | sed -e 's/<[^<]*>//g' | cut -d',' -f 2`
SYNTAXERROR=`echo $SYNTAXERROR | sed -e 's/\(on line [0-9]* \)/\1\n/g'`
fi
# sloppy email notification
ETMP=$WORKING/sloppy.txt
echo "$FLANG Syntax Error: $SYNTAXERROR" > $ETMP
echo >> $ETMP
echo Log: $LOG >> $ETMP
echo >> $ETMP
echo $"$DIFF" >> $ETMP
cat $ETMP | mail -s "SVN SYNTAX ERROR: $AUTHOR" $NOTIFY_SYNTAX
rm -f $ETMP
echo "$AUTHOR: $FLANG Syntax Error: $SYNTAXERROR" >> $SYNTAX_LOG
errormessage "$FLANG Syntax Error: $SYNTAXERROR"
syntaxclean $WORKING
exit 1
fi
fi
done
# exit within a loop only sets the return value of the loop itself, check this to exit
[ $? -ne 0 ] && exit 1
fi
syntaxclean $WORKING
fi