| #!/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 |