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 |