#!/usr/bin/env python # # Copyright (C) Nathaniel Smith # Timothy Brownawell # Thomas Moschny # Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.html # I.e., do what you like, but keep copyright and there's NO WARRANTY. # # CIA bot client script for Monotone repositories, written in python. This # generates commit messages using CIA's XML commit format, and can deliver # them using either XML-RPC or email. Based on the script 'ciabot_svn.py' by # Micah Dowty . # This version is modified to be called by a server hook, instead of a cron job. # To use: # -- make a copy of it somewhere # -- edit the configuration values below # -- include ciabot_monotone_hookversion.lua in the server's monotonerc: class config: def project_for_branch(self, branchname): # Customize this to return your project name(s). If changes to the # given branch are uninteresting -- i.e., changes to them should be # ignored entirely -- then return the python constant None (which is # distinct from the string "None", a valid but poor project name!). #if branchname.startswith("net.venge.monotone-viz"): # return "monotone-viz" #elif branchname.startswith("net.venge.monotone.contrib.monotree"): # return "monotree" #elif branchname.startswith("net.venge.monotone"): # return "monotone" return "FIXME" # The server to deliver XML-RPC messages to, if using XML-RPC delivery. xmlrpc_server = "http://cia.navi.cx" # The email address to deliver messages to, if using email delivery. smtp_address = "cia@cia.navi.cx" # The SMTP server to connect to, if using email delivery. smtp_server = "localhost" # The 'from' address to put on email, if using email delivery. from_address = "cia-user@FIXME" # Set to one of "xmlrpc", "email", "debug". delivery = "debug" ################################################################################ import sys import re import os def escape_for_xml(text, is_attrib=0): text = text.replace("&", "&") text = text.replace("<", "<") text = text.replace(">", ">") if is_attrib: text = text.replace("'", "'") text = text.replace("\"", """) return text TOKEN = re.compile(r''' "(?P(\\\\|\\"|[^"])*)" |\[(?P[a-f0-9]{40}|)\] |(?P\w+) |(?P\s+) ''', re.VERBOSE) def parse_basic_io(raw): parsed = [] key = None for m in TOKEN.finditer(raw): if m.lastgroup == 'key': if key: parsed.append((key, values)) key = m.group('key') values = [] elif m.lastgroup == 'id': values.append(m.group('id')) elif m.lastgroup == 'str': value = m.group('str') # dequote: replace \" with " value = re.sub(r'\\"', '"', value) # dequote: replace \\ with \ value = re.sub(r'\\\\', r'\\', value) values.append(value) if key: parsed.append((key, values)) return parsed def send_message(message, c): if c.delivery == "debug": print message elif c.delivery == "xmlrpc": import xmlrpclib xmlrpclib.ServerProxy(c.xmlrpc_server).hub.deliver(message) elif c.delivery == "email": import smtplib smtp = smtplib.SMTP(c.smtp_server) smtp.sendmail(c.from_address, c.smtp_address, "From: %s\r\nTo: %s\r\n" "Subject: DeliverXML\r\n\r\n%s" % (c.from_address, c.smtp_address, message)) else: sys.exit("delivery option must be one of 'debug', 'xmlrpc', 'email'") def send_change_for(rid, branch, author, log, rev, c): message_tmpl = """ Monotone CIA Bot client python script 0.1 %(project)s %(branch)s %(rid)s %(author)s %(files)s %(log)s """ substs = {} files = [] for key, values in parse_basic_io(rev): if key == 'old_revision': # start a new changeset oldpath = None if key == 'delete': files.append('%s' % escape_for_xml(values[0])) elif key == 'rename': oldpath = values[0] elif key == 'to': if oldpath: files.append('%s' % (escape_for_xml(values[0]), escape_for_xml(oldpath))) oldpath = None elif key == 'add_dir': files.append('%s' % escape_for_xml(values[0] + '/')) elif key == 'add_file': files.append('%s' % escape_for_xml(values[0])) elif key == 'patch': files.append('%s' % escape_for_xml(values[0])) substs["files"] = "\n".join(files) changelog = log.strip() project = c.project_for_branch(branch) if project is None: return substs["author"] = escape_for_xml(author) substs["project"] = escape_for_xml(project) substs["branch"] = escape_for_xml(branch) substs["rid"] = escape_for_xml(rid) substs["log"] = escape_for_xml(changelog) message = message_tmpl % substs send_message(message, c) def main(progname, args): if len(args) != 5: sys.exit("Usage: %s revid branch author changelog revision_text" % (progname, )) # We don't want to clutter the process table with zombies; but we also # don't want to force the monotone server to wait around while we call the # CIA server. So we fork -- the original process exits immediately, and # the child continues (orphaned, so it will eventually be reaped by init). if hasattr(os, "fork"): if os.fork(): return (rid, branch, author, log, rev, ) = args c = config() send_change_for(rid, branch, author, log, rev, c) if __name__ == "__main__": main(sys.argv[0], sys.argv[1:])