diff options
author | Richard J. Knight <rjknight@us.ibm.com> | 2017-10-13 15:49:25 -0500 |
---|---|---|
committer | Richard J. Knight <rjknight@us.ibm.com> | 2017-10-17 15:05:22 -0500 |
commit | 41e7df45e64f623499b8d286a4012e6e7d24eaf6 (patch) | |
tree | e7a54e4950d2610c3fddeaf2b74b53b424315bf1 /tools/verify-commit | |
download | talos-hcode-41e7df45e64f623499b8d286a4012e6e7d24eaf6.tar.gz talos-hcode-41e7df45e64f623499b8d286a4012e6e7d24eaf6.zip |
Initial hcode commit
Change-Id: I2c6c9b05c6afbd7732f472ea9cf049d00c5cad45
Diffstat (limited to 'tools/verify-commit')
-rwxr-xr-x | tools/verify-commit | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/tools/verify-commit b/tools/verify-commit new file mode 100755 index 00000000..4a167981 --- /dev/null +++ b/tools/verify-commit @@ -0,0 +1,390 @@ +#!/usr/bin/perl +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: tools/verify-commit $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] 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; +use Getopt::Long qw(:config pass_through); + +my $issueFound = 0; +my $errorFound = 0; +my $warningCount = 0; +my $showAll = 0; + +use constant MAX_WARNINGS => 5; +use constant MAX_CODE_LINE_LENGTH => 120; + +my $projectName = $ENV{'PROJECT_NAME'}; + +GetOptions("show-all" => \$showAll); + +verifyPatchSet(); # Verify the patch contents. +verifyCommitMsg(); # Verify the commit message. + +# Finish out the divider. +if ($issueFound) +{ + print "------------------------------------------------------------\n"; + my $hiddenWarnings = $warningCount - MAX_WARNINGS; + if ($hiddenWarnings > 0 && !$showAll) + { + print " $hiddenWarnings Warning(s) Hidden\n"; + print " Run 'verify-commit --show-all'\n"; + 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 MAX_CODE_LINE_LENGTH 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 line length. + if (length($line) > MAX_CODE_LINE_LENGTH) + { + # Allow trace statements and .C/.H files to slide. + # Astyle should format .C/.H files, jenkins job will confirm astyle was + # run. Done to avoid scenario where astyle cannot split at MAX_CODE_LINE_LENGTH chars. + if (($line =~ m/TRAC[DSFU]/) || + ($line =~m/TS_FAIL/) || + ($line =~m/printk/) || + ($line =~m/displayf/) || + ($line =~ m/FAPI_(INF|IMP|ERR|DBG|SCAN)/) || + ($line =~ m/print/) || + ($file =~ m/\.[cChH]/)) + { + } + else + { + warning($file,$line,$count, + (sprintf "Length is more than %d characters (%d).", + MAX_CODE_LINE_LENGTH, 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/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."); + } + +} + +# @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 $changeId = ""; + 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)); + } + } + + if ($line =~ m/^\s*Change-Id:\s*[I][\w]+(.*)/) + { + if ("" ne $changeId) + { + error("Commit Message",$line,$lineCount, + "Mulitple Change-Id's found."); + } + + $changeId = $line; + if ("" ne $1) + { + error("Commit Message",$line,$lineCount, + (sprintf "Change-Id 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." + # Unless the Change-Id is in the topic + if ($lineCount > 1 || ($line =~ m/Change-Id/)) + { + $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 missing Change-Id. + if ("" eq $changeId) + { + error("Commit Message","<end-of-file>",$lineCount, + "Change-Id not 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) = @_; + + if ($warningCount < MAX_WARNINGS || $showAll) + { + print "------------------------------------------------------------\n"; + print "WARNING: $statement\n"; + print " $file:$count\n"; + print " $line\n"; + } + + $issueFound = 1; + $warningCount++; +} + +sub error +{ + my ($file, $line, $count, $statement) = @_; + print "------------------------------------------------------------\n"; + print "ERROR: $statement\n"; + print " $file:$count\n"; + print " $line\n"; + + $issueFound = 1; + $errorFound = 1; +} + |