1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import os
17 import urllib
18 from twisted.python import log
19 from twisted.internet import defer, utils
20
21 from buildbot.changes import base
22 from buildbot.util import epoch2datetime
23 from buildbot.util.state import StateMixin
24 from buildbot import config
25
26 -class GitPoller(base.PollingChangeSource, StateMixin):
27 """This source will poll a remote git repo for changes and submit
28 them to the change master."""
29
30 compare_attrs = ["repourl", "branches", "workdir",
31 "pollInterval", "gitbin", "usetimestamps",
32 "category", "project"]
33
34 - def __init__(self, repourl, branches=None, branch=None,
35 workdir=None, pollInterval=10*60,
36 gitbin='git', usetimestamps=True,
37 category=None, project=None,
38 pollinterval=-2, fetch_refspec=None,
39 encoding='utf-8'):
74
84 d.addCallback(setLastRev)
85
86 d.addCallback(lambda _:
87 base.PollingChangeSource.startService(self))
88 d.addErrback(log.err, 'while initializing GitPoller repository')
89
90 return d
91
93 status = ""
94 if not self.master:
95 status = "[STOPPED - check log]"
96 str = ('GitPoller watching the remote git repository %s, branches: %s %s'
97 % (self.repourl, ', '.join(self.branches), status))
98 return str
99
100 @defer.inlineCallbacks
102 yield self._dovccmd('init', ['--bare', self.workdir])
103
104 refspecs = [
105 '+%s:%s'% (branch, self._localBranch(branch))
106 for branch in self.branches
107 ]
108 yield self._dovccmd('fetch',
109 [self.repourl] + refspecs, path=self.workdir)
110
111 revs = {}
112 for branch in self.branches:
113 try:
114 revs[branch] = rev = yield self._dovccmd('rev-parse',
115 [self._localBranch(branch)], path=self.workdir)
116 yield self._process_changes(rev, branch)
117 except:
118 log.err(_why="trying to poll branch %s of %s"
119 % (branch, self.repourl))
120
121 self.lastRev.update(revs)
122 yield self.setState('lastRev', self.lastRev)
123
132 d.addCallback(process)
133 return d
134
136
137 args = ['--no-walk', r'--format=%ct', rev, '--']
138 d = self._dovccmd('log', args, path=self.workdir)
139 def process(git_output):
140 if self.usetimestamps:
141 try:
142 stamp = float(git_output)
143 except Exception, e:
144 log.msg('gitpoller: caught exception converting output \'%s\' to timestamp' % git_output)
145 raise e
146 return stamp
147 else:
148 return None
149 d.addCallback(process)
150 return d
151
153 args = ['--name-only', '--no-walk', r'--format=%n', rev, '--']
154 d = self._dovccmd('log', args, path=self.workdir)
155 def process(git_output):
156 fileList = git_output.split()
157 return fileList
158 d.addCallback(process)
159 return d
160
162 args = ['--no-walk', r'--format=%aN <%aE>', rev, '--']
163 d = self._dovccmd('log', args, path=self.workdir)
164 def process(git_output):
165 git_output = git_output.decode(self.encoding)
166 if len(git_output) == 0:
167 raise EnvironmentError('could not get commit author for rev')
168 return git_output
169 d.addCallback(process)
170 return d
171
172 @defer.inlineCallbacks
174 """
175 Read changes since last change.
176
177 - Read list of commit hashes.
178 - Extract details from each commit.
179 - Add changes to database.
180 """
181
182 lastRev = self.lastRev.get(branch)
183 self.lastRev[branch] = newRev
184 if not lastRev:
185 return
186
187
188 revListArgs = [r'--format=%H', '%s..%s' % (lastRev, newRev), '--']
189 self.changeCount = 0
190 results = yield self._dovccmd('log', revListArgs, path=self.workdir)
191
192
193
194 revList = results.split()
195 revList.reverse()
196 self.changeCount = len(revList)
197
198 log.msg('gitpoller: processing %d changes: %s from "%s"'
199 % (self.changeCount, revList, self.repourl) )
200
201 for rev in revList:
202 dl = defer.DeferredList([
203 self._get_commit_timestamp(rev),
204 self._get_commit_author(rev),
205 self._get_commit_files(rev),
206 self._get_commit_comments(rev),
207 ], consumeErrors=True)
208
209 results = yield dl
210
211
212 failures = [ r[1] for r in results if not r[0] ]
213 if failures:
214
215 raise failures[0]
216
217 timestamp, author, files, comments = [ r[1] for r in results ]
218 yield self.master.addChange(
219 author=author,
220 revision=rev,
221 files=files,
222 comments=comments,
223 when_timestamp=epoch2datetime(timestamp),
224 branch=branch,
225 category=self.category,
226 project=self.project,
227 repository=self.repourl,
228 src='git')
229
230 - def _dovccmd(self, command, args, path=None):
231 d = utils.getProcessOutputAndValue(self.gitbin,
232 [command] + args, path=path, env=os.environ)
233 def _convert_nonzero_to_failure(res):
234 "utility to handle the result of getProcessOutputAndValue"
235 (stdout, stderr, code) = res
236 if code != 0:
237 raise EnvironmentError('command failed with exit code %d: %s'
238 % (code, stderr))
239 return stdout.strip()
240 d.addCallback(_convert_nonzero_to_failure)
241 return d
242
244 return "refs/buildbot/%s/%s" % (urllib.quote(self.repourl, ''), branch)
245