aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorNicolas Vigier <boklm@mageia.org>2011-01-07 17:43:33 +0000
committerNicolas Vigier <boklm@mageia.org>2011-01-07 17:43:33 +0000
commit297d906213901919950052d91375e5de046efed7 (patch)
tree25bfde86d950f5a81d6903ec12074676f0c89bff /modules
parentcadd536bfdce71c1547f87ddd3fdc6deeab6faaa (diff)
downloadpuppet-297d906213901919950052d91375e5de046efed7.tar
puppet-297d906213901919950052d91375e5de046efed7.tar.gz
puppet-297d906213901919950052d91375e5de046efed7.tar.bz2
puppet-297d906213901919950052d91375e5de046efed7.tar.xz
puppet-297d906213901919950052d91375e5de046efed7.zip
add cia bot svn hook
Diffstat (limited to 'modules')
-rw-r--r--modules/subversion/manifests/init.pp12
-rw-r--r--modules/subversion/templates/ciabot_svn.py398
2 files changed, 410 insertions, 0 deletions
diff --git a/modules/subversion/manifests/init.pp b/modules/subversion/manifests/init.pp
index 815e5103..f6b5db1c 100644
--- a/modules/subversion/manifests/init.pp
+++ b/modules/subversion/manifests/init.pp
@@ -123,6 +123,7 @@ class subversion {
define repository ($group = "svn",
$public = true,
$commit_mail = '',
+ $cia_post = true,
$syntax_check = '',
$extract_dir = '') {
# check permissions
@@ -177,6 +178,17 @@ class subversion {
}
}
+ if $cia_post {
+ file { "$name/hooks/post-commit.d/cia.vc":
+ ensure => present,
+ owner => root,
+ group => root,
+ mode => 755,
+ content => template("subversion/ciabot_svn.py"),
+ }
+
+ }
+
if $extract_dir {
file { "$name/hooks/post-commit.d/extract_dir":
ensure => present,
diff --git a/modules/subversion/templates/ciabot_svn.py b/modules/subversion/templates/ciabot_svn.py
new file mode 100644
index 00000000..ab7828b4
--- /dev/null
+++ b/modules/subversion/templates/ciabot_svn.py
@@ -0,0 +1,398 @@
+#!/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("&", "&amp;")
+ text = text.replace("<", "&lt;")
+ text = text.replace(">", "&gt;")
+ if isAttrib == 1:
+ text = text.replace("'", "&apos;")
+ text = text.replace("\"", "&quot;")
+ 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 ###