1 # 2 # Copyright (C) 2015 The Android Open Source Project 3 # 4 # Licensed under the Apache License, Version 2.0 (the 'License'); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an 'AS IS' BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 # 16 import httplib 17 import httplib2 18 import logging 19 import re 20 import socket 21 22 import apiclient.errors 23 24 import gerrit 25 import gmail 26 import presubmit 27 28 29 def get_gerrit_info(body): 30 info = {} 31 gerrit_pattern = r'^Gerrit-(\S+): (.+)$' 32 for match in re.finditer(gerrit_pattern, body, flags=re.MULTILINE): 33 info[match.group(1)] = match.group(2).strip() 34 return info 35 36 37 def process_message(msg, dry_run): 38 try: 39 body = gmail.get_body(msg) 40 gerrit_info = get_gerrit_info(body) 41 if not gerrit_info: 42 logging.fatal('No Gerrit info found: %s', msg.subject) 43 msg_type = gerrit_info['MessageType'] 44 handlers = { 45 'comment': presubmit.handle_comment, 46 'newchange': presubmit.handle_change, 47 'newpatchset': presubmit.handle_change, 48 49 'abandon': presubmit.skip_handler, 50 'merge-failed': presubmit.skip_handler, 51 'merged': presubmit.skip_handler, 52 'restore': presubmit.skip_handler, 53 'revert': presubmit.skip_handler, 54 } 55 56 message_type = gerrit_info['MessageType'] 57 if message_type in handlers: 58 return handlers[message_type](gerrit_info, body, dry_run) 59 else: 60 logging.warning('MessageType %s unhandled.', msg_type) 61 return False 62 except NotImplementedError as ex: 63 logging.error("%s", ex) 64 return False 65 except gerrit.GerritError as ex: 66 change_id = gerrit_info['Change-Id'] 67 logging.error('Gerrit error (%d): %s %s', ex.code, change_id, ex.url) 68 return ex.code == 404 69 70 71 def get_and_process_jobs(): 72 dry_run = False 73 74 gmail_service = gmail.build_service() 75 msg_service = gmail_service.users().messages() 76 77 # We run in a loop because some of the exceptions thrown here mean we just 78 # need to retry. For errors where we should back off (typically any gmail 79 # API exceptions), process_changes catches the error and returns normally. 80 while True: 81 try: 82 process_changes(gmail_service, msg_service, dry_run) 83 return 84 except httplib.BadStatusLine: 85 pass 86 except httplib2.ServerNotFoundError: 87 pass 88 except socket.error: 89 pass 90 91 92 def process_changes(gmail_service, msg_service, dry_run): 93 try: 94 labels = gmail_service.users().labels().list(userId='me').execute() 95 if not labels['labels']: 96 logging.error('Could not retrieve Gmail labels') 97 return 98 label_id = gmail.get_gerrit_label(labels['labels']) 99 if not label_id: 100 logging.error('Could not find gerrit label') 101 return 102 103 for msg in gmail.get_all_messages(gmail_service, label_id): 104 msg = msg_service.get(userId='me', id=msg['id']).execute() 105 if process_message(msg, dry_run) and not dry_run: 106 msg_service.trash(userId='me', id=msg['id']).execute() 107 except apiclient.errors.HttpError as ex: 108 logging.error('API Client HTTP error: %s', ex) 109