diff options
Diffstat (limited to 'modules/subversion/templates/ciabot_svn.py')
-rw-r--r-- | modules/subversion/templates/ciabot_svn.py | 398 |
1 files changed, 0 insertions, 398 deletions
diff --git a/modules/subversion/templates/ciabot_svn.py b/modules/subversion/templates/ciabot_svn.py deleted file mode 100644 index ab7828b4..00000000 --- a/modules/subversion/templates/ciabot_svn.py +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/env python -# -# This is a CIA client script for Subversion repositories, written in python. -# It generates commit messages using CIA's XML format, and can deliver them -# using either XML-RPC or email. See below for usage and cuztomization -# information. -# -# -------------------------------------------------------------------------- -# -# Copyright (c) 2004-2007, Micah Dowty -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# -------------------------------------------------------------------------- -# -# This script is cleaner and much more featureful than the shell -# script version, but won't work on systems without Python. -# -# To use the CIA bot in your Subversion repository... -# -# 1. Customize the parameters below -# -# 2. This script should be called from your repository's post-commit -# hook with the repository and revision as arguments. For example, -# you could copy this script into your repository's "hooks" directory -# and add something like the following to the "post-commit" script, -# also in the repository's "hooks" directory: -# -# REPOS="$1" -# REV="$2" -# $REPOS/hooks/ciabot_svn.py "$REPOS" "$REV" & -# -# Or, if you have multiple project hosted, you can add each -# project's name to the commandline in that project's post-commit -# hook: -# -# $REPOS/hooks/ciabot_svn.py "$REPOS" "$REV" "ProjectName" & -# -############# There are some parameters for this script that you can customize: - -class config: - # Replace this with your project's name, or always provide a project - # name on the commandline. - # - # NOTE: This shouldn't be a long description of your project. Ideally - # it is a short identifier with no spaces, punctuation, or - # unnecessary capitalization. This will be used in URLs related - # to your project, as an internal identifier, and in IRC messages. - # If you want a longer name shown for your project on the web - # interface, please use the "title" metadata key rather than - # putting that here. - # - project = "Mageia" - - # Subversion's normal directory hierarchy is powerful enough that - # it doesn't have special methods of specifying modules, tags, or - # branches like CVS does. Most projects do use a naming - # convention though that works similarly to CVS's modules, tags, - # and branches. - # - # This is a list of regular expressions that are tested against - # paths in the order specified. If a regex matches, the 'branch' - # and 'module' groups are stored and the matching section of the - # path is removed. - # - # Several common directory structure styles are below as defaults. - # Uncomment the ones you're using, or add your own regexes. - # Whitespace in the each regex are ignored. - - pathRegexes = [ - # r"^ trunk/ (?P<module>[^/]+)/ ", - # r"^ (branches|tags)/ (?P<branch>[^/]+)/ ", - # r"^ (branches|tags)/ (?P<module>[^/]+)/ (?P<branch>[^/]+)/ ", - ] - - # If your repository is accessible over the web, put its base URL here - # and 'uri' attributes will be given to all <file> elements. This means - # that in CIA's online message viewer, each file in the tree will link - # directly to the file in your repository. - repositoryURI = None - - # If your repository is accessible over the web via a tool like ViewVC - # that allows viewing information about a full revision, put a format string - # for its URL here. You can specify various substitution keys in the Python - # syntax: "%(project)s" is replaced by the project name, and likewise - # "%(revision)s" and "%(author)s" are replaced by the revision / author. - # The resulting URI is added to the data sent to CIA. After this, in CIA's - # online message viewer, the commit will link directly to the corresponding - # revision page. - revisionURI = None - # Example (works for ViewVC as used by SourceForge.net): - #revisionURI = "https://svn.sourceforge.net/viewcvs.cgi/%(project)s?view=rev&rev=%(revision)s" - - # This can be the http:// URI of the CIA server to deliver commits over - # XML-RPC, or it can be an email address to deliver using SMTP. The - # default here should work for most people. If you need to use e-mail - # instead, you can replace this with "cia@cia.navi.cx" - server = "http://cia.navi.cx" - - # The SMTP server to use, only used if the CIA server above is an - # email address. - smtpServer = "localhost" - - # The 'from' address to use. If you're delivering commits via email, set - # this to the address you would normally send email from on this host. - fromAddress = "cia-user@localhost" - - # When nonzero, print the message to stdout instead of delivering it to CIA. - debug = 0 - - -############# Normally the rest of this won't need modification - -import sys, os, re, urllib, getopt - -class File: - """A file in a Subversion repository. According to our current - configuration, this may have a module, branch, and URI in addition - to a path.""" - - # Map svn's status letters to our action names - actionMap = { - 'U': 'modify', - 'A': 'add', - 'D': 'remove', - } - - def __init__(self, fullPath, status=None): - self.fullPath = fullPath - self.path = fullPath - self.action = self.actionMap.get(status) - - def getURI(self, repo): - """Get the URI of this file, given the repository's URI. This - encodes the full path and joins it to the given URI.""" - quotedPath = urllib.quote(self.fullPath) - if quotedPath[0] == '/': - quotedPath = quotedPath[1:] - if repo[-1] != '/': - repo = repo + '/' - return repo + quotedPath - - def makeTag(self, config): - """Return an XML tag for this file, using the given config""" - attrs = {} - - if config.repositoryURI is not None: - attrs['uri'] = self.getURI(config.repositoryURI) - - if self.action: - attrs['action'] = self.action - - attrString = ''.join([' %s="%s"' % (key, escapeToXml(value,1)) - for key, value in attrs.items()]) - return "<file%s>%s</file>" % (attrString, escapeToXml(self.path)) - - -class SvnClient: - """A CIA client for Subversion repositories. Uses svnlook to - gather information""" - name = 'Python Subversion client for CIA' - version = '1.20' - - def __init__(self, repository, revision, config): - self.repository = repository - self.revision = revision - self.config = config - - def deliver(self, message): - if config.debug: - print message - else: - server = self.config.server - if server.startswith('http:') or server.startswith('https:'): - # Deliver over XML-RPC - import xmlrpclib - xmlrpclib.ServerProxy(server).hub.deliver(message) - else: - # Deliver over email - import smtplib - smtp = smtplib.SMTP(self.config.smtpServer) - smtp.sendmail(self.config.fromAddress, server, - "From: %s\r\nTo: %s\r\n" - "Subject: DeliverXML\r\n\r\n%s" % - (self.config.fromAddress, server, message)) - - def main(self): - self.collectData() - self.deliver("<message>" + - self.makeGeneratorTag() + - self.makeSourceTag() + - self.makeBodyTag() + - "</message>") - - def makeAttrTags(self, *names): - """Given zero or more attribute names, generate XML elements for - those attributes only if they exist and are non-None. - """ - s = '' - for name in names: - if hasattr(self, name): - v = getattr(self, name) - if v is not None: - # Recent Pythons don't need this, but Python 2.1 - # at least can't convert other types directly - # to Unicode. We have to take an intermediate step. - if type(v) not in (type(''), type(u'')): - v = str(v) - - s += "<%s>%s</%s>" % (name, escapeToXml(v), name) - return s - - def makeGeneratorTag(self): - return "<generator>%s</generator>" % self.makeAttrTags( - 'name', - 'version', - ) - - def makeSourceTag(self): - return "<source>%s</source>" % self.makeAttrTags( - 'project', - 'module', - 'branch', - ) - - def makeBodyTag(self): - return "<body><commit>%s%s</commit></body>" % ( - self.makeAttrTags( - 'revision', - 'author', - 'log', - 'diffLines', - 'url', - ), - self.makeFileTags(), - ) - - def makeFileTags(self): - """Return XML tags for our file list""" - return "<files>%s</files>" % ''.join([file.makeTag(self.config) - for file in self.files]) - - def svnlook(self, command): - """Run the given svnlook command on our current repository and - revision, returning all output""" - # We have to set LC_ALL to force svnlook to give us UTF-8 output, - # then we explicitly slurp that into a unicode object. - return unicode(os.popen( - 'LC_ALL="en_US.UTF-8" svnlook %s -r "%s" "%s"' % - (command, self.revision, self.repository)).read(), - 'utf-8', 'replace') - - def collectData(self): - self.author = self.svnlook('author').strip() - self.project = self.config.project - self.log = self.svnlook('log') - self.diffLines = len(self.svnlook('diff').split('\n')) - self.files = self.collectFiles() - if self.config.revisionURI is not None: - self.url = self.config.revisionURI % self.__dict__ - else: - self.url = None - - def collectFiles(self): - # Extract all the files from the output of 'svnlook changed' - files = [] - for line in self.svnlook('changed').split('\n'): - path = line[2:].strip() - if path: - status = line[0] - files.append(File(path, status)) - - # Try each of our several regexes. To be applied, the same - # regex must mach every file under consideration and they must - # all return the same results. If we find one matching regex, - # or we try all regexes without a match, we're done. - matchDict = None - for regex in self.config.pathRegexes: - matchDict = matchAgainstFiles(regex, files) - if matchDict is not None: - self.__dict__.update(matchDict) - break - - return files - - -def matchAgainstFiles(regex, files): - """Try matching a regex against all File objects in the provided list. - If the regex returns the same matches for every file, the matches - are returned in a dict and the matched portions are filtered out. - If not, returns None. - """ - prevMatchDict = None - compiled = re.compile(regex, re.VERBOSE) - for f in files: - - match = compiled.match(f.fullPath) - if not match: - # Give up, it must match every file - return None - - matchDict = match.groupdict() - if prevMatchDict is not None and prevMatchDict != matchDict: - # Give up, we got conflicting matches - return None - - prevMatchDict = matchDict - - # If we got this far, the regex matched every file with - # the same results. Now filter the matched portion out of - # each file and store the matches we found. - for f in files: - f.path = compiled.sub('', f.fullPath) - return prevMatchDict - - -def escapeToXml(text, isAttrib=0): - text = unicode(text) - text = text.replace("&", "&") - text = text.replace("<", "<") - text = text.replace(">", ">") - if isAttrib == 1: - text = text.replace("'", "'") - text = text.replace("\"", """) - return text - - -def usage(): - """Print a short usage description of this script and exit""" - sys.stderr.write("Usage: %s [OPTIONS] REPOS-PATH REVISION [PROJECTNAME]\n" % - sys.argv[0]) - - -def version(): - """Print out the version of this script""" - sys.stderr.write("%s %s\n" % (sys.argv[0], SvnClient.version)) - - -def main(): - try: - options = [ "version" ] - for key in config.__dict__: - if not key.startswith("_"): - options.append(key + "="); - opts, args = getopt.getopt(sys.argv[1:], "", options) - except getopt.GetoptError: - usage() - sys.exit(2) - - for o, a in opts: - if o == "--version": - version() - sys.exit() - else: - # Everything else maps straight to a config key. Just have - # to remove the "--" prefix from the option name. - config.__dict__[o[2:]] = a - - # Print a usage message when not enough parameters are provided. - if not len(args) in (2,3): - sys.stderr.write("%s: incorrect number of arguments\n" % sys.argv[0]) - usage(); - sys.exit(2); - - # If a project name was provided, override the default project name. - if len(args) == 3: - config.project = args[2] - - # Go do the real work. - SvnClient(args[0], args[1], config).main() - - -if __name__ == "__main__": - main() - -### The End ### |