git-hacks

git-hacks Git Source Tree

Root/ciabot.py

1#!/usr/bin/env python2
2# Copyright (c) 2010 Eric S. Raymond <esr@thyrsus.com>
3# Copyright (c) 2010 Bernd Zeimetz <bzed@debian.org>
4# Copyright (c) 2011 Nicola Fontana <ntd@entidi.it>
5# Distributed under BSD terms.
6#
7# This script contains porcelain and porcelain byproducts.
8# It's Python because the Python standard libraries avoid portability/security
9# issues raised by callouts in the ancestral Perl and sh scripts. It should
10# be compatible back to Python 2.1.5
11#
12# usage: ciabot.py [-V] [-n] [-p projectname] [refname [commits...]]
13#
14# This script is meant to be run either in a post-commit hook or in an
15# update hook. If there's nothing unusual about your hosting setup,
16# you can specify the project name and repo with config variables and
17# avoid having to modify this script. Try it with -n to see the
18# notification mail dumped to stdout and verify that it looks
19# sane. With -V it dumps its version and exits.
20#
21# In post-commit, run it without arguments. It will query for
22# current HEAD and the latest commit ID to get the information it
23# needs.
24#
25# In update, call it with a refname followed by a list of commits:
26# You want to reverse the order git rev-list emits becxause it lists
27# from most recent to oldest.
28#
29# /path/to/ciabot.py ${refname} $(git rev-list ${oldhead}..${newhead} | tac)
30#
31# Configuration variables affecting this script:
32# hooks.cia-project = name of the project (required)
33# hooks.cia-branch = the branch the commits will be picked from
34# hooks.cia-xmlrpc = if true, ship notifications via XML-RPC
35# hooks.urlprefix = URL prefix where the commit id will be appended
36# hooks.fromaddr = From: field from which the email is sent
37# hooks.repo = name of the project repo for gitweb/cgit purposes
38# hooks.revformat = format in which the revision is shown
39#
40# The ciabot.repo defaults to ciabot.project lowercased.
41#
42# The revformat variable may have the following values
43# raw -> full hex ID of commit
44# short -> first 12 chars of hex ID
45# describe = -> desescription relative to last tag, falling back to short
46# The default is 'describe'.
47#
48# Note: the shell ancestors of this script used mail, not XML-RPC, in
49# order to avoid stalling until timeout when the CIA XML-RPC server is
50# down. It is unknown whether this is still an issue in 2010, but we
51# default to mail just in case. (Using XML-RPC guarantees that multiple
52# notifications shipped from a commit hook will arrive in order.)
53#
54
55import os, sys, commands, socket, urllib, getpass, cgi
56
57def do(command):
58 return commands.getstatusoutput(command)[1]
59
60# Changeset URL prefix for your repo: when the commit ID is appended
61# to this, it should point at a CGI that will display the commit
62# through gitweb or something similar. The defaults will probably
63# work if you have a typical gitweb/cgit setup.
64#
65urlprefix = do("git config --get hooks.urlprefix")
66if not urlprefix:
67 "http://%(host)s/?p=%(repo)s;a=commit;h="
68
69# The template used to generate the XML messages to CIA. You can make
70# visible changes to the IRC-bot notification lines by hacking this.
71# The default will produce a notfication line that looks like this:
72#
73# ${project}: ${author} ${repo}:${branch} * ${rev} ${files}: ${logmsg} ${url}
74#
75# By omitting $files you can collapse the files part to a single slash.
76xml = '''\
77<message>
78 <generator>
79 <name>CIA Python client for Git</name>
80 <version>%(version)s</version>
81 <url>%(generator)s</url>
82 </generator>
83 <source>
84 <project>%(project)s</project>
85 <branch>%(branch)s</branch>
86 </source>
87 <timestamp>%(ts)s</timestamp>
88 <body>
89 <commit>
90 <author>%(author)s</author>
91 <revision>%(rev)s</revision>
92 <files>
93 %(files)s
94 </files>
95 <log>%(logmsg)s</log>
96 <url>%(url)s</url>
97 </commit>
98 </body>
99</message>
100'''
101
102#
103# No user-serviceable parts below this line:
104#
105
106# Where to ship e-mail notifications.
107toaddr = "cia@cia.navi.cx"
108
109# Identify the generator script.
110# Should only change when the script itself gets a new home and maintainer.
111generator = "http://dev.entidi.com/p/git-hacks/source/tree/master/ciabot.py"
112version = "3.4-ntd"
113
114try:
115 from anyjson import serialize, deserialize
116except ImportError:
117 #there is no anyjson/cjson on alioth yet.
118 from json import write as serialize, read as deserialize
119
120
121def report(refname, merged, xmlrpc):
122 "Generate a commit notification to be reported to CIA"
123
124 url = urlprefix + merged
125 branch = do("git config --get hooks.cia-branch")
126 if not branch:
127 branch = os.path.basename(refname)
128
129 # Compute a description for the revision
130 if revformat == 'raw':
131 rev = merged
132 elif revformat == 'short':
133 rev = ''
134 else: # rev == 'describe'
135 rev = do("git describe %s 2>/dev/null" % merged)
136 if not rev:
137 rev = merged[:12]
138
139 # Extract the meta-information for the commit
140 files=do("git diff-tree -r --name-only '"+ merged +"' | sed -e '1d' -e 's-.*-<file>&</file>-'")
141 metainfo = do("git log -1 '--pretty=format:%an <%ae>%n%at%n%s' " + merged)
142 (author, ts, logmsg) = metainfo.split("\n")
143 author = cgi.escape(author)
144
145 # This ignores the timezone. Not clear what to do with it...
146 ts = ts.strip().split()[0]
147
148 if xmlrpc:
149 out = xml
150 else:
151 out = '''\
152Message-ID: <%(merged)s.%(author)s@%(project)s>
153From: %(fromaddr)s
154To: %(toaddr)s
155Content-type: text/xml
156Subject: DeliverXML
157
158''' + xml
159
160 context = locals()
161 context.update(globals())
162 return out % context
163
164if __name__ == "__main__":
165 import getopt
166
167 # Get all config variables
168 revformat = do("git config --get hooks.revformat")
169 project = do("git config --get hooks.cia-project")
170 repo = do("git config --get hooks.repo")
171 xmlrpc = do("git config --get hooks.cia-xmlrpc")
172 xmlrpc = xmlrpc and xmlrpc != "false"
173
174 host = do("git config --get hooks.hostname")
175 if not host:
176 host = socket.getfqdn()
177 fromaddr = do("git config --get hooks.fromaddr")
178 if not fromaddr:
179 fromaddr = "%s@%s" %(getpass.getuser(), host)
180
181 try:
182 (options, arguments) = getopt.getopt(sys.argv[1:], "np:V")
183 except getopt.GetoptError, msg:
184 print "ciabot.py: " + str(msg)
185 raise SystemExit, 1
186
187 notify = True
188 for (switch, val) in options:
189 if switch == '-p':
190 project = val
191 elif switch == '-n':
192 notify = False
193 elif switch == '-V':
194 print "ciabot.py: version", version
195 sys.exit(0)
196
197 # Cough and die if user has not specified a project
198 if not project:
199 sys.stderr.write("ciabot.py: no project specified, bailing out.\n")
200 sys.exit(1)
201
202 if not repo:
203 repo = project.lower()
204
205 urlprefix = urlprefix % globals()
206
207 # The script wants a reference to head followed by the list of
208 # commit ID to report about.
209 if len(arguments) == 0:
210 refname = do("git symbolic-ref HEAD 2>/dev/null")
211 merges = [do("git rev-parse HEAD")]
212 else:
213 refname = arguments[0]
214 merges = arguments[1:]
215
216 if notify:
217 if xmlrpc:
218 import xmlrpclib
219 server = xmlrpclib.Server('http://cia.navi.cx/RPC2');
220 server = xmlrpclib.ServerProxy('http://cia.vc');
221 else:
222 import smtplib
223 server = smtplib.SMTP('localhost')
224
225 for merged in merges:
226 message = report(refname, merged, xmlrpc)
227 if not notify:
228 print message
229 elif xmlrpc:
230 server.hub.deliver(message)
231 else:
232 server.sendmail(fromaddr, [toaddr], message)
233
234 if notify:
235 if not xmlrpc:
236 server.quit()
237
238#End

Archive Download this file

Branches