1cdf0c1d5Smjnelson#! /usr/bin/python 2cdf0c1d5Smjnelson# 3cdf0c1d5Smjnelson# CDDL HEADER START 4cdf0c1d5Smjnelson# 5cdf0c1d5Smjnelson# The contents of this file are subject to the terms of the 6cdf0c1d5Smjnelson# Common Development and Distribution License (the "License"). 7cdf0c1d5Smjnelson# You may not use this file except in compliance with the License. 8cdf0c1d5Smjnelson# 9cdf0c1d5Smjnelson# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10cdf0c1d5Smjnelson# or http://www.opensolaris.org/os/licensing. 11cdf0c1d5Smjnelson# See the License for the specific language governing permissions 12cdf0c1d5Smjnelson# and limitations under the License. 13cdf0c1d5Smjnelson# 14cdf0c1d5Smjnelson# When distributing Covered Code, include this CDDL HEADER in each 15cdf0c1d5Smjnelson# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16cdf0c1d5Smjnelson# If applicable, add the following below this CDDL HEADER, with the 17cdf0c1d5Smjnelson# fields enclosed by brackets "[]" replaced with your own identifying 18cdf0c1d5Smjnelson# information: Portions Copyright [yyyy] [name of copyright owner] 19cdf0c1d5Smjnelson# 20cdf0c1d5Smjnelson# CDDL HEADER END 21cdf0c1d5Smjnelson# 22cdf0c1d5Smjnelson 23cdf0c1d5Smjnelson# 2441a5f560SMark J. Nelson# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25cdf0c1d5Smjnelson# Use is subject to license terms. 26cdf0c1d5Smjnelson# 27cdf0c1d5Smjnelson 282f54b716SRichard Lowe# Copyright 2007, 2010 Richard Lowe 29*93be19b9SAndy Fiddaman# Copyright 2018 OmniOS Community Edition (OmniOSce) Association. 302f54b716SRichard Lowe 31cdf0c1d5Smjnelson# 32cdf0c1d5Smjnelson# Check delta comments: 33cdf0c1d5Smjnelson# - Have the correct form. 342f54b716SRichard Lowe# - Have a synopsis matching that of the bug 35cdf0c1d5Smjnelson# - Appear only once. 36cdf0c1d5Smjnelson# 37cdf0c1d5Smjnelson 38cdf0c1d5Smjnelsonimport re, sys 392f54b716SRichard Lowefrom onbld.Checks.DbLookups import BugDB 40cdf0c1d5Smjnelson 41ef62fef3SRichard Lowe 422f54b716SRichard Lowebugre = re.compile(r'^(\d{2,7}) (.*)$') 432f54b716SRichard Lowe 44cdf0c1d5Smjnelson 45cdf0c1d5Smjnelsondef isBug(comment): 46cdf0c1d5Smjnelson return bugre.match(comment) 47cdf0c1d5Smjnelson 48cdf0c1d5Smjnelson 492f54b716SRichard Lowedef comchk(comments, check_db=True, output=sys.stderr): 50ef62fef3SRichard Lowe '''Validate checkin comments against ON standards. 51ef62fef3SRichard Lowe 52ef62fef3SRichard Lowe Comments must be a list of one-line comments, with no trailing 53ef62fef3SRichard Lowe newline. 54ef62fef3SRichard Lowe 552f54b716SRichard Lowe If check_db is True (the default), validate bug synopses against the 562f54b716SRichard Lowe databases. 57ef62fef3SRichard Lowe 58ef62fef3SRichard Lowe Error messages intended for the user are written to output, 59ef62fef3SRichard Lowe which defaults to stderr 60ef62fef3SRichard Lowe ''' 612f54b716SRichard Lowe bugnospcre = re.compile(r'^(\d{2,7})([^ ].*)') 622f54b716SRichard Lowe ignorere = re.compile(r'^(' + 632f54b716SRichard Lowe r'Portions contributed by|' + 642f54b716SRichard Lowe r'Contributed by|' + 652f54b716SRichard Lowe r'Reviewed[ -]by|' + 662f54b716SRichard Lowe r'Approved[ -]by|' + 672f54b716SRichard Lowe r'back[ -]?out)' + 682f54b716SRichard Lowe r'[: ]') 69cdf0c1d5Smjnelson 70cdf0c1d5Smjnelson errors = { 'bugnospc': [], 71cdf0c1d5Smjnelson 'mutant': [], 72cdf0c1d5Smjnelson 'dup': [], 73cdf0c1d5Smjnelson 'nomatch': [], 74cdf0c1d5Smjnelson 'nonexistent': [] } 75cdf0c1d5Smjnelson bugs = {} 76ef62fef3SRichard Lowe ret = 0 77ef62fef3SRichard Lowe blanks = False 78cdf0c1d5Smjnelson 79cdf0c1d5Smjnelson for com in comments: 80ef62fef3SRichard Lowe # Our input must be newline-free, comments are line-wise. 81ef62fef3SRichard Lowe if com.find('\n') != -1: 82ef62fef3SRichard Lowe raise ValueError("newline in comment '%s'" % com) 83ef62fef3SRichard Lowe 84cdf0c1d5Smjnelson # Ignore valid comments we can't check 85cdf0c1d5Smjnelson if ignorere.search(com): 86cdf0c1d5Smjnelson continue 87cdf0c1d5Smjnelson 88cdf0c1d5Smjnelson if not com or com.isspace(): 89ef62fef3SRichard Lowe blanks = True 90cdf0c1d5Smjnelson continue 91cdf0c1d5Smjnelson 92cdf0c1d5Smjnelson match = bugre.search(com) 93cdf0c1d5Smjnelson if match: 94cdf0c1d5Smjnelson if match.group(1) not in bugs: 95cdf0c1d5Smjnelson bugs[match.group(1)] = [] 96cdf0c1d5Smjnelson bugs[match.group(1)].append(match.group(2)) 97cdf0c1d5Smjnelson continue 98cdf0c1d5Smjnelson 99cdf0c1d5Smjnelson # 100cdf0c1d5Smjnelson # Bugs missing a space after the ID are still bugs 101cdf0c1d5Smjnelson # for the purposes of the duplicate ID and synopsis 102cdf0c1d5Smjnelson # checks. 103cdf0c1d5Smjnelson # 104cdf0c1d5Smjnelson match = bugnospcre.search(com) 105cdf0c1d5Smjnelson if match: 106cdf0c1d5Smjnelson if match.group(1) not in bugs: 107cdf0c1d5Smjnelson bugs[match.group(1)] = [] 108cdf0c1d5Smjnelson bugs[match.group(1)].append(match.group(2)) 109cdf0c1d5Smjnelson errors['bugnospc'].append(com) 110cdf0c1d5Smjnelson continue 111cdf0c1d5Smjnelson 112cdf0c1d5Smjnelson # Anything else is bogus 113cdf0c1d5Smjnelson errors['mutant'].append(com) 114cdf0c1d5Smjnelson 115cdf0c1d5Smjnelson if len(bugs) > 0 and check_db: 116cdf0c1d5Smjnelson bugdb = BugDB() 117*93be19b9SAndy Fiddaman results = bugdb.lookup(list(bugs.keys())) 118cdf0c1d5Smjnelson 119*93be19b9SAndy Fiddaman for crid, insts in bugs.items(): 120cdf0c1d5Smjnelson if len(insts) > 1: 121cdf0c1d5Smjnelson errors['dup'].append(crid) 122cdf0c1d5Smjnelson 123cdf0c1d5Smjnelson if not check_db: 124cdf0c1d5Smjnelson continue 125cdf0c1d5Smjnelson 126cdf0c1d5Smjnelson if crid not in results: 127cdf0c1d5Smjnelson errors['nonexistent'].append(crid) 128cdf0c1d5Smjnelson continue 129cdf0c1d5Smjnelson 130ef62fef3SRichard Lowe # 131ef62fef3SRichard Lowe # For each synopsis, compare the real synopsis with 132ef62fef3SRichard Lowe # that in the comments, allowing for possible '(fix 133ef62fef3SRichard Lowe # stuff)'-like trailing text 134ef62fef3SRichard Lowe # 135cdf0c1d5Smjnelson for entered in insts: 136cdf0c1d5Smjnelson synopsis = results[crid]["synopsis"] 137ef62fef3SRichard Lowe if not re.search(r'^' + re.escape(synopsis) + 138cdf0c1d5Smjnelson r'( \([^)]+\))?$', entered): 139cdf0c1d5Smjnelson errors['nomatch'].append([crid, synopsis, 140cdf0c1d5Smjnelson entered]) 141cdf0c1d5Smjnelson 142cdf0c1d5Smjnelson 143cdf0c1d5Smjnelson if blanks: 144cdf0c1d5Smjnelson output.write("WARNING: Blank line(s) in comments\n") 145cdf0c1d5Smjnelson ret = 1 146cdf0c1d5Smjnelson 147cdf0c1d5Smjnelson if errors['dup']: 148cdf0c1d5Smjnelson ret = 1 149cdf0c1d5Smjnelson output.write("These IDs appear more than once in your " 150cdf0c1d5Smjnelson "comments:\n") 151cdf0c1d5Smjnelson for err in errors['dup']: 152cdf0c1d5Smjnelson output.write(" %s\n" % err) 153cdf0c1d5Smjnelson 154cdf0c1d5Smjnelson if errors['bugnospc']: 155cdf0c1d5Smjnelson ret = 1 156cdf0c1d5Smjnelson output.write("These bugs are missing a single space following " 157cdf0c1d5Smjnelson "the ID:\n") 158cdf0c1d5Smjnelson for com in errors['bugnospc']: 159cdf0c1d5Smjnelson output.write(" %s\n" % com) 160cdf0c1d5Smjnelson 161cdf0c1d5Smjnelson if errors['mutant']: 162cdf0c1d5Smjnelson ret = 1 1632f54b716SRichard Lowe output.write("These comments are not valid bugs:\n") 164cdf0c1d5Smjnelson for com in errors['mutant']: 165cdf0c1d5Smjnelson output.write(" %s\n" % com) 166cdf0c1d5Smjnelson 167cdf0c1d5Smjnelson if errors['nonexistent']: 168cdf0c1d5Smjnelson ret = 1 1692f54b716SRichard Lowe output.write("These bugs were not found in the databases:\n") 170cdf0c1d5Smjnelson for id in errors['nonexistent']: 171cdf0c1d5Smjnelson output.write(" %s\n" % id) 172cdf0c1d5Smjnelson 173cdf0c1d5Smjnelson if errors['nomatch']: 174cdf0c1d5Smjnelson ret = 1 1752f54b716SRichard Lowe output.write("These bug synopses don't match " 176cdf0c1d5Smjnelson "the database entries:\n") 177cdf0c1d5Smjnelson for err in errors['nomatch']: 1782f54b716SRichard Lowe output.write("Synopsis of %s is wrong:\n" % err[0]) 179cdf0c1d5Smjnelson output.write(" should be: '%s'\n" % err[1]) 180cdf0c1d5Smjnelson output.write(" is: '%s'\n" % err[2]) 181cdf0c1d5Smjnelson 182cdf0c1d5Smjnelson return ret 183