diff options
Diffstat (limited to 'tools/bionicbb/presubmit.py')
-rw-r--r-- | tools/bionicbb/presubmit.py | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/tools/bionicbb/presubmit.py b/tools/bionicbb/presubmit.py new file mode 100644 index 0000000..cc6f3cc --- /dev/null +++ b/tools/bionicbb/presubmit.py @@ -0,0 +1,203 @@ +# +# 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: + job = jenkins[build].invoke() + url = job.get_build().baseurl + 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 |