Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2014 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 import json | |
| 6 import re | |
| 7 | |
| 8 from twisted.internet import defer | |
| 9 from twisted.python import log | |
| 10 | |
| 11 from buildbot.schedulers.base import BaseScheduler | |
| 12 | |
| 13 from master.gerrit_poller import GerritPoller | |
| 14 | |
| 15 | |
| 16 class JobDefinition(object): | |
|
Vadim Sh.
2014/04/29 01:22:45
Consider using collections.namedtuple + factory fu
nodir
2014/04/29 04:01:00
Decided not to change this because I prefer
1) the
| |
| 17 """Describes a try job posted on Gerrit.""" | |
| 18 def __init__(self, builder_names=None): | |
| 19 # Force str type and remove empty builder names. | |
| 20 self.builder_names = [str(b) for b in builder_names or [] | |
| 21 if b] | |
|
Vadim Sh.
2014/04/29 01:22:45
'if b' can probably fit on previous line
also wrap
nodir
2014/04/29 04:01:00
I am not sure it fits the rules
https://engdoc.cor
Vadim Sh.
2014/04/29 18:17:39
Well, I can tell you we are not following this par
| |
| 22 | |
| 23 def __repr__(self): | |
| 24 return repr(self.__dict__) | |
| 25 | |
| 26 @staticmethod | |
| 27 def parse(text): | |
| 28 """Parses a try job definition.""" | |
| 29 text = text and text.strip() | |
| 30 if not text: | |
| 31 # Return an empty definition. | |
| 32 return JobDefinition() | |
|
Vadim Sh.
2014/04/29 01:22:45
Is it valuable? What happens if it is scheduled?
nodir
2014/04/29 04:01:00
Yes. The default list of builders is used
| |
| 33 | |
| 34 # Parse as json. | |
| 35 job = json.loads(text) | |
| 36 | |
| 37 # Convert to canonical form. | |
| 38 if isinstance(job, list): | |
| 39 # Treat a list as builder name list. | |
| 40 job = {'builderNames': job} | |
|
Vadim Sh.
2014/04/29 01:22:45
elif not isinstance(job, dict):
raise ValueError
nodir
2014/04/29 04:01:00
Done.
| |
| 41 | |
| 42 return JobDefinition(job.get('builderNames')) | |
| 43 | |
| 44 | |
| 45 class _TryJobGerritPoller(GerritPoller): | |
| 46 """Polls issues, creates changes and calls scheduler.submitJob. | |
| 47 | |
| 48 This class is a part of TryJobGerritScheduler implementation and not designed | |
| 49 to be used otherwise. | |
| 50 """ | |
| 51 | |
| 52 change_category = 'tryjob' | |
| 53 | |
| 54 MESSAGE_REGEX_TRYJOB = re.compile('Patch set \d+:\s+\!tryjob', re.I) | |
|
Vadim Sh.
2014/04/29 01:22:45
Each message contains 'Patch set XXX:' as a header
nodir
2014/04/29 04:01:00
It does
https://quickoffice-internal-review.google
| |
| 55 | |
| 56 def __init__(self, scheduler, gerrit_host, gerrit_projects=None, | |
| 57 pollInterval=None): | |
| 58 assert scheduler | |
| 59 GerritPoller.__init__(self, gerrit_host, gerrit_projects, pollInterval) | |
| 60 self.scheduler = scheduler | |
| 61 | |
| 62 def _is_interesting_comment(self, comment): # pylint: disable=R0201 | |
|
Vadim Sh.
2014/04/29 01:22:45
Do you still need this pylint disable here?
nodir
2014/04/29 04:01:00
Done.
| |
| 63 return self.MESSAGE_REGEX_TRYJOB.match(comment['message']) | |
| 64 | |
| 65 def getChangeQuery(self): | |
| 66 query = GerritPoller.getChangeQuery(self) | |
| 67 # Request only issues with TryJob=+1 label. | |
| 68 query += '+label:TryJob=%2B1' | |
| 69 return query | |
| 70 | |
| 71 def parseJob(self, comment): | |
| 72 """Parses a JobDefinition from a Gerrit comment.""" | |
| 73 msg = comment['message'] | |
| 74 tryjob_match = self.MESSAGE_REGEX_TRYJOB.match(msg) | |
| 75 assert tryjob_match | |
| 76 job_def_str = msg[tryjob_match.end():] | |
|
Vadim Sh.
2014/04/29 01:22:45
Use tryjob_match.group(<index>) here instead of .e
nodir
2014/04/29 04:01:00
Done.
| |
| 77 return JobDefinition.parse(job_def_str) | |
| 78 | |
| 79 @defer.inlineCallbacks | |
| 80 def addChange(self, (change, comment)): | |
| 81 """Parses a job, adds a change and calls self.scheduler.submitJob.""" | |
| 82 try: | |
| 83 job = self.parseJob(comment) | |
| 84 buildbotChange = yield self.addBuildbotChange(change, comment) | |
| 85 yield self.scheduler.submitJob(buildbotChange, job) | |
| 86 defer.returnValue(buildbotChange) | |
| 87 except Exception as e: | |
| 88 log.err('TryJobGerritPoller failed: %s' % e) | |
|
Vadim Sh.
2014/04/29 01:22:45
Do you need to reraise an exception? Won't caller
nodir
2014/04/29 04:01:00
added raise and tested. This prints a stack trace
| |
| 89 | |
| 90 | |
| 91 class TryJobGerritScheduler(BaseScheduler): | |
| 92 """Polls try jobs on Gerrit and creates buildsets.""" | |
| 93 def __init__(self, name, default_builder_names, gerrit_host, | |
| 94 gerrit_projects=None, pollInterval=None): | |
| 95 """Creates a new TryJobGerritScheduler. | |
| 96 | |
| 97 Args: | |
| 98 name: name of the scheduler. | |
| 99 default_builder_names: a list of builder names used in case a job didn't | |
| 100 specify any. | |
| 101 gerrit_host: URL to the Gerrit instance | |
| 102 gerrit_projects: Gerrit projects to filter issues. | |
| 103 pollInterval: frequency of polling. | |
| 104 """ | |
| 105 BaseScheduler.__init__(self, name, | |
| 106 builderNames=default_builder_names, | |
| 107 properties={}) | |
| 108 self.poller = _TryJobGerritPoller(self, gerrit_host, gerrit_projects, | |
| 109 pollInterval) | |
| 110 | |
| 111 def setServiceParent(self, parent): | |
| 112 BaseScheduler.setServiceParent(self, parent) | |
| 113 self.poller.master = self.master | |
| 114 self.poller.setServiceParent(self) | |
| 115 | |
| 116 def gotChange(self, *args, **kwargs): | |
| 117 """Do nothing because changes are processed by submitJob.""" | |
| 118 | |
| 119 @defer.inlineCallbacks | |
| 120 def submitJob(self, change, job): | |
| 121 bsid = yield self.addBuildsetForChanges( | |
| 122 reason='tryjob', | |
| 123 changeids=[change.number], | |
| 124 builderNames=job.builder_names, | |
| 125 properties=change.properties) | |
| 126 log.msg('Successfully submitted a Gerrit try job for %s: %s.' % | |
| 127 (change.who, job)) | |
| 128 defer.returnValue(bsid) | |
| OLD | NEW |