Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: tools/rietveld_source_owners.py

Issue 2675403002: [DO NOT LAND] Add some scripts to diagnose impact of crbug.com/684270. (Closed)
Patch Set: Fix caching logic Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/rietveld_missing_owners.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2017 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 from __future__ import print_function
7
8
9 import argparse
10 import email.utils
11 import json
12 import os
13 import pickle
14 import re
15 import subprocess
16 import string
17 import sys
18 import urllib2
19
20 sys.path.append('/usr/local/google/home/wez/Projects/depot_tools')
21 import presubmit_support
22 import owners
23
24 def main(argv):
25 parser = argparse.ArgumentParser()
26 parser.add_argument('revisions', action='store')
27 args = parser.parse_args(argv)
28
29 all_commits = fetch_commits(args.revisions)
30 all_commits.reverse()
31 print("There are %d affected revisions." % len(all_commits))
32
33 affected_commits = filter_commits(all_commits)
34 print("There are %d potentially affected CLs." % len(affected_commits))
35
36 try:
37 commit_props_cache = open('/usr/local/google/home/wez/Projects/commit_props_ cache')
38 commit_props = pickle.load(commit_props_cache)
39 except:
40 commit_props = {}
41 finally:
42 commit_props_cache = None
43
44 missed_commits = []
45 missed_no_cl = []
46 needs_reviewers = []
47 needs_message = []
48 tbr_commits = []
49 for i in xrange(len(affected_commits)):
50 commit = affected_commits[i]
51 sys.stdout.write('[%d/%d]\r' % (i+1, len(affected_commits)))
52
53 if not commit.has_key('cl'):
54 missed_no_cl.append(commit)
55 continue
56
57 # Fetch the CL properties, from the cache, or Rietveld.
58 if not commit_props.has_key(commit['cl']):
59 sys.stdout.flush()
60 commit_props[commit['cl']] = rietveld_props_for_issue(commit['cl'])
61 commit_props_cache = open(
62 '/usr/local/google/home/wez/Projects/commit_props_cache', 'w')
63 pickle.dump(commit_props, commit_props_cache)
64 commit_props_cache = None
65
66 # Skip checking out fully-processed CLs.
67 if commit_props[commit['cl']].has_key('missed_files'):
68 if not commit_props[commit['cl']]['missed_files']:
69 continue
70
71 # Check out the preceding revision, to check OWNERS against.
72 os.chdir('/usr/local/google/home/wez/Projects/git-worktree')
73 git(['checkout', commit['previous_revision']])
74
75 # Open the OWNERS database.
76 owners_db = owners.Database(
77 '/usr/local/google/home/wez/Projects/git-worktree', open, os.path)
78
79 # Check which files are not covered by the CL approvers.
80 # Note that the CL owner may be the relevant approver.
81 approvers = rietveld_approvers(commit_props[commit['cl']])
82 approvers += [commit['owner_email']]
83 missed_files = owners_db.files_not_covered_by(commit['files'], approvers)
84 commit_props[commit['cl']]['missed_files'] = missed_files
85
86 if missed_files:
87 if commit.has_key('tbr_emails'):
88 approvers += commit['tbr_emails']
89 missed_files = owners_db.files_not_covered_by(commit['files'],
90 approvers)
91 commit_props[commit['cl']]['missed_files'] = missed_files
92
93 if missed_files:
94 missed_commits.append(commit)
95 commit['missed_files'] = sorted(missed_files)
96
97 # Check if the CL reviewers covers the files' OWNERS.
98 uncovered_files = owners_db.files_not_covered_by(
99 commit['files'],
100 commit_props[commit['cl']]['reviewers'] + [commit['owner_email']])
101 if uncovered_files:
102 needs_reviewers.append(commit)
103 commit['new_reviewers'] = sorted(
104 owners_db.reviewers_for(missed_files, commit['owner_email']))
105
106 # Remove the commit from the props cache, so next run will refresh.
107 del commit_props[commit['cl']]
108 else:
109 # TODO: This is wrong; includes existing non-OWNER reviewers.
110 commit['new_reviewers'] = sorted(set(
111 commit_props[commit['cl']]['reviewers']) - set(approvers))
112
113 # CL has reviewer coverage, so check if it has a message.
114 if not rietveld_has_wez_message(commit_props[commit['cl']]):
115 needs_message.append(commit)
116
117 # Remove the commit from the props cache, so next run will refresh.
118 del commit_props[commit['cl']]
119 else:
120 tbr_commits.append(commit)
121
122 # Persist any deletions from the commit_props cache.
123 commit_props_cache = open(
124 '/usr/local/google/home/wez/Projects/commit_props_cache', 'w')
125 pickle.dump(commit_props, commit_props_cache)
126 commit_props_cache = None
127
128 print("Missed OWNERS for one or more files in %d CLs." % len(missed_commits))
129 if tbr_commits:
130 print("(%d CLs passed only due to TBRs.)" % len(tbr_commits))
131
132 print("%d CLs are missing OWNER reviewers:" % len(needs_reviewers))
133 for commit in needs_reviewers:
134 print("http://crrev.com/%d: add %s for (%s)" % (commit['cl'], commit['new_re viewers'], ','.join(commit['missed_files'])))
135
136 print("%d CLs have reviewers but need messaging:" % len(needs_message))
137 for commit in needs_message:
138 print('\nhttp://crrev.com/%d: has (%s) for:' % (
139 commit['cl'], ', '.join(commit['new_reviewers'])))
140 print('Hallo %s!\nDue to a depot_tools patch which mistakenly removed the OW NERS check for non-source files (see crbug.com/684270), the following files land ed in this CL and need a retrospective review from you:' % ', '.join(commit['new _reviewers']) )
141 for filename in commit['missed_files']:
142 print('\t%s' % filename)
143 file_type = os.path.splitext(filename)[1]
144 if not file_type:
145 file_type = os.path.basename(filename)
146 print('Thanks,\nWez')
147
148
149 def git(args):
150 command = subprocess.Popen(['/usr/bin/git'] + args, stdout=subprocess.PIPE, st derr=subprocess.PIPE)
151 return command.stdout
152
153
154 def read_commit(pipe):
155 commit_info = {}
156 while True:
157 line = pipe.readline()
158 line = line.strip()
159 if line[:6] == 'commit':
160 commit_info['id'] = line.split()[1]
161 if line[:7] == 'Author:':
162 owner = line[7:].strip()
163 commit_info['owner_email'] = email.utils.parseaddr(owner)[1]
164 if line in ['Author: chrome-cron <chrome-cron@google.com>',
165 'Author: chromeos-commit-bot <chromeos-commit-bot@chromium.org>' ]:
166 commit_info['is_bot'] = True
167 if line[-20:] == 'roller@chromium.org>':
168 commit_info['is_bot'] = True
169 if not line:
170 break
171
172 description = []
173 while True:
174 line = pipe.readline()
175 if line[:4] != ' ':
176 break
177 line = line[4:]
178 description.append(line)
179
180 if line[:11] == 'Review-Url:':
181 cl_url_parts = line[11:].strip().split('/')
182 if cl_url_parts[0] != 'https:' or cl_url_parts[1] != '':
183 raise Exception("Strange CL URL format: " + repr(cl_url_parts))
184 commit_info['cl'] = int(cl_url_parts[3].split()[0])
185 if line[:4] == 'TBR=':
186 commit_info['tbr_emails'] = map(lambda x: email.utils.parseaddr(x.strip()) [1], line[4:].split(','))
187
188 commit_info['description'] = description
189
190 files = []
191 while True:
192 line = pipe.readline()
193 if not line:
194 commit_info['last'] = True
195 break
196 if line == '\n':
197 break
198 files.append(line.strip())
199 commit_info['files'] = files
200
201 return commit_info
202
203
204 def fetch_commits(revisions):
205 log_file = git(['log', '--name-only', revisions])
206 commits = []
207 previous_revision = revisions.split('..')[0]
208 while True:
209 commit = read_commit(log_file)
210 commit['previous_revision'] = previous_revision
211 previous_revision = commit['id']
212 commits.append(commit)
213 sys.stdout.write('[%d]\r' % len(commits))
214 if commit.has_key('last'):
215 break
216 return commits
217
218
219 def filter_commits(commits):
220 text_files = (r'.+\.txt$', r'.+\.json$',)
221 exclusions = _EXCLUDED_PATHS = (
222 r"^breakpad[\\\/].*",
223 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
224 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
225 r"^native_client_sdk[\\\/]src[\\\/]tools[\\\/].*.mk",
226 r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
227 r"^skia[\\\/].*",
228 r"^third_party[\\\/]WebKit[\\\/].*",
229 r"^v8[\\\/].*",
230 r".*MakeFile$",
231 r".+_autogen\.h$",
232 r".+[\\\/]pnacl_shim\.c$",
233 r"^gpu[\\\/]config[\\\/].*_list_json\.cc$",
234 r"^chrome[\\\/]browser[\\\/]resources[\\\/]pdf[\\\/]index.js",
235 r".*vulcanized.html$",
236 r".*crisper.js$",
237 )
238 whitelist = lambda x: reduce(lambda y, z: y or z.match(x), map(re.compile, pre submit_support.InputApi.DEFAULT_WHITE_LIST + text_files + exclusions), False)
239 blacklist = lambda x: reduce(lambda y, z: y or z.match(x), map(re.compile, pre submit_support.InputApi.DEFAULT_BLACK_LIST), False)
240
241 def special_case_filter(file_path):
242 # Omit all files under blimp/ - it's gone.
243 if file_path[:6] == 'blimp/' or file_path[:24] == 'third_party/blimp_fonts/' :
244 return False
245 # Omit .xtb files; they're translations, updated by an automatic process.
246 if file_path[-4:] == '.xtb':
247 return False
248 # Omit some third-party directories which were also removed.
249 if file_path[:21] == 'third_party/bintrees/' or file_path[:20] == 'third_par ty/hwcplus/' or file_path[:23] == 'third_party/webtreemap/' or file_path[:24] == 'third_party/v4l2capture/':
250 return False
251 return True
252
253 def filter_commit_files(commit):
254 commit['files'] = filter(lambda f: special_case_filter(f) and (blacklist(f) or not whitelist(f)), commit['files'])
255
256 affected_commits = []
257 for i in xrange(len(commits)):
258 sys.stdout.write('[%d/%d]\r' % (i+1, len(commits)))
259 commit = commits[i]
260 if commit.has_key('is_bot'):
261 continue
262 if commit.get('cl',0) in [2621843003,2585733002,2651543002,2645293002]:
263 # Skip some commits with e.g. broken TBR= lines, manually verified.
264 continue
265 filter_commit_files(commit)
266 if commit['files']:
267 affected_commits.append(commit)
268 return affected_commits
269
270
271 def rietveld_props_for_issue(issue):
272 """Returns a dictionary of properties, including messages, for the issue."""
273
274 url = 'https://codereview.chromium.org/api/%d?messages=true' % issue
275 fp = None
276 try:
277 fp = urllib2.urlopen(url)
278 return json.load(fp)
279 finally:
280 if fp:
281 fp.close()
282
283
284 def rietveld_approvers(props):
285 """Returns a sorted list of the approvers, from an issue's props."""
286 messages = props.get('messages', [])
287 return sorted(set(m['sender'] for m in messages if m.get('approval')))
288
289
290 def rietveld_has_wez_message(props):
291 """Returns True if there is a message from wez@ about the snafu."""
292 messages = props.get('messages', [])
293 return reduce(lambda result, m: result or (m['sender'] == 'wez@chromium.org' a nd m['text'][:6] in ['Hallo ', 'FYI, a']), messages, False)
294
295
296 if __name__ == '__main__':
297 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « tools/rietveld_missing_owners.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698