xref: /illumos-gate/usr/src/tools/onbld/Checks/Comments.py (revision 2dc5cbd37af38054ac6aab88a3ecee22a13ed2eb)
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