[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