monotone

monotone Mtn Source Tree

Root/contrib/ciabot_monotone_hookversion.py

  • Property mtn:execute set to true
1#!/usr/bin/env python
2#
3# Copyright (C) Nathaniel Smith <njs@pobox.com>
4# Timothy Brownawell <tbrownaw@gmail.com>
5# Thomas Moschny <thomas.moschny@gmx.de>
6# Licensed under the MIT license:
7# http://www.opensource.org/licenses/mit-license.html
8# I.e., do what you like, but keep copyright and there's NO WARRANTY.
9#
10# CIA bot client script for Monotone repositories, written in python. This
11# generates commit messages using CIA's XML commit format, and can deliver
12# them using either XML-RPC or email. Based on the script 'ciabot_svn.py' by
13# Micah Dowty <micah@navi.cx>.
14
15# This version is modified to be called by a server hook, instead of a cron job.
16
17# To use:
18# -- make a copy of it somewhere
19# -- edit the configuration values below
20# -- put the following in the server's monotonerc:
21# function note_netsync_revision_received(rid, rdat, certs)
22# local branch, author, changelog
23# for i, cert in pairs(certs)
24# do
25# if (cert.name == "branch") then
26# branch = cert.value
27# end
28# if (cert.name == "author") then
29# author = cert.value
30# end
31# if (cert.name == "changelog") then
32# changelog = cert.value
33# end
34# end
35# local exe = "/path/to/this/script"
36# wait(spawn(exe, rid, branch, author, changelog, rdat))
37# return
38# end
39
40class config:
41 def project_for_branch(self, branchname):
42 # Customize this to return your project name(s). If changes to the
43 # given branch are uninteresting -- i.e., changes to them should be
44 # ignored entirely -- then return the python constant None (which is
45 # distinct from the string "None", a valid but poor project name!).
46 #if branchname.startswith("net.venge.monotone-viz"):
47 # return "monotone-viz"
48 #elif branchname.startswith("net.venge.monotone.contrib.monotree"):
49 # return "monotree"
50 #else:
51 # return "monotone"
52 return "FIXME"
53
54 # The server to deliver XML-RPC messages to, if using XML-RPC delivery.
55 xmlrpc_server = "http://cia.navi.cx"
56
57 # The email address to deliver messages to, if using email delivery.
58 smtp_address = "cia@cia.navi.cx"
59
60 # The SMTP server to connect to, if using email delivery.
61 smtp_server = "localhost"
62
63 # The 'from' address to put on email, if using email delivery.
64 from_address = "cia-user@FIXME"
65
66 # Set to one of "xmlrpc", "email", "debug".
67 delivery = "debug"
68
69################################################################################
70
71import sys
72import re
73import os
74
75def escape_for_xml(text, is_attrib=0):
76 text = text.replace("&", "&amp;")
77 text = text.replace("<", "&lt;")
78 text = text.replace(">", "&gt;")
79 if is_attrib:
80 text = text.replace("'", "&apos;")
81 text = text.replace("\"", "&quot;")
82 return text
83
84TOKEN = re.compile(r'''
85 "(?P<str>(\\\\|\\"|[^"])*)"
86 |\[(?P<id>[a-f0-9]{40}|)\]
87 |(?P<key>\w+)
88 |(?P<ws>\s+)
89''', re.VERBOSE)
90
91def parse_basic_io(raw):
92 parsed = []
93 key = None
94 for m in TOKEN.finditer(raw):
95 if m.lastgroup == 'key':
96 if key:
97 parsed.append((key, values))
98 key = m.group('key')
99 values = []
100 elif m.lastgroup == 'id':
101 values.append(m.group('id'))
102 elif m.lastgroup == 'str':
103 value = m.group('str')
104 # dequote: replace \" with "
105 value = re.sub(r'\\"', '"', value)
106 # dequote: replace \\ with \
107 value = re.sub(r'\\\\', r'\\', value)
108 values.append(value)
109 if key:
110 parsed.append((key, values))
111 return parsed
112
113def send_message(message, c):
114 if c.delivery == "debug":
115 print message
116 elif c.delivery == "xmlrpc":
117 import xmlrpclib
118 xmlrpclib.ServerProxy(c.xmlrpc_server).hub.deliver(message)
119 elif c.delivery == "email":
120 import smtplib
121 smtp = smtplib.SMTP(c.smtp_server)
122 smtp.sendmail(c.from_address, c.smtp_address,
123 "From: %s\r\nTo: %s\r\n"
124 "Subject: DeliverXML\r\n\r\n%s"
125 % (c.from_address, c.smtp_address, message))
126 else:
127 sys.exit("delivery option must be one of 'debug', 'xmlrpc', 'email'")
128
129def send_change_for(rid, branch, author, log, rev, c):
130 message_tmpl = """<message>
131 <generator>
132 <name>Monotone CIA Bot client python script</name>
133 <version>0.1</version>
134 </generator>
135 <source>
136 <project>%(project)s</project>
137 <branch>%(branch)s</branch>
138 </source>
139 <body>
140 <commit>
141 <revision>%(rid)s</revision>
142 <author>%(author)s</author>
143 <files>%(files)s</files>
144 <log>%(log)s</log>
145 </commit>
146 </body>
147</message>"""
148
149 substs = {}
150 files = []
151 for key, values in parse_basic_io(rev):
152 if key == 'old_revision':
153 # start a new changeset
154 oldpath = None
155 if key == 'delete':
156 files.append('<file action="remove">%s</file>'
157 % escape_for_xml(values[0]))
158 elif key == 'rename':
159 oldpath = values[0]
160 elif key == 'to':
161 if oldpath:
162 files.append('<file action="rename" to="%s">%s</file>'
163 % (escape_for_xml(values[0]), escape_for_xml(oldpath)))
164 oldpath = None
165 elif key == 'add_dir':
166 files.append('<file action="add">%s</file>'
167 % escape_for_xml(values[0] + '/'))
168 elif key == 'add_file':
169 files.append('<file action="add">%s</file>'
170 % escape_for_xml(values[0]))
171 elif key == 'patch':
172 files.append('<file action="modify">%s</file>'
173 % escape_for_xml(values[0]))
174
175 substs["files"] = "\n".join(files)
176 changelog = log.strip()
177 project = c.project_for_branch(branch)
178 if project is None:
179 return
180 substs["author"] = escape_for_xml(author)
181 substs["project"] = escape_for_xml(project)
182 substs["branch"] = escape_for_xml(branch)
183 substs["rid"] = escape_for_xml(rid)
184 substs["log"] = escape_for_xml(changelog)
185
186 message = message_tmpl % substs
187 send_message(message, c)
188
189def main(progname, args):
190 if len(args) != 5:
191 sys.exit("Usage: %s revid branch author changelog revision_text" % (progname, ))
192 # We don't want to clutter the process table with zombies; but we also
193 # don't want to force the monotone server to wait around while we call the
194 # CIA server. So we fork -- the original process exits immediately, and
195 # the child continues (orphaned, so it will eventually be reaped by init).
196 if hasattr(os, "fork"):
197 if os.fork():
198 return
199 (rid, branch, author, log, rev, ) = args
200 c = config()
201 send_change_for(rid, branch, author, log, rev, c)
202
203if __name__ == "__main__":
204 main(sys.argv[0], sys.argv[1:])

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status