Himanshu Himanshu - 7 days ago 4
Bash Question

SVN pre-commit hook logic

I'm adding a logic in my svn pre-commit hook to check if there is QA(in upper case starting with space) in commit message then commit should fail. But its not working. Kindly assist me how to write it properly.

REPOS="$1"
TXN="$2"

# Make sure that the log message contains some text.
SVNLOOK=/usr/bin/svnlook

LOGMSG=$($SVNLOOK log -t "$TXN" "$REPOS")

# check if any comment has supplied by the commiter
if [ -z "$LOGMSG" ]; then
echo "Your commit was blocked because it have no comments." 1>&2
exit 1
fi

#check minimum size of text
if [ ${#LOGMSG} -lt 15 ]; then
echo "Your Commit was blocked because the comments does not meet minimum length requirements (15 letters)." 1>&2
exit 1
fi

# get TaskID by regex
TaskID=$(expr "$LOGMSG" : '\([#][0-9]\{1,9\}[:][" "]\)[A-Za-z0-9]*')

# Check if task id was found.
if [ -z "$TaskID" ]; then

echo "" 1>&2
echo "No Task id found in log message \"$LOGMSG\"" 1>&2
echo "" 1>&2
echo "The TaskID must be the first item on the first line of the log message." 1>&2
echo "" 1>&2
echo "Proper TaskID format--> #123- 'Your commit message' " 1>&2
exit 1
fi

#Check that QA should not be present in log message.

QA=$(expr "$LOGMSG" : '\(*[" "][QA][" "]\)')
if [ "$QA" == "QA" ]; then
echo "" 1>&2
echo "Your log message \"$LOGMSG\" must not contain QA in upper case." 1>&2
echo "" 1>&2
exit 1
fi

Answer

The regex is incorrect:

  • \( starts a capturing group in expr, but you don't need a capturing group for your task
  • When * follows a \( in a pattern, it tries to match a literal *
  • [QA] matches a single character, which can be Q or A
  • The pattern of expr must match from the start of the string

As it is, the regex doesn't correspond to your requirement.

Even if the above points are fixed, a pattern QA, "QA" with spaces around it, will not match commit messages like this:

  • "Fix the build of QA"
  • "Broken in QA, temporarily"
  • ... and so on...

That is, instead of "QA" with spaces around, you probably want to match QA with word boundaries around. This is easy to do using grep -w QA.

As you clarified in a comment, you really want a space before the "Q". In that case the -w flag of grep is not suitable, because that requires a word boundary at both sides of patterns. There is another way to match word boundaries, using \< for word start and \> for word end. So to have a space in front of "Q", and a word boundary after "A", you can write QA\>, like this:

if grep -q ' QA\>' <<< "$LOGMSG"; then
    echo
    echo "Your log message \"$LOGMSG\" must not contain QA in upper case."
    echo
    exit 1
fi 1>&2

Notice some other improvements:

  • Instead of redirecting to stderr every single echo, you can redirect the entire if statement
  • Instead of echo "" you can write simply echo
  • Instead of storing the result of a command in a temporary variable, you can write conditionals on the exit code of commands