OLD | NEW |
| (Empty) |
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 | |
6 """ Monkeypatches to override upstream code. """ | |
7 | |
8 | |
9 from buildbot.changes.gitpoller import GitPoller | |
10 from buildbot.process.properties import Properties | |
11 from buildbot.schedulers.trysched import BadJobfile | |
12 from buildbot.status.builder import EXCEPTION, FAILURE | |
13 from buildbot.status.web import base as webstatus_base | |
14 from buildbot.status.web.status_json import BuilderJsonResource | |
15 from buildbot.status.web.status_json import BuildersJsonResource | |
16 from buildbot.status.web.status_json import ChangeSourcesJsonResource | |
17 from buildbot.status.web.status_json import JsonResource | |
18 from buildbot.status.web.status_json import JsonStatusResource | |
19 from buildbot.status.web.status_json import MetricsJsonResource | |
20 from buildbot.status.web.status_json import ProjectJsonResource | |
21 from buildbot.status.web.status_json import SlavesJsonResource | |
22 from master import build_utils | |
23 from master import chromium_notifier | |
24 from master import gatekeeper | |
25 from master import try_job_base | |
26 from master import try_job_rietveld | |
27 from master import try_job_svn | |
28 from master.try_job_base import text_to_dict | |
29 from twisted.internet import defer | |
30 from twisted.python import log | |
31 from twisted.web import server | |
32 from webstatus import builder_statuses | |
33 | |
34 import builder_name_schema | |
35 import config_private | |
36 import json | |
37 import master_revision | |
38 import re | |
39 import slave_hosts_cfg | |
40 import slaves_cfg | |
41 import skia_vars | |
42 import utils | |
43 | |
44 | |
45 # The following users are allowed to run trybots even though they do not have | |
46 # accounts in google.com or chromium.org | |
47 TRYBOTS_REQUESTER_WHITELIST = [ | |
48 'henrik.smiding@intel.com', | |
49 'kkinnunen@nvidia.com', | |
50 'cdalton@nvidia.com', | |
51 'ravimist@gmail.com' | |
52 ] | |
53 | |
54 | |
55 ################################################################################ | |
56 ############################# Trybot Monkeypatches ############################# | |
57 ################################################################################ | |
58 | |
59 | |
60 @defer.deferredGenerator | |
61 def SubmitTryJobChanges(self, changes): | |
62 """ Override of SVNPoller.submit_changes: | |
63 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_svn.py?view=markup | |
64 | |
65 We modify it so that the patch file url is added to the build properties. | |
66 This allows the slave to download the patch directly rather than receiving | |
67 it from the master. | |
68 """ | |
69 for chdict in changes: | |
70 # pylint: disable=E1101 | |
71 parsed = self.parent.parse_options(text_to_dict(chdict['comments'])) | |
72 | |
73 # 'fix' revision. | |
74 # LKGR must be known before creating the change object. | |
75 wfd = defer.waitForDeferred(self.parent.get_lkgr(parsed)) | |
76 yield wfd | |
77 wfd.getResult() | |
78 | |
79 wfd = defer.waitForDeferred(self.master.addChange( | |
80 author=','.join(parsed['email']), | |
81 revision=parsed['revision'], | |
82 comments='', | |
83 properties={'patch_file_url': chdict['repository'] + '/' + \ | |
84 chdict['files'][0]})) | |
85 yield wfd | |
86 change = wfd.getResult() | |
87 | |
88 self.parent.addChangeInner(chdict['files'], parsed, change.number) | |
89 | |
90 try_job_svn.SVNPoller.submit_changes = SubmitTryJobChanges | |
91 | |
92 | |
93 def TryJobCreateBuildset(self, ssid, parsed_job): | |
94 """ Override of TryJobBase.create_buildset: | |
95 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_base.py?view=markup | |
96 | |
97 We modify it to verify that the requested builders are in the builder pool for | |
98 this try scheduler. This prevents try requests from running on builders which | |
99 are not registered as trybots. This apparently isn't a problem for Chromium | |
100 since they use a separate try master. | |
101 """ | |
102 log.msg('Creating try job(s) %s' % ssid) | |
103 result = None | |
104 for builder in parsed_job['bot']: | |
105 if builder in self.pools[self.name]: | |
106 result = self.addBuildsetForSourceStamp(ssid=ssid, | |
107 reason=parsed_job['name'], | |
108 external_idstring=parsed_job['name'], | |
109 builderNames=[builder], | |
110 properties=self.get_props(builder, parsed_job)) | |
111 else: | |
112 log.msg('Scheduler: %s rejecting try job for builder: %s not in %s' % ( | |
113 self.name, | |
114 builder, | |
115 self.pools[self.name])) | |
116 log.msg('Returning buildset: %s' % result) | |
117 return result | |
118 | |
119 try_job_base.TryJobBase.create_buildset = TryJobCreateBuildset | |
120 | |
121 | |
122 def HtmlResourceRender(self, request): | |
123 """ Override of buildbot.status.web.base.HtmlResource.render: | |
124 http://src.chromium.org/viewvc/chrome/trunk/tools/build/third_party/buildbot_8
_4p1/buildbot/status/web/base.py?view=markup | |
125 | |
126 We modify it to pass additional variables on to the web status pages, and | |
127 remove the "if False" section. | |
128 """ | |
129 # tell the WebStatus about the HTTPChannel that got opened, so they | |
130 # can close it if we get reconfigured and the WebStatus goes away. | |
131 # They keep a weakref to this, since chances are good that it will be | |
132 # closed by the browser or by us before we get reconfigured. See | |
133 # ticket #102 for details. | |
134 if hasattr(request, "channel"): | |
135 # web.distrib.Request has no .channel | |
136 request.site.buildbot_service.registerChannel(request.channel) | |
137 | |
138 ctx = self.getContext(request) | |
139 | |
140 ############################## Added by borenet ############################## | |
141 status = self.getStatus(request) | |
142 all_builders = status.getBuilderNames() | |
143 all_full_category_names = set() | |
144 all_categories = set() | |
145 all_subcategories = set() | |
146 subcategories_by_category = {} | |
147 for builder_name in all_builders: | |
148 category_full = status.getBuilder(builder_name).category or 'default' | |
149 all_full_category_names.add(category_full) | |
150 category_split = category_full.split('|') | |
151 category = category_split[0] | |
152 subcategory = category_split[1] if len(category_split) > 1 else 'default' | |
153 all_categories.add(category) | |
154 all_subcategories.add(subcategory) | |
155 if not subcategories_by_category.get(category): | |
156 subcategories_by_category[category] = [] | |
157 if not subcategory in subcategories_by_category[category]: | |
158 subcategories_by_category[category].append(subcategory) | |
159 | |
160 ctx['tree_status_baseurl'] = \ | |
161 skia_vars.GetGlobalVariable('tree_status_baseurl') | |
162 | |
163 ctx['all_full_category_names'] = sorted(list(all_full_category_names)) | |
164 ctx['all_categories'] = sorted(list(all_categories)) | |
165 ctx['all_subcategories'] = sorted(list(all_subcategories)) | |
166 ctx['subcategories_by_category'] = subcategories_by_category | |
167 ctx['default_refresh'] = \ | |
168 skia_vars.GetGlobalVariable('default_webstatus_refresh') | |
169 ctx['skia_repo'] = config_private.SKIA_GIT_URL | |
170 | |
171 active_master = config_private.Master.get_active_master() | |
172 ctx['internal_port'] = active_master.master_port | |
173 ctx['external_port'] = active_master.master_port_alt | |
174 ctx['title_url'] = config_private.Master.Skia.project_url | |
175 ctx['slave_hosts_cfg'] = slave_hosts_cfg.SLAVE_HOSTS | |
176 ctx['slaves_cfg'] = slaves_cfg.SLAVES | |
177 | |
178 ctx['active_master_name'] = active_master.project_name | |
179 ctx['master_revision'] = utils.get_current_revision() | |
180 ctx['master_running_revision'] = active_master.running_revision | |
181 ctx['master_launch_datetime'] = active_master.launch_datetime | |
182 ctx['is_internal_view'] = request.host.port == ctx['internal_port'] | |
183 ctx['masters'] = [] | |
184 for master in config_private.Master.valid_masters: | |
185 ctx['masters'].append({ | |
186 'name': master.project_name, | |
187 'host': master.master_host, | |
188 'internal_port': master.master_port, | |
189 'external_port': master.master_port_alt, | |
190 }) | |
191 ############################################################################## | |
192 | |
193 d = defer.maybeDeferred(lambda : self.content(request, ctx)) | |
194 def handle(data): | |
195 if isinstance(data, unicode): | |
196 data = data.encode("utf-8") | |
197 request.setHeader("content-type", self.contentType) | |
198 if request.method == "HEAD": | |
199 request.setHeader("content-length", len(data)) | |
200 return '' | |
201 return data | |
202 d.addCallback(handle) | |
203 def ok(data): | |
204 request.write(data) | |
205 request.finish() | |
206 def fail(f): | |
207 request.processingFailed(f) | |
208 return None # processingFailed will log this for us | |
209 d.addCallbacks(ok, fail) | |
210 return server.NOT_DONE_YET | |
211 | |
212 webstatus_base.HtmlResource.render = HtmlResourceRender | |
213 | |
214 | |
215 class TryBuildersJsonResource(JsonResource): | |
216 """ Clone of buildbot.status.web.status_json.BuildersJsonResource: | |
217 http://src.chromium.org/viewvc/chrome/trunk/tools/build/third_party/buildbot_8
_4p1/buildbot/status/web/status_json.py?view=markup | |
218 | |
219 We add filtering to display only the try builders. | |
220 """ | |
221 help = """List of all the try builders defined on a master.""" | |
222 pageTitle = 'Builders' | |
223 | |
224 def __init__(self, status, include_only_cq_trybots=False): | |
225 JsonResource.__init__(self, status) | |
226 for builder_name in self.status.getBuilderNames(): | |
227 if builder_name_schema.IsTrybot(builder_name) and ( | |
228 not include_only_cq_trybots or builder_name in slaves_cfg.CQ_TRYBOTS): | |
229 self.putChild(builder_name, | |
230 BuilderJsonResource(status, | |
231 status.getBuilder(builder_name))) | |
232 | |
233 | |
234 class CQRequiredStepsJsonResource(JsonResource): | |
235 help = 'List the steps which cannot fail on the commit queue.' | |
236 pageTitle = 'CQ Required Steps' | |
237 | |
238 def asDict(self, request): | |
239 return {'cq_required_steps': | |
240 skia_vars.GetGlobalVariable('cq_required_steps')} | |
241 | |
242 | |
243 def JsonStatusResourceInit(self, status): | |
244 """ Override of buildbot.status.web.status_json.JsonStatusResource.__init__: | |
245 http://src.chromium.org/viewvc/chrome/trunk/tools/build/third_party/buildbot_8
_4p1/buildbot/status/web/status_json.py?view=markup | |
246 | |
247 We add trybots, cqtrybots, cq_required_steps (details below). | |
248 """ | |
249 JsonResource.__init__(self, status) | |
250 self.level = 1 | |
251 self.putChild('builders', BuildersJsonResource(status)) | |
252 self.putChild('change_sources', ChangeSourcesJsonResource(status)) | |
253 self.putChild('project', ProjectJsonResource(status)) | |
254 self.putChild('slaves', SlavesJsonResource(status)) | |
255 self.putChild('metrics', MetricsJsonResource(status)) | |
256 | |
257 ############################## Added by borenet ############################## | |
258 # Added to address: https://code.google.com/p/skia/issues/detail?id=1134 | |
259 self.putChild('trybots', TryBuildersJsonResource(status)) | |
260 ############################################################################## | |
261 ############################## Added by rmistry ############################## | |
262 # Added to have a place to get the list of trybots run by the CQ. | |
263 self.putChild('cqtrybots', | |
264 TryBuildersJsonResource(status, include_only_cq_trybots=True)) | |
265 ############################################################################## | |
266 ############################## Added by borenet ############################## | |
267 # Added to have a place to get the list of steps which cannot fail on the CQ. | |
268 self.putChild('cq_required_steps', CQRequiredStepsJsonResource(status)) | |
269 ############################################################################## | |
270 | |
271 ############################## Added by borenet ############################## | |
272 # Added to have a way to determine which code revision the master is running. | |
273 self.putChild('master_revision', | |
274 master_revision.MasterCheckedOutRevisionJsonResource(status)) | |
275 running_rev = config_private.Master.get_active_master().running_revision | |
276 self.putChild('master_running_revision', | |
277 master_revision.MasterRunningRevisionJsonResource( | |
278 status=status, running_revision=running_rev)) | |
279 | |
280 # This page gives the result of the most recent build for each builder. | |
281 self.putChild('builder_statuses', | |
282 builder_statuses.BuilderStatusesJsonResource(status)) | |
283 ############################################################################## | |
284 | |
285 # This needs to be called before the first HelpResource().body call. | |
286 self.hackExamples() | |
287 | |
288 JsonStatusResource.__init__ = JsonStatusResourceInit | |
289 | |
290 | |
291 @defer.deferredGenerator | |
292 def TryJobRietveldSubmitJobs(self, jobs): | |
293 """ Override of master.try_job_rietveld.TryJobRietveld.SubmitJobs: | |
294 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_rietveld.py?view=markup | |
295 | |
296 We modify it to include "baseurl" as a build property. | |
297 """ | |
298 log.msg('TryJobRietveld.SubmitJobs: %s' % json.dumps(jobs, indent=2)) | |
299 for job in jobs: | |
300 try: | |
301 # Gate the try job on the user that requested the job, not the one that | |
302 # authored the CL. | |
303 # pylint: disable=W0212 | |
304 ########################## Added by rmistry ########################## | |
305 if (job.get('requester') and not job['requester'].endswith('@google.com') | |
306 and not job['requester'].endswith('@chromium.org') | |
307 and not job['requester'] in TRYBOTS_REQUESTER_WHITELIST): | |
308 # Reject the job only if the requester has an email not ending in | |
309 # google.com or chromium.org | |
310 raise BadJobfile( | |
311 'TryJobRietveld rejecting job from %s' % job['requester']) | |
312 ###################################################################### | |
313 ########################## Added by borenet ########################## | |
314 if not (job.get('baseurl') and | |
315 config_private.Master.Skia.project_name.lower() in | |
316 job['baseurl']): | |
317 raise BadJobfile('TryJobRietveld rejecting job with unknown baseurl: %s' | |
318 % job.get('baseurl')) | |
319 ###################################################################### | |
320 if job['email'] != job['requester']: | |
321 # Note the fact the try job was requested by someone else in the | |
322 # 'reason'. | |
323 job['reason'] = job.get('reason') or '' | |
324 if job['reason']: | |
325 job['reason'] += '; ' | |
326 job['reason'] += "This CL was triggered by %s" % job['requester'] | |
327 | |
328 options = { | |
329 'bot': {job['builder']: job['tests']}, | |
330 'email': [job['email']], | |
331 'project': [self._project], | |
332 'try_job_key': job['key'], | |
333 } | |
334 # Transform some properties as is expected by parse_options(). | |
335 for key in ( | |
336 ########################## Added by borenet ########################## | |
337 'baseurl', | |
338 ###################################################################### | |
339 'name', 'user', 'root', 'reason', 'clobber', 'patchset', | |
340 'issue', 'requester', 'revision'): | |
341 options[key] = [job[key]] | |
342 | |
343 # Now cleanup the job dictionary and submit it. | |
344 cleaned_job = self.parse_options(options) | |
345 | |
346 wfd = defer.waitForDeferred(self.get_lkgr(cleaned_job)) | |
347 yield wfd | |
348 wfd.getResult() | |
349 | |
350 wfd = defer.waitForDeferred(self.master.addChange( | |
351 author=','.join(cleaned_job['email']), | |
352 # TODO(maruel): Get patchset properties to get the list of files. | |
353 # files=[], | |
354 revision=cleaned_job['revision'], | |
355 comments='')) | |
356 yield wfd | |
357 changeids = [wfd.getResult().number] | |
358 | |
359 wfd = defer.waitForDeferred(self.SubmitJob(cleaned_job, changeids)) | |
360 yield wfd | |
361 wfd.getResult() | |
362 except BadJobfile, e: | |
363 # We need to mark it as failed otherwise it'll stay in the pending | |
364 # state. Simulate a buildFinished event on the build. | |
365 if not job.get('key'): | |
366 log.err( | |
367 'Got %s for issue %s but not key, not updating Rietveld' % | |
368 (e, job.get('issue'))) | |
369 continue | |
370 log.err( | |
371 'Got %s for issue %s, updating Rietveld' % (e, job.get('issue'))) | |
372 for service in self.master.services: | |
373 if service.__class__.__name__ == 'TryServerHttpStatusPush': | |
374 # pylint: disable=W0212,W0612 | |
375 build = { | |
376 'properties': [ | |
377 ('buildername', job.get('builder'), None), | |
378 ('buildnumber', -1, None), | |
379 ('issue', job['issue'], None), | |
380 ('patchset', job['patchset'], None), | |
381 ('project', self._project, None), | |
382 ('revision', '', None), | |
383 ('slavename', '', None), | |
384 ('try_job_key', job['key'], None), | |
385 ], | |
386 'reason': job.get('reason', ''), | |
387 # Use EXCEPTION until SKIPPED results in a non-green try job | |
388 # results on Rietveld. | |
389 'results': EXCEPTION, | |
390 } | |
391 ########################## Added by rmistry ######################### | |
392 # Do not update Rietveld to mark the try job request as failed. | |
393 # See https://code.google.com/p/chromium/issues/detail?id=224014 for | |
394 # more context. | |
395 # service.push('buildFinished', build=build) | |
396 ##################################################################### | |
397 break | |
398 | |
399 try_job_rietveld.TryJobRietveld.SubmitJobs = TryJobRietveldSubmitJobs | |
400 | |
401 | |
402 def TryJobBaseGetProps(self, builder, options): | |
403 """ Override of try_job_base.TryJobBase.get_props: | |
404 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_base.py?view=markup | |
405 | |
406 We modify it to add "baseurl". | |
407 """ | |
408 keys = ( | |
409 ############################### Added by borenet ############################### | |
410 'baseurl', | |
411 ################################################################################ | |
412 'clobber', | |
413 'issue', | |
414 'patchset', | |
415 'requester', | |
416 'rietveld', | |
417 'root', | |
418 'try_job_key', | |
419 ) | |
420 # All these settings have no meaning when False or not set, so don't set | |
421 # them in that case. | |
422 properties = dict((i, options[i]) for i in keys if options.get(i)) | |
423 properties['testfilter'] = options['bot'].get(builder, None) | |
424 # pylint: disable=W0212 | |
425 props = Properties() | |
426 props.updateFromProperties(self.properties) | |
427 props.update(properties, self._PROPERTY_SOURCE) | |
428 return props | |
429 | |
430 try_job_base.TryJobBase.get_props = TryJobBaseGetProps | |
431 | |
432 | |
433 def TryJobRietveldConstructor( | |
434 self, name, pools, properties=None, last_good_urls=None, | |
435 code_review_sites=None, project=None): | |
436 try_job_base.TryJobBase.__init__(self, name, pools, properties, | |
437 last_good_urls, code_review_sites) | |
438 # pylint: disable=W0212 | |
439 endpoint = self._GetRietveldEndPointForProject(code_review_sites, project) | |
440 ############################### Added by rmistry ############################### | |
441 # rmistry: Adding '&master=tryserver.skia' to the endpoint to help filter the | |
442 # number of pending try patchsets returned. More details are in | |
443 # https://code.google.com/p/skia/issues/detail?id=2659 | |
444 endpoint += '&master=tryserver.skia' | |
445 # rmistry: Increased the polling time from 10 seconds to 1 min because 10 | |
446 # seconds is too short for us. The RietveldPoller stops working if the time is | |
447 # too short. | |
448 # pylint: disable=W0212 | |
449 self._poller = try_job_rietveld._RietveldPoller(endpoint, interval=60) | |
450 ################################################################################ | |
451 # pylint: disable=W0212 | |
452 self._valid_users = try_job_rietveld._ValidUserPoller(interval=12 * 60 * 60) | |
453 self._project = project | |
454 log.msg('TryJobRietveld created, get_pending_endpoint=%s ' | |
455 'project=%s' % (endpoint, project)) | |
456 | |
457 try_job_rietveld.TryJobRietveld.__init__ = TryJobRietveldConstructor | |
458 | |
459 | |
460 class SkiaGateKeeper(gatekeeper.GateKeeper): | |
461 | |
462 def isInterestingBuilder(self, builder_status): | |
463 """ Override of gatekeeper.GateKeeper.isInterestingBuilder: | |
464 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/gatek
eeper.py?view=markup | |
465 | |
466 We modify it to actually check whether the builder should be considered by | |
467 the GateKeeper, as indicated in its category name. | |
468 """ | |
469 | |
470 ret = (not builder_name_schema.IsTrybot(builder_status.getName()) and | |
471 chromium_notifier.ChromiumNotifier.isInterestingBuilder(self, | |
472 builder_status)) | |
473 log.msg('[gatekeeper-debug2] ======================') | |
474 log.msg('[gatekeeper-debug2] is not trybot: %s' % ( | |
475 not builder_name_schema.IsTrybot(builder_status.getName()))) | |
476 log.msg('[gatekeeper-debug2] isInterestingBuilder: %s' % ( | |
477 chromium_notifier.ChromiumNotifier.isInterestingBuilder( | |
478 self, builder_status))) | |
479 log.msg('[gatekeeper-debug2] builder_status.getName(): %s' % ( | |
480 builder_status.getName())) | |
481 log.msg('[gatekeeper-debug2] ret: %s' % ret) | |
482 log.msg('[gatekeeper-debug2] ======================') | |
483 return ret | |
484 | |
485 def isInterestingStep(self, build_status, step_status, results): | |
486 """ Override of gatekeeper.GateKeeper.isInterestingStep: | |
487 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/gatek
eeper.py?view=markup | |
488 | |
489 We modify it to comment out the SVN revision comparision to determine if the | |
490 current build is older because Skia uses commit hashes. | |
491 """ | |
492 # If we have not failed, or are not interested in this builder, | |
493 # then we have nothing to do. | |
494 if results[0] != FAILURE: | |
495 return False | |
496 | |
497 # Check if the slave is still alive. We should not close the tree for | |
498 # inactive slaves. | |
499 slave_name = build_status.getSlavename() | |
500 if slave_name in self.master_status.getSlaveNames(): | |
501 # @type self.master_status: L{buildbot.status.builder.Status} | |
502 # @type self.parent: L{buildbot.master.BuildMaster} | |
503 # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus} | |
504 slave_status = self.master_status.getSlave(slave_name) | |
505 if slave_status and not slave_status.isConnected(): | |
506 log.msg('[gatekeeper] Slave %s was disconnected, ' | |
507 'not closing the tree' % slave_name) | |
508 return False | |
509 | |
510 # If the previous build step failed with the same result, we don't care | |
511 # about this step. | |
512 previous_build_status = build_status.getPreviousBuild() | |
513 if previous_build_status: | |
514 step_name = self.getName(step_status) | |
515 step_type = self.getGenericName(step_name) | |
516 previous_steps = [step for step in previous_build_status.getSteps() | |
517 if self.getGenericName(self.getName(step)) == step_type] | |
518 if len(previous_steps) == 1: | |
519 if previous_steps[0].getResults()[0] == FAILURE: | |
520 log.msg('[gatekeeper] Slave %s failed, but previously failed on ' | |
521 'the same step (%s). So not closing tree.' % ( | |
522 (step_name, slave_name))) | |
523 return False | |
524 else: | |
525 log.msg('[gatekeeper] len(previous_steps) == %d which is weird' % | |
526 len(previous_steps)) | |
527 | |
528 # If check_revisions=False that means that the tree closure request is | |
529 # coming from nightly scheduled bots, that need not necessarily have the | |
530 # revision info. | |
531 if not self.check_revisions: | |
532 return True | |
533 | |
534 # If we don't have a version stamp nor a blame list, then this is most | |
535 # likely a build started manually, and we don't want to close the | |
536 # tree. | |
537 latest_revision = build_utils.getLatestRevision(build_status) | |
538 if not latest_revision or not build_status.getResponsibleUsers(): | |
539 log.msg('[gatekeeper] Slave %s failed, but no version stamp, ' | |
540 'so skipping.' % slave_name) | |
541 return False | |
542 | |
543 # If the tree is open, we don't want to close it again for the same | |
544 # revision, or an earlier one in case the build that just finished is a | |
545 # slow one and we already fixed the problem and manually opened the tree. | |
546 ############################### Added by rmistry ########################### | |
547 # rmistry: Commenting out the below SVN revision comparision because Skia | |
548 # uses commit hashes. | |
549 # TODO(rmistry): Figure out how to ensure that previous builds do not close | |
550 # the tree again. | |
551 # | |
552 # if latest_revision <= self._last_closure_revision: | |
553 # log.msg('[gatekeeper] Slave %s failed, but we already closed it ' | |
554 # 'for a previous revision (old=%s, new=%s)' % ( | |
555 # slave_name, str(self._last_closure_revision), | |
556 # str(latest_revision))) | |
557 # return False | |
558 ########################################################################### | |
559 | |
560 log.msg('[gatekeeper] Decided to close tree because of slave %s ' | |
561 'on revision %s' % (slave_name, str(latest_revision))) | |
562 | |
563 # Up to here, in theory we'd check if the tree is closed but this is too | |
564 # slow to check here. Instead, take a look only when we want to close the | |
565 # tree. | |
566 return True | |
567 | |
568 | |
569 # Fix try_job_base.TryJobBase._EMAIL_VALIDATOR to handle *.info. This was fixed | |
570 # in https://codereview.chromium.org/216293005 but we need this monkeypatch to | |
571 # pick it up without a DEPS roll. | |
572 try_job_base.TryJobBase._EMAIL_VALIDATOR = re.compile( | |
573 r'[a-zA-Z0-9][a-zA-Z0-9\.\+\-\_]*@[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,}$') | |
574 | |
575 | |
576 # Add logging to GitPoller._stop_on_failure | |
577 def _stop_on_failure(self, f): | |
578 "utility method to stop the service when a failure occurs" | |
579 log.err('GitPoller stopping due to failure: %s' % str(f)) | |
580 if self.running: | |
581 d = defer.maybeDeferred(lambda : self.stopService()) | |
582 d.addErrback(log.err, 'while stopping broken GitPoller service') | |
583 return f | |
584 | |
585 GitPoller._stop_on_failure = _stop_on_failure | |
OLD | NEW |