hooks: Refactor

The hook functions now all work in terms of local variables and argument
passing. They return error codes rather than exiting. Fixing issues
(updating copyright and correcting ifndef/define names) is now optional.
The "interactive" aspect of the pre-recieve hook has been removed and
placed into a new interactive-hook script that can be used for checking
without commiting. The pre-recieve-hook has been updated to include all
the newer checking added to pre-commit-hook.
This commit is contained in:
Will Bainbridge
2019-03-13 12:40:25 +00:00
parent fec55234b6
commit 0ce996ffd4
4 changed files with 501 additions and 427 deletions

View File

@ -2,7 +2,7 @@
# ========= |
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
# \\ / O peration | Website: https://openfoam.org
# \\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation
# \\ / A nd | Copyright (C) 2011-2019 OpenFOAM Foundation
# \\/ M anipulation |
#------------------------------------------------------------------------------
# License
@ -29,11 +29,9 @@
#
#------------------------------------------------------------------------------
headerSeparator="-----------------------------------"
echoIndent=" "
sourceBanner="(\
if [ -z "$sourceBanner" ]
then
sourceBanner="(\
/\*---------------------------------------------------------------------------\*\\\\|\
/\*--------------------------------\*- C\+\+ -\*----------------------------------\*\\\\|\
/\*---------------------------------\*- C -\*-----------------------------------\*\\\\)
@ -61,8 +59,11 @@ License
(.|
)*?
\\\\\*---------------------------------------------------------------------------\*/"
fi
scriptBanner="(\
if [ -z "$scriptBanner" ]
then
scriptBanner="(\
#------------------------------------------------------------------------------|\
#---------------------------------\*- sh -\*-------------------------------------|\
#----------------------------------\*-sh-\*--------------------------------------|\
@ -91,8 +92,11 @@ scriptBanner="(\
(.|
)*?
#------------------------------------------------------------------------------"
fi
dictBanner="(\
if [ -z "$dictBanner" ]
then
dictBanner="(\
/\*---------------------------------------------------------------------------\*\\\\|\
/\*--------------------------------\*- C\+\+ -\*----------------------------------\*\\\\)
========= \|
@ -102,374 +106,350 @@ dictBanner="(\
\\\\\\\\/ M anipulation \|(.|
)*?
\\\\\*---------------------------------------------------------------------------\*/"
fi
#-----------------------------------------------------------------------------
#
# report failure and exit
#
die()
gitIsRevision() # commit
{
echo "$hookName hook failure" 1>&2
echo $headerSeparator 1>&2
echo '' 1>&2
echo "$@" 1>&2
echo '' 1>&2
exit 1
git rev-parse --verify "$1" > /dev/null 2>&1
return $?
}
#
# report failure, listing bad files and exit
#
dieOnBadFiles()
scopeGit() # scope, cacheScope
{
if [ -n "$badFiles" ]
if [ "$1" == "--cached" ]
then
echo "$hookName hook failure" 1>&2
echo $headerSeparator 1>&2
echo "$@" 1>&2
echo '' 1>&2
echo "File(s):" 1>&2
echo "$badFiles" 1>&2
echo '' 1>&2
echo "$2"
elif gitIsRevision "$1"
then
echo "$1:"
else
echo "$(basename "$0"): $1 is not a valid scope"
exit 1
fi
}
#
# qualify 'git grep' to check cached value or from a specific commit
#
gitScopeGrep()
scopeGitGrep() # scope
{
if [ "$#" -gt 0 ]
scopeGit "$1" "--cached -- "
}
scopeGitShow() # scope
{
scopeGit "$1" ":"
}
reportStart() # startMessage
{
echo -n "$1 ... " 1>&2
}
reportEnd() # failMessage, errorCount, errorMessages...
{
local failMessage errorCount
failMessage=$1
shift
errorCount=$1
shift
if [ "$errorCount" -eq 0 ]
then
echo "$1:"
echo PASSED 1>&2
else
echo "--cached -- "
echo "FAILED with $errorCount errors:" 1>&2
echo "$failMessage" | sed "s/^/ /g" 1>&2
for errorMessage in "$@"
do
echo "$errorMessage" | sed "s/^/ /g" 1>&2
done
fi
}
#
# qualify 'git show' to check cached value or from a specific commit
#
gitScopeShow()
checkPattern() # scope, pattern, include, exclude, files...
{
if [ "$#" -gt 0 ]
local scope pattern include exclude lines errorCount=0
scope=$(scopeGitGrep "$1")
shift
pattern="$1"
shift
include="$1"
shift
exclude="$1"
shift
# Loop files, accumulating errors
for file in "$@"
do
case "$file" in
($exclude)
;;
($include)
if [ "$(file -b "$file")" != "data" ]
then
lines=$( \
eval git grep -E -hn $pattern $scope"$file" \
| sed -e "s/:.*//" \
| tr "\n" " " \
)
if [ -n "$lines" ]
then
echo "$file: Lines $lines"
((++errorCount))
fi
fi
;;
esac
done
return $errorCount
}
checkTabs() # scope, files...
{
reportStart "Check for tabs"
local scope pattern include exclude errorCount errorMessages
scope=$1
shift
pattern="-e $'\t'"
include="*"
exclude="*[Mm]akefile* | wmake/rules/* | bin/tools/gtagsrc | *.f* | *.v[cf]proj | *.pdf | *.png | *.html | *.gif | *.css | *.gz"
errorMessages="$(checkPattern "$scope" "$pattern" "$include" "$exclude" "$@")"
errorCount=$?
reportEnd "Remove <TAB> characters from the following files:" $errorCount "$errorMessages"
return $errorCount
}
checkLineEnds() # scope, files...
{
reportStart "Check for bad line endings"
local scope pattern include exclude errorCount errorMessages
scope=$1
shift
pattern="-e \"[ ]+$\" --or -e $'\r'"
include="*"
exclude="*.md"
errorMessages="$(checkPattern "$scope" "$pattern" "$include" "$exclude" "$@")"
errorCount=$?
reportEnd "Remove trailing whitespace and <CR> characters in the following files:" $errorCount "$errorMessages"
return $errorCount
}
checkLineLength() # scope, files...
{
reportStart "Check line lengths"
local scope pattern include exclude errorCount errorMessages
scope=$1
shift
pattern="-e \"^.{81,}\" --and --not -e \"^ *#\""
include="*.[CH]"
exclude=""
errorMessages="$(checkPattern "$scope" "$pattern" "$include" "$exclude" "$@")"
errorCount=$?
reportEnd "Limit lines to 80 characters in the following files:" $errorCount "$errorMessages"
return $errorCount
}
checkNonStandardCode() # scope, files...
{
reportStart "Check for non-standard code"
local scope pattern include exclude errorCount errorMessages
scope=$1
shift
pattern="-e \"> >\" --or -e \"NULL\""
include="*.[CH]"
exclude=""
errorMessages="$(checkPattern "$scope" "$pattern" "$include" "$exclude" "$@")"
errorCount=$?
reportEnd "$(cat << EOF
The following is considerd non-standard code:
1. Spaced ending of multi-level template parameters. For example,
List<List<scalar> >
should instead be written:
List<List<scalar>>
2. The 'NULL' macro. This should be replaced by 'nullptr'.
Remove these patterns from the following files:
EOF
)" $errorCount "$errorMessages"
return $errorCount
}
checkHeaderIfndefNames() # scope, fix, files...
{
reportStart "Check header file #ifndef/#define names"
local scopeGrep fix correctDefine currentDefine failMessage errorCount=0 errorMessages=()
scopeGrep=$(scopeGitGrep "$1")
shift
fix=$1
shift
for file in "$@"
do
case "$file" in
(*.H)
correctDefine=$(basename "$file" | sed 's/\./_/')
if git grep -q -e "#ifndef.*_H" --and --not -e "#ifndef.*[ _]$correctDefine" $scopeGrep"$file"
then
((++errorCount))
errorMessages+=("$file")
currentDefine=$(grep "#ifndef.*_H" "$file" | sed 's/#ifndef\s*//')
if $fix
then
sed -i -e "s/$currentDefine/$correctDefine/" "$file"
fi
fi
;;
esac
done
if $fix
then
echo "$1:"
failMessage="The following files have been automatically updated so that the
#ifndef/#define statements match the file name. Check and re-add them
before pushing:"
else
echo ":"
failMessage="
Revise the following files so that the #ifndef/#define statements match
the file name:"
fi
reportEnd "$failMessage" $errorCount "${errorMessages[@]}"
return $errorCount
}
#
# check for bad strings, characters, etc
#
checkIllegalCode()
checkBanner() # scope, files...
{
echo "$hookName: check bad strings/characters etc ..." 1>&2
reportStart "Check banners"
reBad="("$'\t'")"
msgBad="<TAB>"
local scopeGrep scopeShow fileName fileExtension errorCount=0 errorMessages=()
scope=$(gitScopeGrep $@)
scopeGrep=$(scopeGitGrep "$1")
scopeShow=$(scopeGitShow "$1")
shift
badFiles=$(
for f in $fileList
for file in "$@"
do
case "$f" in
# exclude potential makefiles
(*[Mm]akefile* | wmake/rules/* | bin/tools/gtagsrc | *.f* | *.v[cf]proj | *.pdf | *.png | *.html | *.gif | *.css | *.gz)
;;
(*)
fileType=`file -b $f`
if [ "$fileType" != "data" ]
then
# parse line numbers from grep output:
# <lineNr>: contents
lines=$(git grep -E -hn -e "$reBad" $scope"$f" |
sed -e 's@:.*@@' |
tr '\n' ' '
)
[ -n "$lines" ] && echo "$echoIndent$f -- lines: $lines"
fi
;;
esac
done
)
dieOnBadFiles "Remove/correct bad '$msgBad' references"
}
#
# limit line length to 80-columns
#
checkLineLength()
{
echo "$hookName: check line lengths ..." 1>&2
scope=$(gitScopeGrep $@)
badFiles=$(
for f in $fileList
do
# limit to *.[CH] files
case "$f" in
(*.[CH])
# parse line numbers from grep output:
# <lineNr>: contents
lines=$(git grep -hn -e '^.\{81,\}' $scope"$f" |
sed -e 's@:.*@@' |
tr '\n' ' '
)
[ -n "$lines" ] && echo "$echoIndent$f -- lines: $lines"
;;
esac
done
)
dieOnBadFiles "Limit code to 80 columns before pushing"
}
#
# limit line length to 80-columns, except C++ comment lines
#
checkLineLengthNonComments()
{
echo "$hookName: check line lengths ..." 1>&2
scope=$(gitScopeGrep $@)
badFiles=$(
for f in $fileList
do
# limit to *.[CH] files
case "$f" in
(*.[CH])
# parse line numbers from grep output:
# <lineNr>: contents
lines=$(git grep -hn -e '^.\{81,\}' \
--and --not -e '^ *//' \
$scope"$f" |
sed -e 's@:.*@@' |
tr '\n' ' '
)
[ -n "$lines" ] && echo "$echoIndent$f -- lines: $lines"
;;
esac
done
)
dieOnBadFiles "Limit code to 80 columns before pushing"
}
#
# limit line length to 80-columns, except #directive lines
#
checkLineLengthNonDirective()
{
echo "$hookName: check line lengths ..." 1>&2
scope=$(gitScopeGrep $@)
badFiles=$(
for f in $fileList
do
# limit to *.[CH] files
case "$f" in
(*.[CH])
# parse line numbers from grep output:
# <lineNr>: contents
lines=$(git grep -hn -e '^.\{81,\}' \
--and --not -e '^ *#' \
$scope"$f" |
sed -e 's@:.*@@' |
tr '\n' ' '
)
[ -n "$lines" ] && echo "$echoIndent$f -- lines: $lines"
;;
esac
done
)
dieOnBadFiles "Limit code to 80 columns before pushing"
}
#
# check for non-standard code patterns
#
checkNonStandardCodePatterns()
{
echo "$hookName: checking for non-standard code ..." 1>&2
scope=$(gitScopeGrep $@)
badFiles=$(
for f in $fileList
do
# limit to *.[CH] files
case "$f" in
(*.[CH])
# Directly report the incorrect markers
git grep -n --color -e '> >' \
--or -w -e 'NULL' \
$scope"$f"
;;
esac
done
)
dieOnBadFiles "$(cat<<MESSAGE
Please revise the files reported below for the following non-standard code:
1. Spaced ending of multi-level template parameters are not allowed, such as:
List<List<scalar> >
which instead should be:
List<List<scalar>>
2. The use of the 'NULL' macro should be replaced by 'nullptr'
$headerSeparator
MESSAGE
)"
}
#
# check that header files respect the policy of file names matching the
# #ifndef/#define bounds
#
checkHeaderIfndefNames()
{
echo "$hookName: check header files #ifndef/#define names ..." 1>&2
scope=$(gitScopeGrep $@)
badFiles=$(
for f in $fileList
do
# limit to *.H files
case "$f" in
(*.H)
fileName=$(basename $f)
correctMangledName=$(basename $f | sed 's=\.=_=')
if git grep -q -e "#ifndef.*_H" $scope"$f" && \
! git grep -q -e "#ifndef.*[ _]$correctMangledName" $scope"$f"
then
echo "Updated #ifndef/#define for: $f" 1>&2
echo $f
currentMangled=$(grep "#ifndef.*_H" $f | sed 's=#ifndef\s*==')
sed -i -e 's='$currentMangled'='$correctMangledName'=' $f
fi
;;
esac
done
)
dieOnBadFiles "Some header files were automatically updated to respect #ifndef naming convention; Please check these before pushing"
}
#
# check that the banners are correctly formatted
#
checkBanner()
{
echo "$hookName: check banner ..." 1>&2
scopeGrep=$(gitScopeGrep $@)
scopeShow=$(gitScopeShow $@)
badFiles=$(
for f in $fileList
do
fFile="${f##*/}"
[[ "$fFile" = *.* ]] && fExt="${fFile##*.}" || fExt=""
fileName=$(basename "$file")
fileExtension=${fileName##*.}
# Skip links
if [ -h "$f" ]
if [ -h "$file" ]
then
:
# Copyrighted source and script files
elif git grep -q -e "Copyright (C) [0-9-]\+ OpenFOAM Foundation" $scopeGrep"$f"
elif git grep -q -e "Copyright (C) [0-9-]\+ OpenFOAM Foundation" $scopeGrep"$file"
then
case "$fExt" in
(c|C|Cver|cxx|dox|H|h|l|L)
if ! git show $scopeShow"$f" | pcregrep -q -M "$sourceBanner"
then
echo $f
fi
;;
(''|awk|csh|gnuplot|sed|sh)
if ! git show $scopeShow"$f" | pcregrep -q -M "$scriptBanner"
then
echo $f
fi
;;
(*)
# Unknown extension
echo "Banner not checked for copyrighted file $f" 1>&2
;;
case "$fileExtension" in
(c|C|Cver|cxx|dox|H|h|l|L)
if ! git show $scopeShow"$file" | pcregrep -q -M "$sourceBanner"
then
((++errorCount))
errorMessages+=("$file")
fi
;;
(''|awk|csh|gnuplot|sed|sh)
if ! git show $scopeShow"$file" | pcregrep -q -M "$scriptBanner"
then
((++errorCount))
errorMessages+=("$file")
fi
;;
(*)
:
#((++errorCount))
#errorMessages+=("$file: Unknown extension")
;;
esac
# Versioned case files
elif git grep -q -e "Version: \(dev\|[0-9.]+\)" $scopeGrep"$f"
elif git grep -q -e "Version: \(dev\|[0-9.]+\)" $scopeGrep"$file"
then
if ! git show $scopeShow"$f" | pcregrep -q -M "$dictBanner"
if ! git show $scopeShow"$file" | pcregrep -q -M "$dictBanner"
then
echo $f
((++errorCount))
errorMessages+=("$file")
fi
# Unknown files
elif git grep -q -e "OpenFOAM: The Open Source CFD Toolbox" $scopeGrep"$f"
elif git grep -q -e "OpenFOAM: The Open Source CFD Toolbox" $scopeGrep"$file"
then
: #echo "Banner not checked for file $f" 1>&2
:
#((++errorCount))
#errorMessages+=("$file: Missing copyright or version")
fi
done
)
dieOnBadFiles "Fix banner formatting before pushing"
reportEnd "Correct the banner formating in the following files:" $errorCount "${errorMessages[@]}"
return $errorCount
}
#
# check that OpenFOAM Foundation copyright is current
#
checkCopyright()
checkCopyright() # scope, fix, files...
{
echo "$hookName: check copyright ..." 1>&2
reportStart "Check copyright"
scope=$(gitScopeGrep $@)
local scopeGrep fix year startYear endYear errorCount=0 errorMessages=() failMessage
scopeGrep=$(scopeGitGrep "$1")
shift
fix=$1
shift
year=$(date +%Y)
badFiles=$(
for f in $fileList
for file in "$@"
do
startYear=$(
git grep -h -e "Copyright.*OpenFOAM" $scope"$f" | \
head -n 1 | \
sed 's/[^0-9]*\([0-9]*\).*/\1/g'
)
endYear=$(
git grep -h -e "Copyright.*-.*OpenFOAM" $scope"$f" | \
head -n 1 | \
sed 's/[^-]*-\([0-9]*\).*/\1/g'
)
startYear=$( \
git grep -h -e "Copyright.*OpenFOAM" $scopeGrep"$file" \
| head -n 1 \
| sed 's/[^0-9]*\([0-9]*\).*/\1/g'
)
endYear=$( \
git grep -h -e "Copyright.*-.*OpenFOAM" $scopeGrep"$file" \
| head -n 1 \
| sed 's/[^-]*-\([0-9]*\).*/\1/g'
)
if [ "$startYear" != "" ]
then
@ -478,25 +458,89 @@ checkCopyright()
# Date is of type 2011-2012 OpenFOAM Foundation
if [ "$year" != "$endYear" ]
then
echo "Updated copyright for: $f" 1>&2
echo "$f"
sed -i -e "s/$startYear-$endYear OpenFOAM/$startYear-$year OpenFOAM/g" $f
((++errorCount))
errorMessages+=("$file")
if $fix
then
sed -i -e "s/$startYear-$endYear OpenFOAM/$startYear-$year OpenFOAM/g" "$file"
fi
fi
else
# Date is of type 2011 OpenFOAM Foundation
if [ "$year" != "$startYear" ]
then
echo "$f"
echo "Updated copyright for: $f" 1>&2
sed -i -e "s/$startYear OpenFOAM/$startYear-$year OpenFOAM/g" $f
((++errorCount))
errorMessages+=("$file")
if $fix
then
sed -i -e "s/$startYear OpenFOAM/$startYear-$year OpenFOAM/g" "$file"
fi
fi
fi
fi
done
)
dieOnBadFiles "Some copyright dates were automatically updated; Please check these before pushing"
if $fix
then
failMessage="The following files have had their copyright dates automatically
updated. Check and re-add them before pushing:"
else
failMessage="Update the copyright dates in the following files:"
fi
reportEnd "$failMessage" $errorCount "${errorMessages[@]}"
return $errorCount
}
checkAllNoCopyright() # scope, fix, files...
{
local scope fix returnCode=0
scope="$1"
shift
fix="$1"
shift
# Check for tabs
checkTabs "$scope" "$@" || returnCode=1
# Check line endings
checkLineEnds "$scope" "$@" || returnCode=1
# Ensure code line lengths do not exceed 80 columns
checkLineLength "$scope" "$@" || returnCode=1
# Check for non-standard code patterns
checkNonStandardCode "$scope" "$@" || returnCode=1
# Check if #ifndef/#define bounds are named correctly
checkHeaderIfndefNames "$scope" "$fix" "$@" || returnCode=1
# Check banner
checkBanner "$scope" "$@" || returnCode=1
return $returnCode
}
checkAll() # scope, fix, files...
{
local scope fix returnCode=0
scope="$1"
shift
fix="$1"
shift
# Check all except copyright
checkAllNoCopyright "$scope" "$fix" "$@" || returnCode=1
# Check copyright
checkCopyright "$scope" "$fix" "$@" || returnCode=1
return $returnCode
}
#------------------------------------------------------------------------------