openfoam/bin/tools/pre-commit-hook
2023-05-10 18:23:13 +02:00

405 lines
9.5 KiB
Bash
Executable File

#!/bin/bash
#---------------------------------*- sh -*-------------------------------------
# ========= |
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
# \\ / O peration |
# \\ / A nd | www.openfoam.com
# \\/ M anipulation |
#------------------------------------------------------------------------------
# Copyright (C) 2011-2016 OpenFOAM Foundation
# Copyright (C) 2016 OpenCFD Ltd.
#------------------------------------------------------------------------------
# License
# This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
#
# Script
# pre-commit-hook
#
# Description
# pre-commit hook for git.
# Copy or link this file as ".git/hooks/pre-commit"
#
# Eg,
# (
# cd $WM_PROJECT_DIR/.git/hooks &&
# ln -sf ../../bin/tools/pre-commit-hook pre-commit
# )
#
# Hook receives: empty
#
# Checks for
# - illegal code, e.g. <TAB>
# - columns greater than 80 for *.[CH] files
#
# Note
# Using "git commit --no-verify" it is possible to override the hook.
#
# By supplying arguments to the hook, it can also be used to manually
# test the specified files/directories for standards conformance.
#
#------------------------------------------------------------------------------
hookName="pre-commit"
headerSeparator="-----------------------------------"
die()
{
echo "$hookName hook failure" 1>&2
echo $headerSeparator 1>&2
echo '' 1>&2
echo "$@" 1>&2
echo '' 1>&2
exit 1
}
# Automatically upgrade copyrights in files.
# Disabled by default since some changes (eg, spelling) do not automatically
# imply an update copyright.
optCopyright=false
# Run all tests (do not exit on first failure)
optAll=false
#-----------------------------------------------------------------------------
# Check content that will be added by this commit.
if git rev-parse --verify HEAD > /dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# called manually with arguments for the files/directories to be tested?
if [ "$#" -gt 0 ]
then
while [ "$#" -gt 0 ]
do
case "$1" in
-h | -help)
die "interactive usage: supply list of files/directories to check"
;;
-copy)
echo "$hookName: adjust copyright enabled" 1>&2
optCopyright=true
shift
;;
-all)
echo "$hookName: do all tests (no premature exit)" 1>&2
optAll=true
shift
;;
*)
break
;;
esac
done
# obtain list of all specified files/directories
fileList=$(git ls-files -- $@ 2>/dev/null)
else
# list of all files to be committed
fileList=$(git diff-index --cached --name-only $against --)
fi
#
# no files changed: can skip all the checks
# this usage can correspond to a 'git commit --amend'
#
[ -n "$fileList" ] || exit 0
unset badFiles
# join list of files with this amount of space
Indent=" "
exitCode=0
#
# 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
exitCode=1
if [ "$optAll" = true ]
then
return 0 # Continue to the next test
else
exit $exitCode
fi
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 '> >' -e '\bNULL\b' \
$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 copyright date is current
#
checkCopyright()
{
year=$(date +%Y)
echo "$hookName: check copyright ..." 1>&2
badFiles=$(
for f in $fileList
do
startYear=`grep "Copyright.*OpenCFD" $f | sed 's/[^0-9]*\([0-9]*\).*/\1/g'`
endYear=`grep "Copyright.*-.*OpenCFD" $f | sed 's/[^-]*-\([0-9]*\).*/\1/g'`
#echo "startYear=$startYear endYear=$endYear"
if [ -n "$startYear" ]
then
if [ -n "$endYear" ]
then
# Date is of type 2011-2012 OpenCFD Ltd.
if [ "$year" != "$endYear" ]
then
echo "Updated copyright for: $f" 1>&2
echo "$f"
sed -i -e "s/$startYear-$endYear OpenCFD/$startYear-$year OpenCFD/g" $f
fi
else
# Date is of type 2011 OpenCFD Ltd.
if [ "$year" != "$startYear" ]
then
echo "$f"
echo "Updated copyright for: $f" 1>&2
sed -i -e "s/$startYear OpenCFD/$startYear-$year OpenCFD/g" $f
fi
fi
fi
done
)
dieOnBadFiles "Some copyright dates were automatically updated; Please check these before pushing"
}
#------------------------------------------------------------------------------
# Main code : do all checks
#
# builtin whitespace check to avoid trailing space, including CR-LF endings
bad=$(git diff-index --cached --check $against --) || die "$bad"
# check for illegal code, e.g. <TAB>, etc
checkIllegalCode
# ensure code conforms to 80 columns max
checkLineLengthNonDirective
# check for non-standard code patterns
checkNonStandardCodePatterns
# Stop now if there were any errors
[ "$exitCode" = 0 ] || exit $exitCode
# check copyright date (normally disabled)
[ "$optCopyright" = true ] && checkCopyright
exit 0
#------------------------------------------------------------------------------