launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #22442
[Merge] lp:~cjwatson/lpbuildbot/git-poller into lp:lpbuildbot
Colin Watson has proposed merging lp:~cjwatson/lpbuildbot/git-poller into lp:lpbuildbot.
Commit message:
Add a GitPoller implementation.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/lpbuildbot/git-poller/+merge/344977
This is a bit different from buildbot's version, in that we're expecting a merge workflow (at least at the moment) and so only want to consider left-hand parents rather than every merged revision for the purpose of things like AggregatingScheduler. It still involves a fair bit less custom code than BzrPoller though.
I haven't yet plumbed this into master.cfg; that can come when we're ready.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/lpbuildbot/git-poller into lp:lpbuildbot.
=== modified file 'bzrbuildbot/poller.py'
--- bzrbuildbot/poller.py 2018-05-02 16:52:29 +0000
+++ bzrbuildbot/poller.py 2018-05-02 19:41:26 +0000
@@ -1,19 +1,27 @@
-import bzrlib.branch
-import twisted.python.log
-import twisted.internet.defer
-import twisted.internet.reactor
-import twisted.internet.task
-import twisted.internet.threads
-
-import buildbot.util
-import buildbot.changes.base
-import buildbot.changes.changes
+import os
+
+from bzrlib.branch import Branch
+from twisted.internet import (
+ defer,
+ reactor,
+ task,
+ threads,
+ )
+from twisted.internet.utils import getProcessOutput
+from twisted.python import log
+
+from buildbot.changes.base import ChangeSource
+from buildbot.changes.changes import Change
+from buildbot.changes.gitpoller import GitPoller as _GitPoller
+from buildbot.util import (
+ ComparableMixin,
+ epoch2datetime,
+ )
import bzrbuildbot.change
-class BzrPoller(buildbot.changes.base.ChangeSource,
- buildbot.util.ComparableMixin):
+class BzrPoller(ChangeSource, ComparableMixin):
compare_attrs = ['urls']
@@ -30,7 +38,7 @@
self.last_revisions[url] = None
self.polling = False
self.poll_interval = poll_interval
- self.loop = twisted.internet.task.LoopingCall(self.poll)
+ self.loop = task.LoopingCall(self.poll)
self.blame_merge_author = blame_merge_author
self._objectids = {}
@@ -38,28 +46,27 @@
def urls(self):
return self.last_revisions.keys()
- @twisted.internet.defer.inlineCallbacks
+ @defer.inlineCallbacks
def getState(self, url, *args, **kwargs):
if self._objectids.get(url) is None:
self._objectids[url] = yield self.master.db.state.getObjectId(
url, self.__class__.__name__)
state = yield self.master.db.state.getState(
self._objectids[url], *args, **kwargs)
- twisted.internet.defer.returnValue(state)
+ defer.returnValue(state)
- @twisted.internet.defer.inlineCallbacks
+ @defer.inlineCallbacks
def setState(self, url, key, value):
if self._objectids.get(url) is None:
self._objectids[url] = yield self.master.db.state.getObjectId(
url, self.__class__.__name__)
yield self.master.db.state.setState(self._objectids[url], key, value)
- @twisted.internet.defer.inlineCallbacks
+ @defer.inlineCallbacks
def startService(self):
- twisted.python.log.msg("BzrPoller%r starting" % self.urls)
- buildbot.changes.base.ChangeSource.startService(self)
- twisted.internet.reactor.callWhenRunning(
- self.loop.start, self.poll_interval)
+ log.msg("BzrPoller%r starting" % self.urls)
+ ChangeSource.startService(self)
+ reactor.callWhenRunning(self.loop.start, self.poll_interval)
missing = len([v for v in self.last_revisions.values() if v is None])
if hasattr(self, 'master') and hasattr(self.master, 'db'):
for url in self.urls:
@@ -75,15 +82,15 @@
self.polling = False
def stopService(self):
- twisted.python.log.msg("BzrPoller%r shutting down" % self.urls)
+ log.msg("BzrPoller%r shutting down" % self.urls)
if self.loop.running:
self.loop.stop()
- return buildbot.changes.base.ChangeSource.stopService(self)
+ return ChangeSource.stopService(self)
def describe(self):
return "BzrPoller watching %r" % self.urls
- @twisted.internet.defer.inlineCallbacks
+ @defer.inlineCallbacks
def poll(self):
if self.polling: # this is called in a loop, and the loop might
# conceivably overlap.
@@ -97,30 +104,28 @@
finally:
self.polling = False
- @twisted.internet.defer.inlineCallbacks
+ @defer.inlineCallbacks
def _poll(self, url):
# On a big tree, even individual elements of the bzr commands can
# take awhile. So we just push the bzr work off to a thread.
try:
- changes = yield twisted.internet.threads.deferToThread(
- self.getRawChanges, url)
+ changes = yield threads.deferToThread(self.getRawChanges, url)
except (SystemExit, KeyboardInterrupt):
raise
except:
# we'll try again next poll. Meanwhile, let's report.
- twisted.python.log.err()
+ log.err()
else:
for change in changes:
change['branch'] = url
- yield self.addChange(
- buildbot.changes.changes.Change(**change))
+ yield self.addChange(Change(**change))
self.last_revisions[url] = change['revision']
if hasattr(self, 'master') and hasattr(self.master, 'db'):
yield self.setState(
url, 'last_revisions', self.last_revisions[url])
def getRawChanges(self, url):
- branch = bzrlib.branch.Branch.open_containing(url)[0]
+ branch = Branch.open_containing(url)[0]
try:
changes = []
change = bzrbuildbot.change.generate(
@@ -146,7 +151,6 @@
if hasattr(self.parent, 'addChange'):
return self.parent.addChange(change)
else:
- from buildbot.util import epoch2datetime
return self.master.addChange(
author=change.who,
revision=change.revision,
@@ -155,5 +159,83 @@
when_timestamp=epoch2datetime(change.when),
branch=change.branch,
src='bzr')
- twisted.internet.task.deferLater(
- twisted.internet.reactor, 0, _add_change)
+ task.deferLater(reactor, 0, _add_change)
+
+
+# Unfortunately we have to clone-and-hack quite a lot here, but
+# inlineCallbacks makes it slightly less painful.
+class GitPoller(_GitPoller):
+ """A variant of buildbot's GitPoller that only looks at left-hand parents.
+
+ We don't care about every revision in a branch that gets merged, only
+ the merge itself.
+ """
+
+ @defer.inlineCallbacks
+ def _get_commit_files(self, rev):
+ args = [
+ 'log', rev, '--name-only', '%s~..%s' % (rev, rev), r'--format=%n']
+ git_output = yield getProcessOutput(
+ self.gitbin, args, path=self.workdir,
+ env={'PATH': os.environ['PATH']}, errortoo=False)
+ fileList = sorted(set(git_output.split()))
+ defer.returnValue(fileList)
+
+ @defer.inlineCallbacks
+ def _get_commit_name(self, rev):
+ args = ['log', rev, '--no-walk', r'--format=%aN <%aE>']
+ git_output = yield getProcessOutput(
+ self.gitbin, args, path=self.workdir,
+ env={'PATH': os.environ['PATH']}, errortoo=False)
+ stripped_output = git_output.strip().decode(self.encoding)
+ if len(stripped_output) == 0:
+ raise EnvironmentError('could not get commit name for rev')
+ defer.returnValue(stripped_output)
+
+ @defer.inlineCallbacks
+ def _process_changes(self, unused_output):
+ revListArgs = [
+ 'log', '--first-parent',
+ '%s..origin/%s' % (self.branch, self.branch), r'--format=%H']
+ self.changeCount = 0
+ results = yield getProcessOutput(
+ self.gitbin, revListArgs, path=self.workdir,
+ env={'PATH': os.environ['PATH']}, errortoo=False)
+
+ # process oldest change first
+ revList = results.split()
+ if not revList:
+ return
+
+ revList.reverse()
+ self.changeCount = len(revList)
+
+ log.msg('gitpoller: processing %d changes: %s in "%s"'
+ % (self.changeCount, revList, self.workdir))
+
+ for rev in revList:
+ results = yield defer.DeferredList([
+ self._get_commit_timestamp(rev),
+ self._get_commit_name(rev),
+ self._get_commit_files(rev),
+ self._get_commit_comments(rev),
+ ], consumeErrors=True)
+
+ # check for failures
+ failures = [ r[1] for r in results if not r[0] ]
+ if failures:
+ # just fail on the first error; they're probably all related!
+ raise failures[0]
+
+ timestamp, name, files, comments = [ r[1] for r in results ]
+ yield self.master.addChange(
+ author=name,
+ revision=rev,
+ files=files,
+ comments=comments,
+ when_timestamp=epoch2datetime(timestamp),
+ branch=self.branch,
+ category=self.category,
+ project=self.project,
+ repository=self.repourl,
+ src='git')
Follow ups