diff options
Diffstat (limited to 'lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/changes/svnpoller.py')
-rw-r--r-- | lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/changes/svnpoller.py | 412 |
1 files changed, 0 insertions, 412 deletions
diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/changes/svnpoller.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/changes/svnpoller.py deleted file mode 100644 index 1e2fde81..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/changes/svnpoller.py +++ /dev/null @@ -1,412 +0,0 @@ -# This file is part of Buildbot. Buildbot 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, version 2. -# -# 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, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from __future__ import with_statement - - -# Based on the work of Dave Peticolas for the P4poll -# Changed to svn (using xml.dom.minidom) by Niklaus Giger -# Hacked beyond recognition by Brian Warner - -from twisted.python import log -from twisted.internet import defer, utils - -from buildbot import util -from buildbot.changes import base - -import xml.dom.minidom -import os, urllib - -# these split_file_* functions are available for use as values to the -# split_file= argument. -def split_file_alwaystrunk(path): - return dict(path=path) - -def split_file_branches(path): - # turn "trunk/subdir/file.c" into (None, "subdir/file.c") - # and "trunk/subdir/" into (None, "subdir/") - # and "trunk/" into (None, "") - # and "branches/1.5.x/subdir/file.c" into ("branches/1.5.x", "subdir/file.c") - # and "branches/1.5.x/subdir/" into ("branches/1.5.x", "subdir/") - # and "branches/1.5.x/" into ("branches/1.5.x", "") - pieces = path.split('/') - if len(pieces) > 1 and pieces[0] == 'trunk': - return (None, '/'.join(pieces[1:])) - elif len(pieces) > 2 and pieces[0] == 'branches': - return ('/'.join(pieces[0:2]), '/'.join(pieces[2:])) - else: - return None - -def split_file_projects_branches(path): - # turn projectname/trunk/subdir/file.c into dict(project=projectname, branch=trunk, path=subdir/file.c) - if not "/" in path: - return None - project, path = path.split("/", 1) - f = split_file_branches(path) - if f: - info = dict(project=project, path=f[1]) - if f[0]: - info['branch'] = f[0] - return info - return f - -class SVNPoller(base.PollingChangeSource, util.ComparableMixin): - """ - Poll a Subversion repository for changes and submit them to the change - master. - """ - - compare_attrs = ["svnurl", "split_file", - "svnuser", "svnpasswd", "project", - "pollInterval", "histmax", - "svnbin", "category", "cachepath"] - - parent = None # filled in when we're added - last_change = None - loop = None - - def __init__(self, svnurl, split_file=None, - svnuser=None, svnpasswd=None, - pollInterval=10*60, histmax=100, - svnbin='svn', revlinktmpl='', category=None, - project='', cachepath=None, pollinterval=-2, - extra_args=None): - - # for backward compatibility; the parameter used to be spelled with 'i' - if pollinterval != -2: - pollInterval = pollinterval - - base.PollingChangeSource.__init__(self, name=svnurl, pollInterval=pollInterval) - - if svnurl.endswith("/"): - svnurl = svnurl[:-1] # strip the trailing slash - self.svnurl = svnurl - self.extra_args = extra_args - self.split_file = split_file or split_file_alwaystrunk - self.svnuser = svnuser - self.svnpasswd = svnpasswd - - self.revlinktmpl = revlinktmpl - - self.environ = os.environ.copy() # include environment variables - # required for ssh-agent auth - - self.svnbin = svnbin - self.histmax = histmax - self._prefix = None - self.category = category - self.project = project - - self.cachepath = cachepath - if self.cachepath and os.path.exists(self.cachepath): - try: - with open(self.cachepath, "r") as f: - self.last_change = int(f.read().strip()) - log.msg("SVNPoller: SVNPoller(%s) setting last_change to %s" % (self.svnurl, self.last_change)) - # try writing it, too - with open(self.cachepath, "w") as f: - f.write(str(self.last_change)) - except: - self.cachepath = None - log.msg(("SVNPoller: SVNPoller(%s) cache file corrupt or unwriteable; " + - "skipping and not using") % self.svnurl) - log.err() - - def describe(self): - return "SVNPoller: watching %s" % self.svnurl - - def poll(self): - # Our return value is only used for unit testing. - - # we need to figure out the repository root, so we can figure out - # repository-relative pathnames later. Each SVNURL is in the form - # (ROOT)/(PROJECT)/(BRANCH)/(FILEPATH), where (ROOT) is something - # like svn://svn.twistedmatrix.com/svn/Twisted (i.e. there is a - # physical repository at /svn/Twisted on that host), (PROJECT) is - # something like Projects/Twisted (i.e. within the repository's - # internal namespace, everything under Projects/Twisted/ has - # something to do with Twisted, but these directory names do not - # actually appear on the repository host), (BRANCH) is something like - # "trunk" or "branches/2.0.x", and (FILEPATH) is a tree-relative - # filename like "twisted/internet/defer.py". - - # our self.svnurl attribute contains (ROOT)/(PROJECT) combined - # together in a way that we can't separate without svn's help. If the - # user is not using the split_file= argument, then self.svnurl might - # be (ROOT)/(PROJECT)/(BRANCH) . In any case, the filenames we will - # get back from 'svn log' will be of the form - # (PROJECT)/(BRANCH)/(FILEPATH), but we want to be able to remove - # that (PROJECT) prefix from them. To do this without requiring the - # user to tell us how svnurl is split into ROOT and PROJECT, we do an - # 'svn info --xml' command at startup. This command will include a - # <root> element that tells us ROOT. We then strip this prefix from - # self.svnurl to determine PROJECT, and then later we strip the - # PROJECT prefix from the filenames reported by 'svn log --xml' to - # get a (BRANCH)/(FILEPATH) that can be passed to split_file() to - # turn into separate BRANCH and FILEPATH values. - - # whew. - - if self.project: - log.msg("SVNPoller: polling " + self.project) - else: - log.msg("SVNPoller: polling") - - d = defer.succeed(None) - if not self._prefix: - d.addCallback(lambda _ : self.get_prefix()) - def set_prefix(prefix): - self._prefix = prefix - d.addCallback(set_prefix) - - d.addCallback(self.get_logs) - d.addCallback(self.parse_logs) - d.addCallback(self.get_new_logentries) - d.addCallback(self.create_changes) - d.addCallback(self.submit_changes) - d.addCallback(self.finished_ok) - d.addErrback(log.err, 'SVNPoller: Error in while polling') # eat errors - return d - - def getProcessOutput(self, args): - # this exists so we can override it during the unit tests - d = utils.getProcessOutput(self.svnbin, args, self.environ) - return d - - def get_prefix(self): - args = ["info", "--xml", "--non-interactive", self.svnurl] - if self.svnuser: - args.extend(["--username=%s" % self.svnuser]) - if self.svnpasswd: - args.extend(["--password=%s" % self.svnpasswd]) - if self.extra_args: - args.extend(self.extra_args) - d = self.getProcessOutput(args) - def determine_prefix(output): - try: - doc = xml.dom.minidom.parseString(output) - except xml.parsers.expat.ExpatError: - log.msg("SVNPoller: SVNPoller._determine_prefix_2: ExpatError in '%s'" - % output) - raise - rootnodes = doc.getElementsByTagName("root") - if not rootnodes: - # this happens if the URL we gave was already the root. In this - # case, our prefix is empty. - self._prefix = "" - return self._prefix - rootnode = rootnodes[0] - root = "".join([c.data for c in rootnode.childNodes]) - # root will be a unicode string - if not self.svnurl.startswith(root): - log.msg(format="svnurl='%(svnurl)s' doesn't start with <root>='%(root)s'", - svnurl=self.svnurl, root=root) - raise RuntimeError("Can't handle redirected svn connections!? " - "This shouldn't happen.") - prefix = self.svnurl[len(root):] - if prefix.startswith("/"): - prefix = prefix[1:] - log.msg("SVNPoller: svnurl=%s, root=%s, so prefix=%s" % - (self.svnurl, root, prefix)) - return prefix - d.addCallback(determine_prefix) - return d - - def get_logs(self, _): - args = [] - args.extend(["log", "--xml", "--verbose", "--non-interactive"]) - if self.svnuser: - args.extend(["--username=%s" % self.svnuser]) - if self.svnpasswd: - args.extend(["--password=%s" % self.svnpasswd]) - if self.extra_args: - args.extend(self.extra_args) - args.extend(["--limit=%d" % (self.histmax), self.svnurl]) - d = self.getProcessOutput(args) - return d - - def parse_logs(self, output): - # parse the XML output, return a list of <logentry> nodes - try: - doc = xml.dom.minidom.parseString(output) - except xml.parsers.expat.ExpatError: - log.msg("SVNPoller: SVNPoller.parse_logs: ExpatError in '%s'" % output) - raise - logentries = doc.getElementsByTagName("logentry") - return logentries - - - def get_new_logentries(self, logentries): - last_change = old_last_change = self.last_change - - # given a list of logentries, calculate new_last_change, and - # new_logentries, where new_logentries contains only the ones after - # last_change - - new_last_change = None - new_logentries = [] - if logentries: - new_last_change = int(logentries[0].getAttribute("revision")) - - if last_change is None: - # if this is the first time we've been run, ignore any changes - # that occurred before now. This prevents a build at every - # startup. - log.msg('SVNPoller: starting at change %s' % new_last_change) - elif last_change == new_last_change: - # an unmodified repository will hit this case - log.msg('SVNPoller: no changes') - else: - for el in logentries: - if last_change == int(el.getAttribute("revision")): - break - new_logentries.append(el) - new_logentries.reverse() # return oldest first - - self.last_change = new_last_change - log.msg('SVNPoller: _process_changes %s .. %s' % - (old_last_change, new_last_change)) - return new_logentries - - - def _get_text(self, element, tag_name): - try: - child_nodes = element.getElementsByTagName(tag_name)[0].childNodes - text = "".join([t.data for t in child_nodes]) - except: - text = "<unknown>" - return text - - def _transform_path(self, path): - if not path.startswith(self._prefix): - log.msg(format="SVNPoller: ignoring path '%(path)s' which doesn't" - "start with prefix '%(prefix)s'", - path=path, prefix=self._prefix) - return - relative_path = path[len(self._prefix):] - if relative_path.startswith("/"): - relative_path = relative_path[1:] - where = self.split_file(relative_path) - # 'where' is either None, (branch, final_path) or a dict - if not where: - return - if isinstance(where, tuple): - where = dict(branch=where[0], path=where[1]) - return where - - def create_changes(self, new_logentries): - changes = [] - - for el in new_logentries: - revision = str(el.getAttribute("revision")) - - revlink='' - - if self.revlinktmpl: - if revision: - revlink = self.revlinktmpl % urllib.quote_plus(revision) - - log.msg("Adding change revision %s" % (revision,)) - author = self._get_text(el, "author") - comments = self._get_text(el, "msg") - # there is a "date" field, but it provides localtime in the - # repository's timezone, whereas we care about buildmaster's - # localtime (since this will get used to position the boxes on - # the Waterfall display, etc). So ignore the date field, and - # addChange will fill in with the current time - branches = {} - try: - pathlist = el.getElementsByTagName("paths")[0] - except IndexError: # weird, we got an empty revision - log.msg("ignoring commit with no paths") - continue - - for p in pathlist.getElementsByTagName("path"): - kind = p.getAttribute("kind") - action = p.getAttribute("action") - path = "".join([t.data for t in p.childNodes]) - # the rest of buildbot is certaily not yet ready to handle - # unicode filenames, because they get put in RemoteCommands - # which get sent via PB to the buildslave, and PB doesn't - # handle unicode. - path = path.encode("ascii") - if path.startswith("/"): - path = path[1:] - if kind == "dir" and not path.endswith("/"): - path += "/" - where = self._transform_path(path) - - # if 'where' is None, the file was outside any project that - # we care about and we should ignore it - if where: - branch = where.get("branch", None) - filename = where["path"] - if not branch in branches: - branches[branch] = { 'files': [], 'number_of_directories': 0} - if filename == "": - # root directory of branch - branches[branch]['files'].append(filename) - branches[branch]['number_of_directories'] += 1 - elif filename.endswith("/"): - # subdirectory of branch - branches[branch]['files'].append(filename[:-1]) - branches[branch]['number_of_directories'] += 1 - else: - branches[branch]['files'].append(filename) - - if not branches[branch].has_key('action'): - branches[branch]['action'] = action - - for key in ("repository", "project", "codebase"): - if key in where: - branches[branch][key] = where[key] - - for branch in branches.keys(): - action = branches[branch]['action'] - files = branches[branch]['files'] - - number_of_directories_changed = branches[branch]['number_of_directories'] - number_of_files_changed = len(files) - - if action == u'D' and number_of_directories_changed == 1 and number_of_files_changed == 1 and files[0] == '': - log.msg("Ignoring deletion of branch '%s'" % branch) - else: - chdict = dict( - author=author, - files=files, - comments=comments, - revision=revision, - branch=branch, - revlink=revlink, - category=self.category, - repository=branches[branch].get('repository', self.svnurl), - project=branches[branch].get('project', self.project), - codebase=branches[branch].get('codebase', None)) - changes.append(chdict) - - return changes - - @defer.inlineCallbacks - def submit_changes(self, changes): - for chdict in changes: - yield self.master.addChange(src='svn', **chdict) - - def finished_ok(self, res): - if self.cachepath: - with open(self.cachepath, "w") as f: - f.write(str(self.last_change)) - - log.msg("SVNPoller: finished polling %s" % res) - return res |