diff options
Diffstat (limited to 'sbe/build/tools/hooks/verify-commit')
-rwxr-xr-x | sbe/build/tools/hooks/verify-commit | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/sbe/build/tools/hooks/verify-commit b/sbe/build/tools/hooks/verify-commit new file mode 100755 index 00000000..c29ba9ac --- /dev/null +++ b/sbe/build/tools/hooks/verify-commit @@ -0,0 +1,359 @@ +#!/usr/bin/perl +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: sbe/build/tools/hooks/verify-commit $ +# +# OpenPOWER sbe Project +# +# Contributors Listed Below - COPYRIGHT 2016 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG + +use strict; + +my $issueFound = 0; +my $errorFound = 0; + +my $projectName = $ENV{'PROJECT_NAME'}; +# Relative path of import tree from project root +my $importPrefix = $ENV{'IMPORT_REL_PATH'}."/"; + +verifyPatchSet(); # Verify the patch contents. +verifyCommitMsg(); # Verify the commit message. + +# Finish out the divider. +if ($issueFound) +{ + print "------------------------------------------------------------\n"; +} + +# Return a bad RC if we found an error. Let warnings pass. +exit ($errorFound ? -1 : 0); + + +########################### Subroutines ################################ + +# @sub verifyPatchSet +# +# Extract the contents (lines changed) from the patch set and verify. +# +sub verifyPatchSet +{ + # git-diff options: + # * Diff against the previous commit (HEAD~1) + # * Filter only added and modified files (AM). + # * Show only the lines changed, with no context (U0). + # Grep only the lines marked with "+" (instead of "-") to find just the + # actions done by this patchset and not the content removed. + open PATCH_CONTENTS, "git diff HEAD~1 --diff-filter=AM -U0 | ". + "grep -e \"^+\" -e \"^@@.*+[0-9]\\+\" |"; + + my %fileContents = (); + + my $lastFile = ""; + my $fileLines = (); + my $lineCount = 0; + while (my $line = <PATCH_CONTENTS>) + { + chomp $line; + + # Line starting with "+++ b/path/to/file" indicate a new file. + if ($line =~ m/^\+\+\+ b\/(.*)/) + { + # Save previous file into the map. + if ($lastFile ne "") + { + $fileContents{$lastFile} = $fileLines; + $fileLines = (); + $lineCount = 0; + } + $lastFile = $1; + } + # Lines starting with "@@" indicates a seek in the file, so update + # line numbers. + elsif ($line =~ m/^@@.*\+([0-9]+)/) + { + $lineCount = $1 - 1; + } + else + { + $line =~ s/^\+//; # filter off the leading + symbol. + $lineCount++; + push @{$fileLines}, [$line, $lineCount]; + } + } + if ($lastFile ne "") # Save last file into the map. + { + $fileContents{$lastFile} = $fileLines; + $fileLines = (); + } + + # Verify each line of each file. + foreach my $file (sort keys %fileContents) + { + foreach my $line (@{$fileContents{$file}}) + { + verifyFileLine($file, @{$line}[0], @{$line}[1]); + } + } +} + +# @sub verifyFileLine +# +# Checks a particular line of the file for the following issues: +# * Warning: Lines longer than 80 characters, except in trace statement. +# * Warning: Trailing whitespace. +# * Warning: Tab characters outside of makefiles. +# * Warning: TODO or FIXME type tag without a corresponding RTC number. +# * Warning: NOMERGE tag found. +# +sub verifyFileLine +{ + my ($file,$line,$count) = @_; + + # Check if file was mirrored + my $mirror = 0; + if ($file =~ m/^$importPrefix/) + { + $mirror = 1; + } + + # Check line length. + if (length($line) > 80) + { + # Allow trace statements to slide. + if (($line =~ m/TRAC[DSFU]/) || + ($line =~m/TS_FAIL/) || + ($line =~m/printk/) || + ($line =~m/displayf/) || + ($line =~ m/FAPI_(INF|IMP|ERR|DBG|SCAN)/)) + { + } + else + { + warning($file,$line,$count, + (sprintf "Length is more than 80 characters (%d).", + length($line)) + ); + } + } + + # Check trailing whitespace. + if ($line =~ m/\s$/) + { + warning($file,$line,$count, + "Trailing whitespace found."); + } + + # Check tabs. + if ($line =~ m/\t/) + { + # Makefiles are ok (require tabs). + if (not (($file =~ m/makefile/) || ($file =~ m/\.mk/))) + { + warning($file,$line,$count, + "Tab character found."); + } + } + + # Check "TODO" or "FIXME" type comments. + if (($line =~ m/TODO/i) || ($line =~ m/FIXME/i)) + { + if ( (not ($line =~ m/RTC[\s:]\s*[0-9]+/)) && + (not ($line =~ m/CQ[\s:]\s*[A-Z][A-Z][0-9]+/))) + { + warning($file,$line,$count, + "TODO/FIXME tag without corresponding RTC or CQ number."); + } + } + + # Check "NOMERGE" type comment. + if ($line =~ m/NOMERGE/i) + { + warning($file,$line,$count, + "NOMERGE tag found."); + } + + # Check for "Confidential", unless it's a mirrored commit + if ($line =~ m/Confidential/i && $projectName =~ m/HostBoot/i && !$mirror) + { + unless (($file =~ m/verify-commit/) || + ($file =~ m/addCopyright.pl/)) + { + error($file,$line,$count, + "File contains 'Confidential'."); + } + } +} + +# @sub verifyCommitMsg +# +# Looks at the commit message to verify the following items: +# * Topic is exactly 1 line long. +# * Lines are less than 80 characters. +# * No trailing whitespace. +# * Tags, such as 'RTC:', are only found in the footer. +# * Untagged lines are not found in the footer. +# * RTC tag is formatted correctly. +# * Warning for lacking RTC tag. +# +sub verifyCommitMsg +{ + open COMMIT_CONTENTS, "git log -n1 --pretty=format:%B |"; + my $lineCount = 0; + my $rtcTag = ""; + my $cqTag = ""; + my $taggedLine = ""; + my $untaggedLine = ""; + + while (my $line = <COMMIT_CONTENTS>) + { + $lineCount++; + chomp $line; + + # Check line length over 80 characters. + if (length($line) > 80) + { + error("Commit Message",$line,$lineCount, + (sprintf "Length is more than 80 characters (%d).", + length($line)) + ); + } + + # Check trailing whitespace. + if ($line =~ m/[^\s]+\s$/) + { + error("Commit Message",$line,$lineCount, + "Trailing whitespace found."); + } + + # Blank line indicates a new "section". + if ($line =~ m/^$/) + { + # Check for tags outside of the footer. + # (new section implies previous section was not a footer) + if ("" ne $taggedLine) + { + error("Commit Message",$taggedLine,$lineCount, + "Tagged line found outside commit-msg footer."); + } + + $rtcTag = ""; + $cqTag = ""; + $untaggedLine = ""; + $taggedLine = ""; + } + else + { + # Check for multi-line topic. + if ($lineCount == 2) + { + error("Commit Message",$line,$lineCount, + "Topic must be only one line long."); + } + } + + # Verify format of RTC message. + if ($line =~ m/^\s*RTC:\s*[0-9]+(.*)/) + { + if ("" ne $rtcTag) + { + error("Commit Message",$line,$lineCount, + "Duplicate RTC tag found."); + } + + $rtcTag = $line; + if ("" ne $1) + { + error("Commit Message",$line,$lineCount, + (sprintf "RTC tag format incorrect (%s).", $1)); + } + } + + if ($line =~ m/^\s*CQ:\s*[A-Z][A-Z][0-9]+(.*)/) + { + if ("" ne $cqTag) + { + error("Commit Message",$line,$lineCount, + "Duplicate CQ tag found."); + } + + $cqTag = $line; + if ("" ne $1) + { + error("Commit Message",$line,$lineCount, + (sprintf "CQ tag format incorrect (%s).", $1)); + } + } + + # Identify if this is a tagged line or a non-tagged line and store + # away. + if ($line =~ m/^\s*[A-Za-z0-9\-_]+:[^:]/) + { + # We allow lines that look like tags in the topic like... + # "FOO: Adding support for BAR." + if ($lineCount > 1) + { + $taggedLine = $line; + } + } + else + { + $untaggedLine = $line; + } + } + + # Warn for missing RTC tag. + if (("" eq $rtcTag) && ("" eq $cqTag)) + { + warning("Commit Message","<end-of-file>",$lineCount, + "Neither RTC nor CQ tag found."); + } + + # Error for a mix of tag / untagged in the last section (ie. untagged + # lines in the footer). + if (("" ne $untaggedLine) && ("" ne $taggedLine)) + { + error("Commit Message",$untaggedLine,$lineCount, + "Untagged line found in footer."); + } +} + +sub warning +{ + my ($file, $line, $count, $statement) = @_; + print "------------------------------------------------------------\n"; + print "WARNING: $statement\n"; + print " $file:$count\n"; + print " $line\n"; + + $issueFound = 1; +} + +sub error +{ + my ($file, $line, $count, $statement) = @_; + print "------------------------------------------------------------\n"; + print "ERROR: $statement\n"; + print " $file:$count\n"; + print " $line\n"; + + $issueFound = 1; + $errorFound = 1; +} + |