Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/buildbot/contrib/bzr_buildbot.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildbot/contrib/bzr_buildbot.py')
-rwxr-xr-xbuildbot/contrib/bzr_buildbot.py467
1 files changed, 0 insertions, 467 deletions
diff --git a/buildbot/contrib/bzr_buildbot.py b/buildbot/contrib/bzr_buildbot.py
deleted file mode 100755
index cc32350..0000000
--- a/buildbot/contrib/bzr_buildbot.py
+++ /dev/null
@@ -1,467 +0,0 @@
-# Copyright (C) 2008-2009 Canonical
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""\
-bzr buildbot integration
-========================
-
-This file contains both bzr commit/change hooks and a bzr poller.
-
-------------
-Requirements
-------------
-
-This has been tested with buildbot 0.7.9, bzr 1.10, and Twisted 8.1.0. It
-should work in subsequent releases.
-
-For the hook to work, Twisted must be installed in the same Python that bzr
-uses.
-
------
-Hooks
------
-
-To install, put this file in a bzr plugins directory (e.g.,
-~/.bazaar/plugins). Then, in one of your bazaar conf files (e.g.,
-~/.bazaar/locations.conf), set the location you want to connect with buildbot
-with these keys:
-
-- buildbot_on: one of 'commit', 'push, or 'change'. Turns the plugin on to
- report changes via commit, changes via push, or any changes to the trunk.
- 'change' is recommended.
-
-- buildbot_server: (required to send to a buildbot master) the URL of the
- buildbot master to which you will connect (as of this writing, the same
- server and port to which slaves connect).
-
-- buildbot_port: (optional, defaults to 9989) the port of the buildbot master
- to which you will connect (as of this writing, the same server and port to
- which slaves connect)
-
-- buildbot_pqm: (optional, defaults to not pqm) Normally, the user that
- commits the revision is the user that is responsible for the change. When
- run in a pqm (Patch Queue Manager, see https://launchpad.net/pqm)
- environment, the user that commits is the Patch Queue Manager, and the user
- that committed the *parent* revision is responsible for the change. To turn
- on the pqm mode, set this value to any of (case-insensitive) "Yes", "Y",
- "True", or "T".
-
-- buildbot_dry_run: (optional, defaults to not a dry run) Normally, the
- post-commit hook will attempt to communicate with the configured buildbot
- server and port. If this parameter is included and any of (case-insensitive)
- "Yes", "Y", "True", or "T", then the hook will simply print what it would
- have sent, but not attempt to contact the buildbot master.
-
-- buildbot_send_branch_name: (optional, defaults to not sending the branch
- name) If your buildbot's bzr source build step uses a repourl, do
- *not* turn this on. If your buildbot's bzr build step uses a baseURL, then
- you may set this value to any of (case-insensitive) "Yes", "Y", "True", or
- "T" to have the buildbot master append the branch name to the baseURL.
-
-When buildbot no longer has a hardcoded password, it will be a configuration
-option here as well.
-
-------
-Poller
-------
-
-Put this file somewhere that your buildbot configuration can import it. Even
-in the same directory as the master.cfg should work. Install the poller in
-the buildbot configuration as with any other change source. Minimally,
-provide a URL that you want to poll (bzr://, bzr+ssh://, or lp:), though make
-sure the buildbot user has necessary privileges. You may also want to specify
-these optional values.
-
-poll_interval: the number of seconds to wait between polls. Defaults to 10
- minutes.
-
-branch_name: any value to be used as the branch name. Defaults to None, or
- specify a string, or specify the constants from this file SHORT
- or FULL to get the short branch name or full branch address.
-
-blame_merge_author: normally, the user that commits the revision is the user
- that is responsible for the change. When run in a pqm
- (Patch Queue Manager, see https://launchpad.net/pqm)
- environment, the user that commits is the Patch Queue
- Manager, and the user that committed the merged, *parent*
- revision is responsible for the change. set this value to
- True if this is pointed against a PQM-managed branch.
-
--------------------
-Contact Information
--------------------
-
-Maintainer/author: gary.poster@canonical.com
-"""
-
-try:
- import buildbot.util
- import buildbot.changes.base
- import buildbot.changes.changes
-except ImportError:
- DEFINE_POLLER = False
-else:
- DEFINE_POLLER = True
-import bzrlib.branch
-import bzrlib.errors
-import bzrlib.trace
-import twisted.cred.credentials
-import twisted.internet.base
-import twisted.internet.defer
-import twisted.internet.reactor
-import twisted.internet.selectreactor
-import twisted.internet.task
-import twisted.internet.threads
-import twisted.python.log
-import twisted.spread.pb
-
-
-#############################################################################
-# This is the code that the poller and the hooks share.
-
-def generate_change(branch,
- old_revno=None, old_revid=None,
- new_revno=None, new_revid=None,
- blame_merge_author=False):
- """Return a dict of information about a change to the branch.
-
- Dict has keys of "files", "who", "comments", and "revision", as used by
- the buildbot Change (and the PBChangeSource).
-
- If only the branch is given, the most recent change is returned.
-
- If only the new_revno is given, the comparison is expected to be between
- it and the previous revno (new_revno -1) in the branch.
-
- Passing old_revid and new_revid is only an optimization, included because
- bzr hooks usually provide this information.
-
- blame_merge_author means that the author of the merged branch is
- identified as the "who", not the person who committed the branch itself.
- This is typically used for PQM.
- """
- change = {} # files, who, comments, revision; NOT branch (= branch.nick)
- if new_revno is None:
- new_revno = branch.revno()
- if new_revid is None:
- new_revid = branch.get_rev_id(new_revno)
- # TODO: This falls over if this is the very first revision
- if old_revno is None:
- old_revno = new_revno -1
- if old_revid is None:
- old_revid = branch.get_rev_id(old_revno)
- repository = branch.repository
- new_rev = repository.get_revision(new_revid)
- if blame_merge_author:
- # this is a pqm commit or something like it
- change['who'] = repository.get_revision(
- new_rev.parent_ids[-1]).get_apparent_author()
- else:
- change['who'] = new_rev.get_apparent_author()
- # maybe useful to know:
- # name, email = bzrtools.config.parse_username(change['who'])
- change['comments'] = new_rev.message
- change['revision'] = new_revno
- files = change['files'] = []
- changes = repository.revision_tree(new_revid).changes_from(
- repository.revision_tree(old_revid))
- for (collection, name) in ((changes.added, 'ADDED'),
- (changes.removed, 'REMOVED'),
- (changes.modified, 'MODIFIED')):
- for info in collection:
- path = info[0]
- kind = info[2]
- files.append(' '.join([path, kind, name]))
- for info in changes.renamed:
- oldpath, newpath, id, kind, text_modified, meta_modified = info
- elements = [oldpath, kind,'RENAMED', newpath]
- if text_modified or meta_modified:
- elements.append('MODIFIED')
- files.append(' '.join(elements))
- return change
-
-#############################################################################
-# poller
-
-# We don't want to make the hooks unnecessarily depend on buildbot being
-# installed locally, so we conditionally create the BzrPoller class.
-if DEFINE_POLLER:
-
- FULL = object()
- SHORT = object()
-
-
- class BzrPoller(buildbot.changes.base.ChangeSource,
- buildbot.util.ComparableMixin):
-
- compare_attrs = ['url']
-
- def __init__(self, url, poll_interval=10*60, blame_merge_author=False,
- branch_name=None):
- # poll_interval is in seconds, so default poll_interval is 10
- # minutes.
- # bzr+ssh://bazaar.launchpad.net/~launchpad-pqm/launchpad/devel/
- # works, lp:~launchpad-pqm/launchpad/devel/ doesn't without help.
- if url.startswith('lp:'):
- url = 'bzr+ssh://bazaar.launchpad.net/' + url[3:]
- self.url = url
- self.poll_interval = poll_interval
- self.loop = twisted.internet.task.LoopingCall(self.poll)
- self.blame_merge_author = blame_merge_author
- self.branch_name = branch_name
-
- def startService(self):
- twisted.python.log.msg("BzrPoller(%s) starting" % self.url)
- buildbot.changes.base.ChangeSource.startService(self)
- twisted.internet.reactor.callWhenRunning(
- self.loop.start, self.poll_interval)
- for change in reversed(self.parent.changes):
- if change.branch == self.url:
- self.last_revision = change.revision
- break
- else:
- self.last_revision = None
- self.polling = False
-
- def stopService(self):
- twisted.python.log.msg("BzrPoller(%s) shutting down" % self.url)
- self.loop.stop()
- return buildbot.changes.base.ChangeSource.stopService(self)
-
- def describe(self):
- return "BzrPoller watching %s" % self.url
-
- @twisted.internet.defer.inlineCallbacks
- def poll(self):
- if self.polling: # this is called in a loop, and the loop might
- # conceivably overlap.
- return
- self.polling = True
- try:
- # 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)
- except (SystemExit, KeyboardInterrupt):
- raise
- except:
- # we'll try again next poll. Meanwhile, let's report.
- twisted.python.log.err()
- else:
- for change in changes:
- yield self.addChange(
- buildbot.changes.changes.Change(**change))
- self.last_revision = change['revision']
- finally:
- self.polling = False
-
- def getRawChanges(self):
- branch = bzrlib.branch.Branch.open_containing(self.url)[0]
- if self.branch_name is FULL:
- branch_name = self.url
- elif self.branch_name is SHORT:
- branch_name = branch.nick
- else: # presumably a string or maybe None
- branch_name = self.branch_name
- changes = []
- change = generate_change(
- branch, blame_merge_author=self.blame_merge_author)
- if (self.last_revision is None or
- change['revision'] > self.last_revision):
- change['branch'] = branch_name
- changes.append(change)
- if self.last_revision is not None:
- while self.last_revision + 1 < change['revision']:
- change = generate_change(
- branch, new_revno=change['revision']-1,
- blame_merge_author=self.blame_merge_author)
- change['branch'] = branch_name
- changes.append(change)
- changes.reverse()
- return changes
-
- def addChange(self, change):
- d = twisted.internet.defer.Deferred()
- def _add_change():
- d.callback(
- self.parent.addChange(change))
- twisted.internet.reactor.callLater(0, _add_change)
- return d
-
-#############################################################################
-# hooks
-
-HOOK_KEY = 'buildbot_on'
-SERVER_KEY = 'buildbot_server'
-PORT_KEY = 'buildbot_port'
-DRYRUN_KEY = 'buildbot_dry_run'
-PQM_KEY = 'buildbot_pqm'
-SEND_BRANCHNAME_KEY = 'buildbot_send_branch_name'
-
-PUSH_VALUE = 'push'
-COMMIT_VALUE = 'commit'
-CHANGE_VALUE = 'change'
-
-def _is_true(config, key):
- val = config.get_user_option(key)
- return val is not None and val.lower().strip() in (
- 'y', 'yes', 't', 'true')
-
-def _installed_hook(branch):
- value = branch.get_config().get_user_option(HOOK_KEY)
- if value is not None:
- value = value.strip().lower()
- if value not in (PUSH_VALUE, COMMIT_VALUE, CHANGE_VALUE):
- raise bzrlib.errors.BzrError(
- '%s, if set, must be one of %s, %s, or %s' % (
- HOOK_KEY, PUSH_VALUE, COMMIT_VALUE, CHANGE_VALUE))
- return value
-
-##########################
-# Work around Twisted bug.
-# See http://twistedmatrix.com/trac/ticket/3591
-import operator
-import socket
-from twisted.internet import defer
-from twisted.python import failure
-
-# replaces twisted.internet.thread equivalent
-def _putResultInDeferred(reactor, deferred, f, args, kwargs):
- """
- Run a function and give results to a Deferred.
- """
- try:
- result = f(*args, **kwargs)
- except:
- f = failure.Failure()
- reactor.callFromThread(deferred.errback, f)
- else:
- reactor.callFromThread(deferred.callback, result)
-
-# would be a proposed addition. deferToThread could use it
-def deferToThreadInReactor(reactor, f, *args, **kwargs):
- """
- Run function in thread and return result as Deferred.
- """
- d = defer.Deferred()
- reactor.callInThread(_putResultInDeferred, reactor, d, f, args, kwargs)
- return d
-
-# uses its own reactor for the threaded calls, unlike Twisted's
-class ThreadedResolver(twisted.internet.base.ThreadedResolver):
- def getHostByName(self, name, timeout = (1, 3, 11, 45)):
- if timeout:
- timeoutDelay = reduce(operator.add, timeout)
- else:
- timeoutDelay = 60
- userDeferred = defer.Deferred()
- lookupDeferred = deferToThreadInReactor(
- self.reactor, socket.gethostbyname, name)
- cancelCall = self.reactor.callLater(
- timeoutDelay, self._cleanup, name, lookupDeferred)
- self._runningQueries[lookupDeferred] = (userDeferred, cancelCall)
- lookupDeferred.addBoth(self._checkTimeout, name, lookupDeferred)
- return userDeferred
-##########################
-
-def send_change(branch, old_revno, old_revid, new_revno, new_revid, hook):
- config = branch.get_config()
- server = config.get_user_option(SERVER_KEY)
- if not server:
- bzrlib.trace.warning(
- 'bzr_buildbot: ERROR. If %s is set, %s must be set',
- HOOK_KEY, SERVER_KEY)
- return
- change = generate_change(
- branch, old_revno, old_revid, new_revno, new_revid,
- blame_merge_author=_is_true(config, PQM_KEY))
- if _is_true(config, SEND_BRANCHNAME_KEY):
- change['branch'] = branch.nick
- # as of this writing (in Buildbot 0.7.9), 9989 is the default port when
- # you make a buildbot master.
- port = int(config.get_user_option(PORT_KEY) or 9989)
- # if dry run, stop.
- if _is_true(config, DRYRUN_KEY):
- bzrlib.trace.note("bzr_buildbot DRY RUN "
- "(*not* sending changes to %s:%d on %s)",
- server, port, hook)
- keys = change.keys()
- keys.sort()
- for k in keys:
- bzrlib.trace.note("[%10s]: %s", k, change[k])
- return
- # We instantiate our own reactor so that this can run within a server.
- reactor = twisted.internet.selectreactor.SelectReactor()
- # See other reference to http://twistedmatrix.com/trac/ticket/3591
- # above. This line can go away with a release of Twisted that addresses
- # this issue.
- reactor.resolver = ThreadedResolver(reactor)
- pbcf = twisted.spread.pb.PBClientFactory()
- reactor.connectTCP(server, port, pbcf)
- deferred = pbcf.login(
- twisted.cred.credentials.UsernamePassword('change', 'changepw'))
-
- def sendChanges(remote):
- """Send changes to buildbot."""
- bzrlib.trace.mutter("bzrbuildout sending changes: %s", change)
- return remote.callRemote('addChange', change)
-
- deferred.addCallback(sendChanges)
-
- def quit(ignore, msg):
- bzrlib.trace.note("bzrbuildout: %s", msg)
- reactor.stop()
-
- def failed(failure):
- bzrlib.trace.warning("bzrbuildout: FAILURE\n %s", failure)
- reactor.stop()
-
- deferred.addCallback(quit, "SUCCESS")
- deferred.addErrback(failed)
- reactor.callLater(60, quit, None, "TIMEOUT")
- bzrlib.trace.note(
- "bzr_buildbot: SENDING CHANGES to buildbot master %s:%d on %s",
- server, port, hook)
- reactor.run(installSignalHandlers=False) # run in a thread when in server
-
-def post_commit(local_branch, master_branch, # branch is the master_branch
- old_revno, old_revid, new_revno, new_revid):
- if _installed_hook(master_branch) == COMMIT_VALUE:
- send_change(master_branch,
- old_revid, old_revid, new_revno, new_revid, COMMIT_VALUE)
-
-def post_push(result):
- if _installed_hook(result.target_branch) == PUSH_VALUE:
- send_change(result.target_branch,
- result.old_revid, result.old_revid,
- result.new_revno, result.new_revid, PUSH_VALUE)
-
-def post_change_branch_tip(result):
- if _installed_hook(result.branch) == CHANGE_VALUE:
- send_change(result.branch,
- result.old_revid, result.old_revid,
- result.new_revno, result.new_revid, CHANGE_VALUE)
-
-bzrlib.branch.Branch.hooks.install_named_hook(
- 'post_commit', post_commit,
- 'send change to buildbot master')
-bzrlib.branch.Branch.hooks.install_named_hook(
- 'post_push', post_push,
- 'send change to buildbot master')
-bzrlib.branch.Branch.hooks.install_named_hook(
- 'post_change_branch_tip', post_change_branch_tip,
- 'send change to buildbot master')