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