Bugzilla 2.18 and on use contrib/sendbugmail.pl, not processmail.
During 2.17, Bugzilla ditched the old 'processmail' script. With 2.18
contrib/sendbugmail.pl arrived in its place.
For notification emails to work properly, sendbugmail.pl requires as
its second parameter the Bugzilla user who made the commit. Otherwise
the user will not be recognised as the committer, and will receive
notification emails about the commit regardless of their preference
about being notified on their own commits. This parameter should be given
to processmail also, but wasn't for historical reasons.
Add new config with the local Bugzilla install directory, and provide
defaults for the notify string which should work for most setups.
Still permit notify string to be specified, and for backwards
compatibility with any extant notify strings try first interpolating
notify string with old-style single bug ID argument. Add new 2.18
support version to introduce sendbugmail.pl.
In other words, this update should be backwards-compatible with existing
installations, but offers simplified setup in most cases. And as a bonus
Bugzilla notification emails will be dispatched correctly; notifiers will
not receive an email unless configured to do so.
#!/usr/bin/env python
import os, sys, struct, stat
import difflib
import re
from optparse import OptionParser
from mercurial.bdiff import bdiff, blocks
from mercurial.mdiff import bunidiff, diffopts
VERSION="0.3"
usage = "usage: %prog [options] file1 file2"
parser = OptionParser(usage=usage)
parser.add_option("-d", "--difflib", action="store_true", default=False)
parser.add_option('-x', '--count', default=1)
parser.add_option('-c', '--context', type="int", default=3)
parser.add_option('-p', '--show-c-function', action="store_true", default=False)
parser.add_option('-w', '--ignore-all-space', action="store_true",
default=False)
(options, args) = parser.parse_args()
if not args:
parser.print_help()
sys.exit(1)
# simple utility function to put all the
# files from a directory tree into a dict
def buildlist(names, top):
tlen = len(top)
for root, dirs, files in os.walk(top):
l = root[tlen + 1:]
for x in files:
p = os.path.join(root, x)
st = os.lstat(p)
if stat.S_ISREG(st.st_mode):
names[os.path.join(l, x)] = (st.st_dev, st.st_ino)
def diff_files(file1, file2):
if file1 == None:
b = file(file2).read().splitlines(1)
l1 = "--- %s\n" % (file2)
l2 = "+++ %s\n" % (file2)
l3 = "@@ -0,0 +1,%d @@\n" % len(b)
l = [l1, l2, l3] + ["+" + e for e in b]
elif file2 == None:
a = file(file1).read().splitlines(1)
l1 = "--- %s\n" % (file1)
l2 = "+++ %s\n" % (file1)
l3 = "@@ -1,%d +0,0 @@\n" % len(a)
l = [l1, l2, l3] + ["-" + e for e in a]
else:
t1 = file(file1).read()
t2 = file(file2).read()
l1 = t1.splitlines(1)
l2 = t2.splitlines(1)
if options.difflib:
l = difflib.unified_diff(l1, l2, file1, file2)
else:
l = bunidiff(t1, t2, l1, l2, file1, file2,
diffopts(context=options.context,
showfunc=options.show_c_function,
ignorews=options.ignore_all_space))
for x in l:
if x[-1] != '\n':
x += "\n\ No newline at end of file\n"
print x,
file1 = args[0]
file2 = args[1]
if os.path.isfile(file1) and os.path.isfile(file2):
diff_files(file1, file2)
elif os.path.isdir(file1):
if not os.path.isdir(file2):
sys.stderr.write("file types don't match\n")
sys.exit(1)
d1 = {}
d2 = {}
buildlist(d1, file1)
buildlist(d2, file2)
keys = d1.keys()
keys.sort()
for x in keys:
if x not in d2:
f2 = None
else:
f2 = os.path.join(file2, x)
st1 = d1[x]
st2 = d2[x]
del d2[x]
if st1[0] == st2[0] and st1[1] == st2[1]:
sys.stderr.write("%s is a hard link\n" % x)
continue
x = os.path.join(file1, x)
diff_files(x, f2)
keys = d2.keys()
keys.sort()
for x in keys:
f1 = None
x = os.path.join(file2, x)
diff_files(f1, x)