| # | 
 | # Copyright (C) 2015 The Android Open Source Project | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the 'License'); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an 'AS IS' BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 | # | 
 | from __future__ import absolute_import | 
 |  | 
 | import json | 
 | import logging | 
 | import os.path | 
 | import re | 
 | import requests | 
 |  | 
 | import jenkinsapi | 
 |  | 
 | import gerrit | 
 |  | 
 | import config | 
 |  | 
 |  | 
 | def is_untrusted_committer(change_id, patch_set): | 
 |     # TODO(danalbert): Needs to be based on the account that made the comment. | 
 |     commit = gerrit.get_commit(change_id, patch_set) | 
 |     committer = commit['committer']['email'] | 
 |     return not committer.endswith('@google.com') | 
 |  | 
 |  | 
 | def contains_cleanspec(change_id, patch_set): | 
 |     files = gerrit.get_files_for_revision(change_id, patch_set) | 
 |     return 'CleanSpec.mk' in [os.path.basename(f) for f in files] | 
 |  | 
 |  | 
 | def contains_bionicbb(change_id, patch_set): | 
 |     files = gerrit.get_files_for_revision(change_id, patch_set) | 
 |     return any('tools/bionicbb' in f for f in files) | 
 |  | 
 |  | 
 | def should_skip_build(info): | 
 |     if info['MessageType'] not in ('newchange', 'newpatchset', 'comment'): | 
 |         raise ValueError('should_skip_build() is only valid for new ' | 
 |                          'changes, patch sets, and commits.') | 
 |  | 
 |     change_id = info['Change-Id'] | 
 |     patch_set = info['PatchSet'] | 
 |  | 
 |     checks = [ | 
 |         is_untrusted_committer, | 
 |         contains_cleanspec, | 
 |         contains_bionicbb, | 
 |     ] | 
 |     for check in checks: | 
 |         if check(change_id, patch_set): | 
 |             return True | 
 |     return False | 
 |  | 
 |  | 
 | def clean_project(dry_run): | 
 |     username = config.jenkins_credentials['username'] | 
 |     password = config.jenkins_credentials['password'] | 
 |     jenkins_url = config.jenkins_url | 
 |     jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password) | 
 |  | 
 |     build = 'clean-bionic-presubmit' | 
 |     if build in jenkins: | 
 |         if not dry_run: | 
 |             _ = jenkins[build].invoke() | 
 |             # https://issues.jenkins-ci.org/browse/JENKINS-27256 | 
 |             # url = job.get_build().baseurl | 
 |             url = 'URL UNAVAILABLE' | 
 |         else: | 
 |             url = 'DRY_RUN_URL' | 
 |         logging.info('Cleaning: %s %s', build, url) | 
 |     else: | 
 |         logging.error('Failed to clean: could not find project %s', build) | 
 |     return True | 
 |  | 
 |  | 
 | def build_project(gerrit_info, dry_run, lunch_target=None): | 
 |     project_to_jenkins_map = { | 
 |         'platform/bionic': 'bionic-presubmit', | 
 |         'platform/build': 'bionic-presubmit', | 
 |         'platform/external/jemalloc': 'bionic-presubmit', | 
 |         'platform/external/libcxx': 'bionic-presubmit', | 
 |         'platform/external/libcxxabi': 'bionic-presubmit', | 
 |         'platform/external/compiler-rt': 'bionic-presubmit', | 
 |     } | 
 |  | 
 |     username = config.jenkins_credentials['username'] | 
 |     password = config.jenkins_credentials['password'] | 
 |     jenkins_url = config.jenkins_url | 
 |     jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password) | 
 |  | 
 |     project = gerrit_info['Project'] | 
 |     change_id = gerrit_info['Change-Id'] | 
 |     if project in project_to_jenkins_map: | 
 |         build = project_to_jenkins_map[project] | 
 |     else: | 
 |         build = 'bionic-presubmit' | 
 |  | 
 |     if build in jenkins: | 
 |         project_path = '/'.join(project.split('/')[1:]) | 
 |         if not project_path: | 
 |             raise RuntimeError('bogus project: {}'.format(project)) | 
 |         if project_path.startswith('platform/'): | 
 |             raise RuntimeError('Bad project mapping: {} => {}'.format( | 
 |                 project, project_path)) | 
 |         ref = gerrit.ref_for_change(change_id) | 
 |         params = { | 
 |             'REF': ref, | 
 |             'CHANGE_ID': change_id, | 
 |             'PROJECT': project_path | 
 |         } | 
 |         if lunch_target is not None: | 
 |             params['LUNCH_TARGET'] = lunch_target | 
 |         if not dry_run: | 
 |             _ = jenkins[build].invoke(build_params=params) | 
 |             # https://issues.jenkins-ci.org/browse/JENKINS-27256 | 
 |             # url = job.get_build().baseurl | 
 |             url = 'URL UNAVAILABLE' | 
 |         else: | 
 |             url = 'DRY_RUN_URL' | 
 |         logging.info('Building: %s => %s %s %s', project, build, url, | 
 |                      change_id) | 
 |     else: | 
 |         logging.error('Unknown build: %s => %s %s', project, build, change_id) | 
 |     return True | 
 |  | 
 |  | 
 | def handle_change(gerrit_info, _, dry_run): | 
 |     if should_skip_build(gerrit_info): | 
 |         return True | 
 |     return build_project(gerrit_info, dry_run) | 
 |  | 
 |  | 
 | def drop_rejection(gerrit_info, dry_run): | 
 |     request_data = { | 
 |         'changeid': gerrit_info['Change-Id'], | 
 |         'patchset': gerrit_info['PatchSet'] | 
 |     } | 
 |     url = '{}/{}'.format(config.build_listener_url, 'drop-rejection') | 
 |     headers = {'Content-Type': 'application/json;charset=UTF-8'} | 
 |     if not dry_run: | 
 |         try: | 
 |             requests.post(url, headers=headers, data=json.dumps(request_data)) | 
 |         except requests.exceptions.ConnectionError as ex: | 
 |             logging.error('Failed to drop rejection: %s', ex) | 
 |             return False | 
 |     logging.info('Dropped rejection: %s', gerrit_info['Change-Id']) | 
 |     return True | 
 |  | 
 |  | 
 | def handle_comment(gerrit_info, body, dry_run): | 
 |     if 'Verified+1' in body: | 
 |         drop_rejection(gerrit_info, dry_run) | 
 |  | 
 |     if should_skip_build(gerrit_info): | 
 |         return True | 
 |  | 
 |     command_map = { | 
 |         'clean': lambda: clean_project(dry_run), | 
 |         'retry': lambda: build_project(gerrit_info, dry_run), | 
 |  | 
 |         'arm': lambda: build_project(gerrit_info, dry_run, | 
 |                                      lunch_target='aosp_arm-eng'), | 
 |         'aarch64': lambda: build_project(gerrit_info, dry_run, | 
 |                                          lunch_target='aosp_arm64-eng'), | 
 |         'mips': lambda: build_project(gerrit_info, dry_run, | 
 |                                       lunch_target='aosp_mips-eng'), | 
 |         'mips64': lambda: build_project(gerrit_info, dry_run, | 
 |                                         lunch_target='aosp_mips64-eng'), | 
 |         'x86': lambda: build_project(gerrit_info, dry_run, | 
 |                                      lunch_target='aosp_x86-eng'), | 
 |         'x86_64': lambda: build_project(gerrit_info, dry_run, | 
 |                                         lunch_target='aosp_x86_64-eng'), | 
 |     } | 
 |  | 
 |     def handle_unknown_command(): | 
 |         pass    # TODO(danalbert): should complain to the commenter. | 
 |  | 
 |     commands = [match.group(1).strip() for match in | 
 |                 re.finditer(r'^bionicbb:\s*(.+)$', body, flags=re.MULTILINE)] | 
 |  | 
 |     for command in commands: | 
 |         if command in command_map: | 
 |             command_map[command]() | 
 |         else: | 
 |             handle_unknown_command() | 
 |  | 
 |     return True | 
 |  | 
 |  | 
 | def skip_handler(gerrit_info, _, __): | 
 |     logging.info('Skipping %s: %s', gerrit_info['MessageType'], | 
 |                  gerrit_info['Change-Id']) | 
 |     return True |