bin/tools: Fixes to git hooks

The functions shared by pre-commit and pre-receive hooks have been
consolidated into bin/tools/HookFunctions in order to reduce
duplication. The #ifndef/#define and copyright checks have also been
fixed to operate on the staged changes, not the saved file.
This commit is contained in:
Will Bainbridge
2018-06-18 15:52:01 +01:00
parent b15724888c
commit 91c3430acc
3 changed files with 390 additions and 510 deletions

354
bin/tools/HookFunctions Executable file
View File

@ -0,0 +1,354 @@
#---------------------------------*- sh -*-------------------------------------
# ========= |
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
# \\ / O peration |
# \\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation
# \\/ M anipulation |
#------------------------------------------------------------------------------
# License
# This file is part of OpenFOAM.
#
# OpenFOAM 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 3 of the License, or
# (at your option) any later version.
#
# OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
#
# Script
# HookFunctions
#
# Description
# Functions for automatically checking code pre commit and receive
#
#------------------------------------------------------------------------------
headerSeparator="-----------------------------------"
echoIndent=" "
#-----------------------------------------------------------------------------
#
# report failure and exit
#
die()
{
echo "$hookName hook failure" 1>&2
echo $headerSeparator 1>&2
echo '' 1>&2
echo "$@" 1>&2
echo '' 1>&2
exit 1
}
#
# report failure, listing bad files and exit
#
dieOnBadFiles()
{
if [ -n "$badFiles" ]
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
exit 1
fi
}
#
# qualify 'git grep' to check cached value or from a specific commit
#
gitScope()
{
if [ "$#" -gt 0 ]
then
echo "$1:"
else
echo "--cached -- "
fi
}
#
# check for bad strings, characters, etc
#
checkIllegalCode()
{
echo "$hookName: check bad strings/characters etc ..." 1>&2
reBad="("$'\t'")"
msgBad="<TAB>"
scope=$(gitScope $@)
badFiles=$(
for f in $fileList
do
case "$f" in
# exclude potential makefiles
(*[Mm]akefile* | wmake/rules/* | *.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=$(gitScope $@)
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=$(gitScope $@)
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=$(gitScope $@)
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=$(gitScope $@)
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=$(gitScope $@)
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 OpenFOAM Foundation copyright is current
#
checkCopyright()
{
echo "$hookName: check copyright ..." 1>&2
scope=$(gitScope $@)
year=$(date +%Y)
badFiles=$(
for f in $fileList
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'
)
if [ "$startYear" != "" ]
then
if [ "$endYear" != "" ]
then
# 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
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
fi
fi
fi
done
)
dieOnBadFiles "Some copyright dates were automatically updated; Please check these before pushing"
}
#------------------------------------------------------------------------------

View File

@ -3,7 +3,7 @@
# ========= | # ========= |
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox # \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
# \\ / O peration | # \\ / O peration |
# \\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation # \\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation
# \\/ M anipulation | # \\/ M anipulation |
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# License # License
@ -38,8 +38,12 @@
# Hook receives: empty # Hook receives: empty
# #
# Checks for # Checks for
# - trailing whitespace and non-standard line endings
# - illegal code, e.g. <TAB> # - illegal code, e.g. <TAB>
# - columns greater than 80 for *.[CH] files # - columns greater than 80 for *.[CH] files
# - non-standard code patterns
# - missmatched header #ifndef/#define names
# - incorrect copyright statements
# #
# Note # Note
# Using "git commit --no-verify" it is possible to override the hook. # Using "git commit --no-verify" it is possible to override the hook.
@ -49,351 +53,51 @@
# #
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
hookName="pre-commit" . bin/tools/HookFunctions || exit 1
headerSeparator="-----------------------------------"
hookName="pre-commit"
die()
{
echo "$hookName hook failure" 1>&2
echo $headerSeparator 1>&2
echo '' 1>&2
echo "$@" 1>&2
echo '' 1>&2
exit 1
}
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# Check content that will be added by this commit. # Check content that will be added by this commit
#
# Clear
unset fileList
unset badFiles
# Get the commit to test against
if git rev-parse --verify HEAD > /dev/null 2>&1 if git rev-parse --verify HEAD > /dev/null 2>&1
then then
against=HEAD against=HEAD
else else
# Initial commit: diff against an empty tree object # Git's "empty" object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 against=$(git hash-object -t tree /dev/null)
fi fi
# called manually with arguments for the files/directories to be tested? # Get the list of files to check
if [ "$#" -gt 0 ] if [ "$#" -gt 0 ]
then then
# Called with arguments for the files/directories to be tested
case "$1" in case "$1" in
-h | -help) -h | -help)
die "interactive usage: supply list of files/directories to check" die "interactive usage: supply list of files/directories to check"
;; ;;
esac esac
# obtain list of all specified files/directories
fileList=$(git ls-files -- $@ 2>/dev/null) fileList=$(git ls-files -- $@ 2>/dev/null)
else else
# list of all files to be committed # Called with out arguments to test staged changes
fileList=$(git diff-index --cached --name-only $against --) fileList=$(git diff-index --cached --name-only $against --)
fi fi
# # If no files have changed then the checks are not needed. This usage can
# no files changed: can skip all the checks # correspond to a 'git commit --amend'
# this usage can correspond to a 'git commit --amend'
#
[ -n "$fileList" ] || exit 0 [ -n "$fileList" ] || exit 0
unset badFiles
# join list of files with this amount of space
Indent=" "
#
# report bad files and die if there are any
#
dieOnBadFiles()
{
if [ -n "$badFiles" ]
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
exit 1
fi
}
#
# qualify 'git grep' to check cached value or from a specific commit
#
gitScope()
{
if [ "$#" -gt 0 ]
then
echo "$1:"
else
echo "--cached -- "
fi
}
#
# check for bad strings, characters, etc
#
checkIllegalCode()
{
echo "$hookName: check bad strings/characters etc ..." 1>&2
reBad="("$'\t'")"
msgBad="<TAB>"
scope=$(gitScope $@)
badFiles=$(
for f in $fileList
do
case "$f" in
# exclude potential makefiles
(*[Mm]akefile* | wmake/rules/* | *.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 "$Indent$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=$(gitScope $@)
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 "$Indent$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=$(gitScope $@)
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 "$Indent$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=$(gitScope $@)
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 "$Indent$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=$(gitScope $@)
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
badFiles=$(
for f in $fileList
do
# limit to *.H files
case "$f" in
(*.H)
fileName=$(basename $f)
correctMangledName=$(basename $f | sed 's=\.=_=')
if grep -q "#ifndef.*_H" $f
then
if ! grep "#ifndef.*_H" $f | grep -q $correctMangledName
then
echo "$(grep -H "#ifndef" $f | sed 's=#=\n\t#=') --> $correctMangledName"
currentMangled=$(grep "#ifndef.*_H" $f | sed 's=#ifndef\s*==')
sed -i -e 's='$currentMangled'='$correctMangledName'=' $f
fi
fi
;;
esac
done
)
dieOnBadFiles "Some header files were automatically updated to respect #ifndef naming convention; Please check these before pushing"
}
#
# check that OpenFOAM Foundation copyright is current
#
checkCopyright()
{
year=$(date +%Y)
echo "$hookName: check copyright ..." 1>&2
badFiles=$(
for f in $fileList
do
startYear=`grep -m 1 "Copyright.*OpenFOAM" $f | sed 's/[^0-9]*\([0-9]*\).*/\1/g'`
endYear=`grep -m 1 "Copyright.*-.*OpenFOAM" $f | sed 's/[^-]*-\([0-9]*\).*/\1/g'`
#echo "startYear=$startYear endYear=$endYear"
if [ "$startYear" != "" ]
then
if [ "$endYear" != "" ]
then
# 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
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
fi
fi
fi
done
)
dieOnBadFiles "Some copyright dates were automatically updated; Please check these before pushing"
}
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Main code : do all checks # Main code. Do all checks.
# #
# builtin whitespace check to avoid trailing space, including CR-LF endings # builtin whitespace check to avoid trailing space, including CR-LF endings
@ -411,7 +115,9 @@ checkNonStandardCodePatterns
# check if #ifndef/#define bounds are named correctly # check if #ifndef/#define bounds are named correctly
checkHeaderIfndefNames checkHeaderIfndefNames
# check copyright
checkCopyright checkCopyright
exit 0 exit 0
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------

View File

@ -3,7 +3,7 @@
# ========= | # ========= |
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox # \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
# \\ / O peration | # \\ / O peration |
# \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation # \\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation
# \\/ M anipulation | # \\/ M anipulation |
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# License # License
@ -42,192 +42,14 @@
# - columns greater than 80 for *.[CH] files # - columns greater than 80 for *.[CH] files
# #
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
. bin/tools/HookFunctions || exit 1
hookName="pre-receive" hookName="pre-receive"
die()
{
echo "$hookName hook failure" 1>&2
echo '-----------------------------------' 1>&2
echo '' 1>&2
echo "$@" 1>&2
echo '' 1>&2
exit 1
}
#-----------------------------------------------------------------------------
unset fileList
unset badFiles
# join list of files with this amount of space
Indent=" "
#
# report bad files and die if there are any
#
dieOnBadFiles()
{
if [ -n "$badFiles" ]
then
echo "$hookName hook failure" 1>&2
echo '-----------------------------------' 1>&2
echo "$@" 1>&2
echo '' 1>&2
echo "File(s):" 1>&2
echo "$badFiles" 1>&2
echo '' 1>&2
exit 1
fi
}
#
# qualify 'git grep' to check cached value or from a specific commit
#
gitScope()
{
if [ "$#" -gt 0 ]
then
echo "$1:"
else
echo "--cached -- "
fi
}
#
# check for bad strings, characters, etc
#
checkIllegalCode()
{
echo "$hookName: check bad strings/characters etc ..." 1>&2
reBad="("$'\t'"|"$'\r\n'")"
msgBad="<TAB> or DOS-line-endings"
scope=$(gitScope $@)
badFiles=$(
for f in $fileList
do
case "$f" in
# exclude potential makefiles
(*[Mm]akefile* | wmake/rules/*)
;;
(*)
# 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 "$Indent$f -- lines: $lines"
;;
esac
done
)
dieOnBadFiles "Remove/correct bad '$msgBad' references"
}
#
# limit line length to 80-columns
#
checkLineLength()
{
echo "$hookName: check line lengths ..." 1>&2
scope=$(gitScope $@)
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 "$Indent$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=$(gitScope $@)
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 "$Indent$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=$(gitScope $@)
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 "$Indent$f -- lines: $lines"
;;
esac
done
)
dieOnBadFiles "Limit code to 80 columns before pushing"
}
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Main code : do all checks # Main code. Do all checks.
# #
while read oldSHA1 newSHA1 refName while read oldSHA1 newSHA1 refName
@ -236,20 +58,18 @@ do
if [ "$newSHA1" = 0 ] if [ "$newSHA1" = 0 ]
then then
# ref to be deleted # Ref to be deleted
continue continue
elif [ "$oldSHA1" = 0 ] elif [ "$oldSHA1" = 0 ]
then then
# ref to be created # Ref to be created
rawFileList=$(git diff-tree --root $newSHA1) rawFileList=$(git diff-tree --root $newSHA1)
else else
# normal changes # Normal changes
rawFileList=$(git diff --name-only $oldSHA1..$newSHA1) rawFileList=$(git diff --name-only $oldSHA1..$newSHA1)
fi fi
# # No files have changed, so the checks are not necessary
# no files changed: can skip all the checks
#
[ -n "$rawFileList" ] || continue [ -n "$rawFileList" ] || continue
fileList=$( fileList=$(
@ -267,6 +87,6 @@ do
done done
exit 0 exit 0
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------