| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import datetime | 5 import datetime |
| 6 | 6 |
| 7 from buildbot.changes import base | 7 from buildbot.changes import base |
| 8 from buildbot.util import deferredLocked | 8 from buildbot.util import deferredLocked |
| 9 from twisted.python import log | 9 from twisted.python import log |
| 10 from twisted.internet import defer | 10 from twisted.internet import defer |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 | 27 |
| 28 @staticmethod | 28 @staticmethod |
| 29 def _parse_timestamp(tm): | 29 def _parse_timestamp(tm): |
| 30 tm = tm[:tm.index('.')+7] | 30 tm = tm[:tm.index('.')+7] |
| 31 return datetime.datetime.strptime(tm, '%Y-%m-%d %H:%M:%S.%f') | 31 return datetime.datetime.strptime(tm, '%Y-%m-%d %H:%M:%S.%f') |
| 32 | 32 |
| 33 def startService(self): | 33 def startService(self): |
| 34 self.initLastTimeStamp() | 34 self.initLastTimeStamp() |
| 35 base.PollingChangeSource.startService(self) | 35 base.PollingChangeSource.startService(self) |
| 36 | 36 |
| 37 def getChangeQuery(self): |
| 38 return 'status:open' |
| 39 |
| 37 @deferredLocked('initLock') | 40 @deferredLocked('initLock') |
| 38 def initLastTimeStamp(self): | 41 def initLastTimeStamp(self): |
| 39 log.msg('GerritPoller: Getting latest timestamp from gerrit server.') | 42 log.msg('GerritPoller: Getting latest timestamp from gerrit server.') |
| 40 path = '/changes/?q=status:open&n=1' | 43 path = '/changes/?q=%s&n=1' % self.getChangeQuery() |
| 41 d = self.agent.request('GET', path) | 44 d = self.agent.request('GET', path) |
| 42 def _get_timestamp(j): | 45 def _get_timestamp(j): |
| 43 if len(j) == 0: | 46 if len(j) == 0: |
| 44 self.last_timestamp = datetime.datetime.now() | 47 self.last_timestamp = datetime.datetime.now() |
| 45 else: | 48 else: |
| 46 self.last_timestamp = self._parse_timestamp(j[0]['updated']) | 49 self.last_timestamp = self._parse_timestamp(j[0]['updated']) |
| 47 d.addCallback(_get_timestamp) | 50 d.addCallback(_get_timestamp) |
| 48 return d | 51 return d |
| 49 | 52 |
| 50 def getChanges(self, sortkey=None): | 53 def getChanges(self, sortkey=None): |
| 51 path = '/changes/?q=status:open&n=10' | 54 path = '/changes/?q=%s&n=10' % self.getChangeQuery() |
| 52 if sortkey: | 55 if sortkey: |
| 53 path += '&N=%s' % sortkey | 56 path += '&N=%s' % sortkey |
| 54 return self.agent.request('GET', path) | 57 return self.agent.request('GET', path) |
| 55 | 58 |
| 59 def _is_interesting_comment(self, comment): |
| 60 return comment['message'].startswith('Uploaded patch set ') |
| 61 |
| 56 def checkForNewPatchset(self, change, since): | 62 def checkForNewPatchset(self, change, since): |
| 57 o_params = '&'.join('o=%s' % x for x in ( | 63 o_params = '&'.join('o=%s' % x for x in ( |
| 58 'MESSAGES', 'CURRENT_REVISION', 'CURRENT_COMMIT', 'ALL_FILES')) | 64 'MESSAGES', 'CURRENT_REVISION', 'CURRENT_COMMIT', 'ALL_FILES')) |
| 59 path = '/changes/%s?%s' % (change['_number'], o_params) | 65 path = '/changes/%s?%s' % (change['_number'], o_params) |
| 60 d = self.agent.request('GET', path) | 66 d = self.agent.request('GET', path) |
| 61 def _parse_messages(j): | 67 def _parse_messages(j): |
| 62 if not j or 'messages' not in j: | 68 if not j or 'messages' not in j: |
| 63 return | 69 return |
| 64 for m in reversed(j['messages']): | 70 for m in reversed(j['messages']): |
| 65 if self._parse_timestamp(m['date']) <= since: | 71 if self._parse_timestamp(m['date']) <= since: |
| 66 break | 72 break |
| 67 if m['message'].startswith('Uploaded patch set '): | 73 if self._is_interesting_comment(m): |
| 68 return j | 74 return j, m |
| 69 d.addCallback(_parse_messages) | 75 d.addCallback(_parse_messages) |
| 70 return d | 76 return d |
| 71 | 77 |
| 72 def createBuildbotChange(self, change): | 78 def addBuildbotChange(self, change, comment): |
| 73 revision = change['revisions'].values()[0] | 79 revision = change['revisions'].values()[0] |
| 74 commit = revision['commit'] | 80 commit = revision['commit'] |
| 75 properties = {'event.change.number': change['_number']} | 81 properties = {'event.change.number': change['_number']} |
| 76 if change['status'] == 'NEW': | 82 if change['status'] == 'NEW': |
| 77 ref = revision.get('fetch', {}).get('http', {}).get('ref') | 83 ref = revision.get('fetch', {}).get('http', {}).get('ref') |
| 78 if ref: | 84 if ref: |
| 79 properties['event.patchSet.ref'] = ref | 85 properties['event.patchSet.ref'] = ref |
| 80 elif change['status'] in ('SUBMITTED', 'MERGED'): | 86 elif change['status'] in ('SUBMITTED', 'MERGED'): |
| 81 properties['event.refUpdate.newRev'] = change['current_revision'] | 87 properties['event.refUpdate.newRev'] = change['current_revision'] |
| 82 chdict = { | 88 chdict = { |
| 83 'author': '%s <%s>' % ( | 89 'author': '%s <%s>' % ( |
| 84 commit['author']['name'], commit['author']['email']), | 90 commit['author']['name'], commit['author']['email']), |
| 85 'project': change['project'], | 91 'project': change['project'], |
| 86 'branch': change['branch'], | 92 'branch': change['branch'], |
| 87 'revision': change['current_revision'], | 93 'revision': change['current_revision'], |
| 88 'comments': commit['subject'], | 94 'comments': commit['subject'], |
| 89 'files': commit['files'].keys() if 'files' in commit else ['UNKNOWN'], | 95 'files': commit['files'].keys() if 'files' in commit else ['UNKNOWN'], |
| 90 'category': 'patchset-created', | 96 'category': 'patchset-created', |
| 91 'when_timestamp': self._parse_timestamp(commit['committer']['date']), | 97 'when_timestamp': self._parse_timestamp(commit['committer']['date']), |
| 92 'revlink': '%s://%s/#/c/%s' % ( | 98 'revlink': '%s://%s/#/c/%s' % ( |
| 93 self.agent.gerrit_protocol, self.agent.gerrit_host, | 99 self.agent.gerrit_protocol, self.agent.gerrit_host, |
| 94 change['_number']), | 100 change['_number']), |
| 95 'repository': '%s://%s/%s' % ( | 101 'repository': '%s://%s/%s' % ( |
| 96 self.agent.gerrit_protocol, self.agent.gerrit_host, | 102 self.agent.gerrit_protocol, self.agent.gerrit_host, |
| 97 change['project']), | 103 change['project']), |
| 98 'properties': properties} | 104 'properties': properties, |
| 105 } |
| 99 d = self.master.addChange(**chdict) | 106 d = self.master.addChange(**chdict) |
| 100 d.addErrback(log.err, 'GerritPoller: Could not add buildbot change for ' | 107 d.addErrback(log.err, 'GerritPoller: Could not add buildbot change for ' |
| 101 'gerrit change %s.' % revision['_number']) | 108 'gerrit change %s.' % revision['_number']) |
| 102 return d | 109 return d |
| 103 | 110 |
| 111 def addChange(self, (change, comment)): |
| 112 return self.addBuildbotChange(change, comment) |
| 113 |
| 104 def processChanges(self, j, since): | 114 def processChanges(self, j, since): |
| 105 need_more = bool(j) | 115 need_more = bool(j) |
| 106 for change in j: | 116 for change in j: |
| 107 tm = self._parse_timestamp(change['updated']) | 117 tm = self._parse_timestamp(change['updated']) |
| 108 if tm <= since: | 118 if tm <= since: |
| 109 need_more = False | 119 need_more = False |
| 110 break | 120 break |
| 111 if self.gerrit_projects and change['project'] not in self.gerrit_projects: | 121 if self.gerrit_projects and change['project'] not in self.gerrit_projects: |
| 112 continue | 122 continue |
| 113 d = self.checkForNewPatchset(change, since) | 123 d = self.checkForNewPatchset(change, since) |
| 114 d.addCallback(lambda x: self.createBuildbotChange(x) if x else None) | 124 d.addCallback(lambda x: self.addChange(x) if x else None) |
| 115 if need_more and j[-1].get('_more_changes'): | 125 if need_more and j[-1].get('_more_changes'): |
| 116 d = self.getChanges(sortkey=j[-1]['_sortkey']) | 126 d = self.getChanges(sortkey=j[-1]['_sortkey']) |
| 117 d.addCallback(self.processChanges, since=since) | 127 d.addCallback(self.processChanges, since=since) |
| 118 else: | 128 else: |
| 119 d = defer.succeed(None) | 129 d = defer.succeed(None) |
| 120 return d | 130 return d |
| 121 | 131 |
| 122 @deferredLocked('initLock') | 132 @deferredLocked('initLock') |
| 123 def poll(self): | 133 def poll(self): |
| 124 log.msg('GerritPoller: getting latest changes...') | 134 log.msg('GerritPoller: getting latest changes...') |
| 125 since = self.last_timestamp | 135 since = self.last_timestamp |
| 126 d = self.getChanges() | 136 d = self.getChanges() |
| 127 def _update_last_timestamp(j): | 137 def _update_last_timestamp(j): |
| 128 if j: | 138 if j: |
| 129 self.last_timestamp = self._parse_timestamp(j[0]['updated']) | 139 self.last_timestamp = self._parse_timestamp(j[0]['updated']) |
| 130 return j | 140 return j |
| 131 d.addCallback(_update_last_timestamp) | 141 d.addCallback(_update_last_timestamp) |
| 132 d.addCallback(self.processChanges, since=since) | 142 d.addCallback(self.processChanges, since=since) |
| 133 return d | 143 return d |
| OLD | NEW |