| OLD | NEW |
| (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 json |
| 11 import os |
| 12 import subprocess |
| 13 import string |
| 14 import sys |
| 15 import urllib2 |
| 16 |
| 17 sys.path.append('/usr/local/google/home/wez/Projects/depot_tools') |
| 18 from owners import Database as OwnersDatabase |
| 19 |
| 20 def main(argv): |
| 21 parser = argparse.ArgumentParser() |
| 22 parser.add_argument('revisions', action='store') |
| 23 args = parser.parse_args(argv) |
| 24 |
| 25 owners = OwnersDatabase('/usr/local/google/home/wez/Projects/git-chrome/src',
open, os.path) |
| 26 |
| 27 try: |
| 28 affected_revisions = git_log(args.revisions) |
| 29 print("There are %d affected revisions." % len(affected_revisions)) |
| 30 |
| 31 affected_cls = cls_and_files_from_revisions(affected_revisions) |
| 32 print("There are %d potentially affected CLs." % len(affected_cls)) |
| 33 for cl, files in affected_cls: |
| 34 issue = rietveld_props_for_issue(cl) |
| 35 approvers = rietveld_approvers(issue) + [issue['owner_email']] |
| 36 missed_files = owners.files_not_covered_by(files, approvers) |
| 37 if missed_files: |
| 38 print('http://codereview.chromium.org/%d (missed:%s)' % (cl, missed_file
s)) |
| 39 |
| 40 except Exception as e: |
| 41 print('Exception raised: %s' % str(e), file=sys.stderr) |
| 42 return 1 |
| 43 |
| 44 |
| 45 def git(args): |
| 46 command = subprocess.Popen(['/usr/bin/git'] + args, stdout=subprocess.PIPE, st
derr=subprocess.PIPE) |
| 47 output = command.stdout.readlines() |
| 48 if command.stderr.read(): |
| 49 raise Exception("git failed: (" + repr(args) + ")") |
| 50 return output |
| 51 |
| 52 |
| 53 def git_show_raw(revision): |
| 54 return git(['show', '--name-only', revision]) |
| 55 |
| 56 |
| 57 def cls_and_files_from_revisions(affected_revisions): |
| 58 cls = [] |
| 59 for revision in affected_revisions: |
| 60 revision_cl = None |
| 61 lines = git_show_raw(revision) |
| 62 is_bot = False |
| 63 while lines and lines[0].rstrip() and lines[0][0] not in string.whitespace: |
| 64 line, lines = lines[0].strip(), lines[1:] |
| 65 if line in ['Author: chrome-cron <chrome-cron@google.com>', |
| 66 'Author: chromeos-commit-bot <chromeos-commit-bot@chromium.org
>']: |
| 67 is_bot = True |
| 68 break |
| 69 if is_bot: |
| 70 continue |
| 71 is_xtb_update = False |
| 72 while lines and lines[0] and lines[0][0] in string.whitespace: |
| 73 line, lines = lines[0].strip(), lines[1:] |
| 74 if line == 'Updating XTBs based on .GRDs from branch master': |
| 75 is_xtb_update = True |
| 76 break |
| 77 if line[:11] == 'Review-Url:': |
| 78 cl_url_parts = line[11:].strip().split('/') |
| 79 if cl_url_parts[0] != 'https:' or cl_url_parts[1] != '': |
| 80 raise Exception("Strange CL URL format: " + repr(cl_url_parts)) |
| 81 new_revision_cl = int(cl_url_parts[3].split()[0]) |
| 82 if revision_cl and revision_cl != new_revision_cl: |
| 83 print("WARNING: Multiple CLs?!? (%s has: %d vs %d)" % (revision, revis
ion_cl, new_revision_cl)) |
| 84 revision_cl = new_revision_cl |
| 85 if is_xtb_update: |
| 86 continue |
| 87 if not revision_cl: |
| 88 if revision in ['957e153e766373fd1589dfaf2a07cf1929de6463']: |
| 89 continue |
| 90 raise Exception("Missing CL revision: git %s" % revision) |
| 91 files = map(string.strip, lines) |
| 92 cls.append((revision_cl, files)) |
| 93 return cls |
| 94 |
| 95 |
| 96 def git_log(revisions): |
| 97 return map(lambda x: x.split()[0], |
| 98 git(['log', '--format=oneline', revisions])) |
| 99 |
| 100 |
| 101 def rietveld_props_for_issue(issue): |
| 102 """Returns a dictionary of properties, including messages, for the issue.""" |
| 103 |
| 104 url = 'https://codereview.chromium.org/api/%d?messages=true' % issue |
| 105 fp = None |
| 106 try: |
| 107 fp = urllib2.urlopen(url) |
| 108 return json.load(fp) |
| 109 finally: |
| 110 if fp: |
| 111 fp.close() |
| 112 |
| 113 |
| 114 def rietveld_approvers(props): |
| 115 """Returns a sorted list of the approvers, from an issue's props.""" |
| 116 messages = props.get('messages', []) |
| 117 return sorted(set(m['sender'] for m in messages if m.get('approval'))) |
| 118 |
| 119 |
| 120 if __name__ == '__main__': |
| 121 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |