blob: cc6f3ccd87fec462af14e9492a144a29ed9f5b81 [file] [log] [blame]
Dan Albertd3fe4f12015-04-17 13:01:29 -07001#
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#
16from __future__ import absolute_import
17
18import json
19import logging
20import os.path
21import re
22import requests
23
24import jenkinsapi
25
26import gerrit
27
28import config
29
30
31def is_untrusted_committer(change_id, patch_set):
32 # TODO(danalbert): Needs to be based on the account that made the comment.
33 commit = gerrit.get_commit(change_id, patch_set)
34 committer = commit['committer']['email']
35 return not committer.endswith('@google.com')
36
37
38def contains_cleanspec(change_id, patch_set):
39 files = gerrit.get_files_for_revision(change_id, patch_set)
40 return 'CleanSpec.mk' in [os.path.basename(f) for f in files]
41
42
43def contains_bionicbb(change_id, patch_set):
44 files = gerrit.get_files_for_revision(change_id, patch_set)
45 return any('tools/bionicbb' in f for f in files)
46
47
48def should_skip_build(info):
49 if info['MessageType'] not in ('newchange', 'newpatchset', 'comment'):
50 raise ValueError('should_skip_build() is only valid for new '
51 'changes, patch sets, and commits.')
52
53 change_id = info['Change-Id']
54 patch_set = info['PatchSet']
55
56 checks = [
57 is_untrusted_committer,
58 contains_cleanspec,
59 contains_bionicbb,
60 ]
61 for check in checks:
62 if check(change_id, patch_set):
63 return True
64 return False
65
66
67def clean_project(dry_run):
68 username = config.jenkins_credentials['username']
69 password = config.jenkins_credentials['password']
70 jenkins_url = config.jenkins_url
71 jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password)
72
73 build = 'clean-bionic-presubmit'
74 if build in jenkins:
75 if not dry_run:
76 job = jenkins[build].invoke()
77 url = job.get_build().baseurl
78 else:
79 url = 'DRY_RUN_URL'
80 logging.info('Cleaning: %s %s', build, url)
81 else:
82 logging.error('Failed to clean: could not find project %s', build)
83 return True
84
85
86def build_project(gerrit_info, dry_run, lunch_target=None):
87 project_to_jenkins_map = {
88 'platform/bionic': 'bionic-presubmit',
89 'platform/build': 'bionic-presubmit',
90 'platform/external/jemalloc': 'bionic-presubmit',
91 'platform/external/libcxx': 'bionic-presubmit',
92 'platform/external/libcxxabi': 'bionic-presubmit',
93 'platform/external/compiler-rt': 'bionic-presubmit',
94 }
95
96 username = config.jenkins_credentials['username']
97 password = config.jenkins_credentials['password']
98 jenkins_url = config.jenkins_url
99 jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password)
100
101 project = gerrit_info['Project']
102 change_id = gerrit_info['Change-Id']
103 if project in project_to_jenkins_map:
104 build = project_to_jenkins_map[project]
105 else:
106 build = 'bionic-presubmit'
107
108 if build in jenkins:
109 project_path = '/'.join(project.split('/')[1:])
110 if not project_path:
111 raise RuntimeError('bogus project: {}'.format(project))
112 if project_path.startswith('platform/'):
113 raise RuntimeError('Bad project mapping: {} => {}'.format(
114 project, project_path))
115 ref = gerrit.ref_for_change(change_id)
116 params = {
117 'REF': ref,
118 'CHANGE_ID': change_id,
119 'PROJECT': project_path
120 }
121 if lunch_target is not None:
122 params['LUNCH_TARGET'] = lunch_target
123 if not dry_run:
124 _ = jenkins[build].invoke(build_params=params)
125 # https://issues.jenkins-ci.org/browse/JENKINS-27256
126 # url = job.get_build().baseurl
127 url = 'URL UNAVAILABLE'
128 else:
129 url = 'DRY_RUN_URL'
130 logging.info('Building: %s => %s %s %s', project, build, url,
131 change_id)
132 else:
133 logging.error('Unknown build: %s => %s %s', project, build, change_id)
134 return True
135
136
137def handle_change(gerrit_info, _, dry_run):
138 if should_skip_build(gerrit_info):
139 return True
140 return build_project(gerrit_info, dry_run)
141
142
143def drop_rejection(gerrit_info, dry_run):
144 request_data = {
145 'changeid': gerrit_info['Change-Id'],
146 'patchset': gerrit_info['PatchSet']
147 }
148 url = '{}/{}'.format(config.build_listener_url, 'drop-rejection')
149 headers = {'Content-Type': 'application/json;charset=UTF-8'}
150 if not dry_run:
151 try:
152 requests.post(url, headers=headers, data=json.dumps(request_data))
153 except requests.exceptions.ConnectionError as ex:
154 logging.error('Failed to drop rejection: %s', ex)
155 return False
156 logging.info('Dropped rejection: %s', gerrit_info['Change-Id'])
157 return True
158
159
160def handle_comment(gerrit_info, body, dry_run):
161 if 'Verified+1' in body:
162 drop_rejection(gerrit_info, dry_run)
163
164 if should_skip_build(gerrit_info):
165 return True
166
167 command_map = {
168 'clean': lambda: clean_project(dry_run),
169 'retry': lambda: build_project(gerrit_info, dry_run),
170
171 'arm': lambda: build_project(gerrit_info, dry_run,
172 lunch_target='aosp_arm-eng'),
173 'aarch64': lambda: build_project(gerrit_info, dry_run,
174 lunch_target='aosp_arm64-eng'),
175 'mips': lambda: build_project(gerrit_info, dry_run,
176 lunch_target='aosp_mips-eng'),
177 'mips64': lambda: build_project(gerrit_info, dry_run,
178 lunch_target='aosp_mips64-eng'),
179 'x86': lambda: build_project(gerrit_info, dry_run,
180 lunch_target='aosp_x86-eng'),
181 'x86_64': lambda: build_project(gerrit_info, dry_run,
182 lunch_target='aosp_x86_64-eng'),
183 }
184
185 def handle_unknown_command():
186 pass # TODO(danalbert): should complain to the commenter.
187
188 commands = [match.group(1).strip() for match in
189 re.finditer(r'^bionicbb:\s*(.+)$', body, flags=re.MULTILINE)]
190
191 for command in commands:
192 if command in command_map:
193 command_map[command]()
194 else:
195 handle_unknown_command()
196
197 return True
198
199
200def skip_handler(gerrit_info, _, __):
201 logging.info('Skipping %s: %s', gerrit_info['MessageType'],
202 gerrit_info['Change-Id'])
203 return True