| 1 | # -*- test-case-name: buildbot.test.test_bzrpoller -*- |
|---|
| 2 | |
|---|
| 3 | from zope.interface import implements |
|---|
| 4 | from twisted.python import log |
|---|
| 5 | from twisted.application import service, internet |
|---|
| 6 | |
|---|
| 7 | from buildbot import util, interfaces |
|---|
| 8 | from buildbot.changes.changes import Change |
|---|
| 9 | |
|---|
| 10 | from bzrlib.branch import Branch |
|---|
| 11 | |
|---|
| 12 | class BzrPoller(service.MultiService, util.ComparableMixin): |
|---|
| 13 | """This source will poll a Bzr repository for changes and submit them to |
|---|
| 14 | the change master.""" |
|---|
| 15 | implements(interfaces.IChangeSource) |
|---|
| 16 | |
|---|
| 17 | compare_attrs = ["location", "pollinterval"] |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | parent = None # filled in when we're added |
|---|
| 21 | last_change = None |
|---|
| 22 | loop = None |
|---|
| 23 | working = False |
|---|
| 24 | |
|---|
| 25 | def __init__(self, location, pollinterval=10*60): |
|---|
| 26 | """ |
|---|
| 27 | @type location: string |
|---|
| 28 | @param location: the URL of the branch that this poller should watch. |
|---|
| 29 | This is typically an http: or sftp: URL. |
|---|
| 30 | |
|---|
| 31 | @type pollinterval: int |
|---|
| 32 | @param pollinterval: interval in seconds between polls. The default |
|---|
| 33 | is 600 seconds (10 minutes). Smaller values |
|---|
| 34 | decrease the latency between the time a change |
|---|
| 35 | is recorded and the time the buildbot notices |
|---|
| 36 | it, but it also increases the system load. |
|---|
| 37 | """ |
|---|
| 38 | service.MultiService.__init__(self) |
|---|
| 39 | |
|---|
| 40 | self.location = location |
|---|
| 41 | |
|---|
| 42 | self.pollinterval = pollinterval |
|---|
| 43 | self.overrun_counter = 0 |
|---|
| 44 | timer = internet.TimerService(pollinterval, self.poll) |
|---|
| 45 | timer.setServiceParent(self) |
|---|
| 46 | |
|---|
| 47 | def describe(self): |
|---|
| 48 | return "BzrPoller watching %s" % self.location |
|---|
| 49 | |
|---|
| 50 | def poll(self): |
|---|
| 51 | log.msg("BzrPoller polling") |
|---|
| 52 | # location="http://bazaar-vcs.org/bzr/bzr.dev" |
|---|
| 53 | b = Branch.open_containing(self.url)[0] |
|---|
| 54 | # this is subclass of bzrlib.branch.Branch |
|---|
| 55 | current_revision = b.revno() |
|---|
| 56 | # NOTE: b.revision_history() does network IO, and is blocking. |
|---|
| 57 | revisions = b.revision_history()[last_revno:] # each is an id string |
|---|
| 58 | changes = [] |
|---|
| 59 | for r in revisions: |
|---|
| 60 | rev = b.repository.get_revision(r) |
|---|
| 61 | # bzrlib.revision.Revision |
|---|
| 62 | who = rev.committer |
|---|
| 63 | comments = rev.message |
|---|
| 64 | when = rev.timestamp |
|---|
| 65 | # rev.timezone, interesting. Not sure it's used. |
|---|
| 66 | |
|---|
| 67 | d = b.repository.get_revision_delta(r) |
|---|
| 68 | # this is a delta.TreeDelta |
|---|
| 69 | files = ([f[0] for f in d.added] + |
|---|
| 70 | [f[0] for f in d.removed] + |
|---|
| 71 | [f[1] for f in d.renamed] + |
|---|
| 72 | [f[0] for f in d.modified] |
|---|
| 73 | ) |
|---|
| 74 | |
|---|
| 75 | # revision= ? |
|---|
| 76 | # branch= ? |
|---|
| 77 | c = Change(who=rev.committer, |
|---|
| 78 | files=files, |
|---|
| 79 | comments=rev.message, |
|---|
| 80 | when=rev.timestamp, |
|---|
| 81 | ) |
|---|
| 82 | changes.append(c) |
|---|
| 83 | for c in changes: |
|---|
| 84 | self.parent.addChange(c) |
|---|
| 85 | log.msg("BzrPoller finished polling, %d changes found" % len(changes)) |
|---|