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 2019 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# - Do not contain common spelling errors. 37# 38 39import re, sys 40from onbld.Checks.DbLookups import BugDB 41from onbld.Checks.SpellCheck import spellcheck_line 42 43 44bugre = re.compile(r'^(\d{2,7}) (.*)$') 45 46 47def isBug(comment): 48 return bugre.match(comment) 49 50 51def comchk(comments, check_db=True, output=sys.stderr): 52 '''Validate checkin comments against ON standards. 53 54 Comments must be a list of one-line comments, with no trailing 55 newline. 56 57 If check_db is True (the default), validate bug synopses against the 58 databases. 59 60 Error messages intended for the user are written to output, 61 which defaults to stderr 62 ''' 63 bugnospcre = re.compile(r'^(\d{2,7})([^ ].*)') 64 ignorere = re.compile(r'^(' + 65 r'Portions contributed by|' + 66 r'Contributed by|' + 67 r'Reviewed[ -]by|' + 68 r'Approved[ -]by|' + 69 r'back[ -]?out)' + 70 r'[: ]') 71 72 errors = { 'bugnospc': [], 73 'mutant': [], 74 'dup': [], 75 'nomatch': [], 76 'nonexistent': [], 77 'spelling': [] } 78 bugs = {} 79 ret = 0 80 blanks = False 81 82 lineno = 0 83 for com in comments: 84 lineno += 1 85 86 # Our input must be newline-free, comments are line-wise. 87 if com.find('\n') != -1: 88 raise ValueError("newline in comment '%s'" % com) 89 90 # Ignore valid comments we can't check 91 if ignorere.search(com): 92 continue 93 94 if not com or com.isspace(): 95 blanks = True 96 continue 97 98 for err in spellcheck_line(com): 99 errors['spelling'].append( 100 'comment line {} - {}'.format(lineno, err)) 101 102 match = bugre.search(com) 103 if match: 104 if match.group(1) not in bugs: 105 bugs[match.group(1)] = [] 106 bugs[match.group(1)].append(match.group(2)) 107 continue 108 109 # 110 # Bugs missing a space after the ID are still bugs 111 # for the purposes of the duplicate ID and synopsis 112 # checks. 113 # 114 match = bugnospcre.search(com) 115 if match: 116 if match.group(1) not in bugs: 117 bugs[match.group(1)] = [] 118 bugs[match.group(1)].append(match.group(2)) 119 errors['bugnospc'].append(com) 120 continue 121 122 # Anything else is bogus 123 errors['mutant'].append(com) 124 125 if len(bugs) > 0 and check_db: 126 bugdb = BugDB() 127 results = bugdb.lookup(list(bugs.keys())) 128 129 for crid, insts in bugs.items(): 130 if len(insts) > 1: 131 errors['dup'].append(crid) 132 133 if not check_db: 134 continue 135 136 if crid not in results: 137 errors['nonexistent'].append(crid) 138 continue 139 140 # 141 # For each synopsis, compare the real synopsis with 142 # that in the comments, allowing for possible '(fix 143 # stuff)'-like trailing text 144 # 145 for entered in insts: 146 synopsis = results[crid]["synopsis"] 147 if not re.search(r'^' + re.escape(synopsis) + 148 r'( \([^)]+\))?$', entered): 149 errors['nomatch'].append([crid, synopsis, 150 entered]) 151 152 153 if blanks: 154 output.write("WARNING: Blank line(s) in comments\n") 155 ret = 1 156 157 if errors['dup']: 158 ret = 1 159 output.write("These IDs appear more than once in your " 160 "comments:\n") 161 for err in errors['dup']: 162 output.write(" %s\n" % err) 163 164 if errors['bugnospc']: 165 ret = 1 166 output.write("These bugs are missing a single space following " 167 "the ID:\n") 168 for com in errors['bugnospc']: 169 output.write(" %s\n" % com) 170 171 if errors['mutant']: 172 ret = 1 173 output.write("These comments are not valid bugs:\n") 174 for com in errors['mutant']: 175 output.write(" %s\n" % com) 176 177 if errors['nonexistent']: 178 ret = 1 179 output.write("These bugs were not found in the databases:\n") 180 for id in errors['nonexistent']: 181 output.write(" %s\n" % id) 182 183 if errors['nomatch']: 184 ret = 1 185 output.write("These bug synopses don't match " 186 "the database entries:\n") 187 for err in errors['nomatch']: 188 output.write("Synopsis of %s is wrong:\n" % err[0]) 189 output.write(" should be: '%s'\n" % err[1]) 190 output.write(" is: '%s'\n" % err[2]) 191 192 if errors['spelling']: 193 ret = 1 194 output.write("Spellcheck:\n") 195 for err in errors['spelling']: 196 output.write('{}\n'.format(err)) 197 198 return ret 199