summaryrefslogtreecommitdiffstats
path: root/tools/bionicbb/presubmit.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/bionicbb/presubmit.py')
-rw-r--r--tools/bionicbb/presubmit.py203
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