[evolvis-commits] r11505: adding syncmail to cvssyncmail plugin↵
mirabilos at evolvis.org
mirabilos at evolvis.org
Thu Feb 24 18:17:25 CET 2011
Author: mirabilos
Date: 2011-02-24 18:17:24 +0100 (Thu, 24 Feb 2011)
New Revision: 11505
Added:
trunk/gforge_base/evolvisforge-5.1/gforge-plugin-cvssyncmail/bin/
trunk/gforge_base/evolvisforge-5.1/gforge-plugin-cvssyncmail/bin/syncmail
Log:
adding syncmail to cvssyncmail plugin
Added: trunk/gforge_base/evolvisforge-5.1/gforge-plugin-cvssyncmail/bin/syncmail
===================================================================
--- trunk/gforge_base/evolvisforge-5.1/gforge-plugin-cvssyncmail/bin/syncmail (rev 0)
+++ trunk/gforge_base/evolvisforge-5.1/gforge-plugin-cvssyncmail/bin/syncmail 2011-02-24 17:17:24 UTC (rev 11505)
@@ -0,0 +1,328 @@
+#! /usr/bin/python
+
+# NOTE: Until SourceForge installs a modern version of Python on the cvs
+# servers, this script MUST be compatible with Python 1.5.2.
+
+"""Complicated notification for CVS checkins.
+
+This script is used to provide email notifications of changes to the CVS
+repository. These email changes will include context diffs of the changes.
+Really big diffs will be trimmed.
+
+This script is run from a CVS loginfo file (see $CVSROOT/CVSROOT/loginfo). To
+set this up, create a loginfo entry that looks something like this:
+
+ mymodule /path/to/this/script %%s some-email-addr at your.domain
+
+In this example, whenever a checkin that matches `mymodule' is made, this
+script is invoked, which will generate the diff containing email, and send it
+to some-email-addr at your.domain.
+
+ Note: This module used to also do repository synchronizations via
+ rsync-over-ssh, but since the repository has been moved to SourceForge,
+ this is no longer necessary. The syncing functionality has been ripped
+ out in the 3.0, which simplifies it considerably. Access the 2.x versions
+ to refer to this functionality. Because of this, the script is misnamed.
+
+It no longer makes sense to run this script from the command line. Doing so
+will only print out this usage information.
+
+Usage:
+
+ %(PROGRAM)s [options] <%%S> email-addr [email-addr ...]
+
+Where options are:
+
+ --cvsroot=<path>
+ Use <path> as the environment variable CVSROOT. Otherwise this
+ variable must exist in the environment.
+
+ --context=#
+ -C #
+ Include # lines of context around lines that differ (default: 2).
+
+ -c
+ Produce a context diff (default).
+
+ -u
+ Produce a unified diff (smaller).
+
+ --quiet / -q
+ Don't print as much status to stdout.
+
+ --fromhost=hostname
+ -f hostname
+ The hostname that email messages appear to be coming from. The From:
+ header will of the outgoing message will look like user at hostname. By
+ default, hostname is the machine's fully qualified domain name.
+
+ --help / -h
+ Print this text.
+
+The rest of the command line arguments are:
+
+ <%%S>
+ CVS %%s loginfo expansion. When invoked by CVS, this will be a single
+ string containing the directory the checkin is being made in, relative
+ to $CVSROOT, followed by the list of files that are changing. If the
+ %%s in the loginfo file is %%{sVv}, context diffs for each of the
+ modified files are included in any email messages that are generated.
+
+ email-addrs
+ At least one email address.
+"""
+import os
+import sys
+import re
+import time
+import string
+import getopt
+import smtplib
+import pwd
+import socket
+
+try:
+ from socket import getfqdn
+except ImportError:
+ def getfqdn():
+ # Python 1.5.2 :(
+ hostname = socket.gethostname()
+ byaddr = socket.gethostbyaddr(socket.gethostbyname(hostname))
+ aliases = byaddr[1]
+ aliases.insert(0, byaddr[0])
+ aliases.insert(0, hostname)
+ for fqdn in aliases:
+ if '.' in fqdn:
+ break
+ else:
+ fqdn = 'localhost.localdomain'
+ return fqdn
+
+
+from cStringIO import StringIO
+
+# Which SMTP server to do we connect to? Empty string means localhost.
+MAILHOST = 'localhost'
+MAILPORT = 25
+
+# Diff trimming stuff
+DIFF_HEAD_LINES = 20
+DIFF_TAIL_LINES = 20
+DIFF_TRUNCATE_IF_LARGER = 1000
+
+EMPTYSTRING = ''
+SPACE = ' '
+DOT = '.'
+COMMASPACE = ', '
+
+PROGRAM = sys.argv[0]
+
+BINARY_EXPLANATION_LINES = [
+ "(This appears to be a binary file; contents omitted.)\n"
+ ]
+
+REVCRE = re.compile("^(NONE|[0-9.]+)$")
+NOVERSION = "Couldn't generate diff; no version number found in filespec: %s"
+BACKSLASH = "Couldn't generate diff: backslash in filespec's filename: %s"
+
+
+
+def usage(code, msg=''):
+ print __doc__ % globals()
+ if msg:
+ print msg
+ sys.exit(code)
+
+
+
+def calculate_diff(filespec, contextlines):
+ file, oldrev, newrev = string.split(filespec, ',')
+ # Make sure we can find a CVS version number
+ if not REVCRE.match(oldrev):
+ return NOVERSION % filespec
+ if not REVCRE.match(newrev):
+ return NOVERSION % filespec
+
+ if string.find(file, '\\') <> -1:
+ # I'm sorry, a file name that contains a backslash is just too much.
+ # XXX if someone wants to figure out how to escape the backslashes in
+ # a safe way to allow filenames containing backslashes, this is the
+ # place to do it. --Zooko 2002-03-17
+ return BACKSLASH % filespec
+
+ if string.find(file, "'") <> -1:
+ # Those crazy users put single-quotes in their file names! Now we
+ # have to escape everything that is meaningful inside double-quotes.
+ filestr = string.replace(file, '`', '\`')
+ filestr = string.replace(filestr, '"', '\"')
+ filestr = string.replace(filestr, '$', '\$')
+ # and quote it with double-quotes.
+ filestr = '"' + filestr + '"'
+ else:
+ # quote it with single-quotes.
+ filestr = "'" + file + "'"
+ if oldrev == 'NONE':
+ try:
+ if os.path.exists(file):
+ fp = open(file)
+ else:
+ update_cmd = "cvs -fn update -r %s -p %s" % (newrev, filestr)
+ fp = os.popen(update_cmd)
+ lines = fp.readlines()
+ fp.close()
+ # Is this a binary file? Let's look at the first few
+ # lines to figure it out:
+ for line in lines[:5]:
+ for c in string.rstrip(line):
+ if c in string.whitespace:
+ continue
+ if c < ' ' or c > chr(127):
+ lines = BINARY_EXPLANATION_LINES[:]
+ break
+ lines.insert(0, '--- NEW FILE: %s ---\n' % file)
+ except IOError, e:
+ lines = ['***** Error reading new file: ',
+ str(e), '\n***** file: ', file, ' cwd: ', os.getcwd()]
+ elif newrev == 'NONE':
+ lines = ['--- %s DELETED ---\n' % file]
+ else:
+ # This /has/ to happen in the background, otherwise we'll run into CVS
+ # lock contention. What a crock.
+ if contextlines > 0:
+ difftype = "-C " + str(contextlines)
+ else:
+ difftype = "-u"
+ diffcmd = "/usr/bin/cvs -f diff -kk %s --minimal -r %s -r %s %s" \
+ % (difftype, oldrev, newrev, filestr)
+ fp = os.popen(diffcmd)
+ lines = fp.readlines()
+ sts = fp.close()
+ # ignore the error code, it always seems to be 1 :(
+## if sts:
+## return 'Error code %d occurred during diff\n' % (sts >> 8)
+ if len(lines) > DIFF_TRUNCATE_IF_LARGER:
+ removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES
+ del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES]
+ lines.insert(DIFF_HEAD_LINES,
+ '[...%d lines suppressed...]\n' % removedlines)
+ return string.join(lines, '')
+
+
+
+def blast_mail(subject, people, filestodiff, contextlines, fromhost):
+ # cannot wait for child process or that will cause parent to retain cvs
+ # lock for too long. Urg!
+ if not os.fork():
+ # in the child
+ # give up the lock you cvs thang!
+ time.sleep(2)
+ # Create the smtp connection to the localhost
+ conn = smtplib.SMTP()
+ conn.connect(MAILHOST, MAILPORT)
+ user = pwd.getpwuid(os.getuid())[0]
+ domain = fromhost or getfqdn()
+ author = '%s@%s' % (user, domain)
+ s = StringIO()
+ sys.stdout = s
+ try:
+ print '''\
+From: %(author)s
+To: %(people)s
+Subject: %(subject)s
+''' % {'author' : author,
+ 'people' : string.join(people, COMMASPACE),
+ 'subject': subject,
+ }
+ s.write(sys.stdin.read())
+ # append the diffs if available
+ print
+ for file in filestodiff:
+ print calculate_diff(file, contextlines)
+ finally:
+ sys.stdout = sys.__stdout__
+ resp = conn.sendmail(author, people, s.getvalue())
+ conn.close()
+ os._exit(0)
+
+
+
+# scan args for options
+def main():
+ try:
+ opts, args = getopt.getopt(
+ sys.argv[1:], 'hC:cuqf:',
+ ['fromhost=', 'context=', 'cvsroot=', 'help', 'quiet'])
+ except getopt.error, msg:
+ usage(1, msg)
+
+ # parse the options
+ contextlines = 2
+ verbose = 1
+ fromhost = None
+ for opt, arg in opts:
+ if opt in ('-h', '--help'):
+ usage(0)
+ elif opt == '--cvsroot':
+ os.environ['CVSROOT'] = arg
+ elif opt in ('-C', '--context'):
+ contextlines = int(arg)
+ elif opt == '-c':
+ if contextlines <= 0:
+ contextlines = 2
+ elif opt == '-u':
+ contextlines = 0
+ elif opt in ('-q', '--quiet'):
+ verbose = 0
+ elif opt in ('-f', '--fromhost'):
+ fromhost = arg
+
+ # What follows is the specification containing the files that were
+ # modified. The argument actually must be split, with the first component
+ # containing the directory the checkin is being made in, relative to
+ # $CVSROOT, followed by the list of files that are changing.
+ if not args:
+ usage(1, 'No CVS module specified')
+ subject = args[0]
+ fileargs = args[1:-1]
+ specs = []
+ specs.append(subject)
+ while len(fileargs) > 0:
+ specs.append(string.join(fileargs[:3], ','))
+ fileargs = fileargs[3:]
+
+ if not args[-1]:
+ usage(1, 'No recipients specified')
+ people = []
+ people.append(args[-1])
+
+ # The remaining args should be the email addresses
+ # Now do the mail command
+
+ if verbose:
+ print 'Mailing %s...' % string.join(people, COMMASPACE)
+
+ if specs == ['-', 'Imported', 'sources']:
+ return
+ if specs[-3:] == ['-', 'New', 'directory']:
+ del specs[-3:]
+ elif len(specs) > 2:
+ L = specs[:2]
+ for s in specs[2:]:
+ prev = L[-1]
+ if string.count(prev, ',') < 2:
+ L[-1] = "%s %s" % (prev, s)
+ else:
+ L.append(s)
+ specs = L
+
+ if verbose:
+ print 'Generating notification message...'
+ blast_mail(subject, people, specs[1:], contextlines, fromhost)
+ if verbose:
+ print 'Generating notification message... done.'
+
+
+
+if __name__ == '__main__':
+ main()
+ sys.exit(0)
Property changes on: trunk/gforge_base/evolvisforge-5.1/gforge-plugin-cvssyncmail/bin/syncmail
___________________________________________________________________
Added: svn:executable
+ *
More information about the evolvis-commits
mailing list