210 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			210 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 
								 | 
							
								#! /usr/bin/python
							 | 
						|||
| 
								 | 
							
								#  -*- Python -*-
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								"""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@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@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 is:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    --cvsroot=<path>
							 | 
						|||
| 
								 | 
							
								    	Use <path> as the environment variable CVSROOT.  Otherwise this
							 | 
						|||
| 
								 | 
							
								    	variable must exist in the environment.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    --help
							 | 
						|||
| 
								 | 
							
								    -h
							 | 
						|||
| 
								 | 
							
								        Print this text.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    --context=#
							 | 
						|||
| 
								 | 
							
								    -C #
							 | 
						|||
| 
								 | 
							
								        Include # lines of context around lines that differ (default: 2).
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    -c
							 | 
						|||
| 
								 | 
							
								        Produce a context diff (default).
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    -u
							 | 
						|||
| 
								 | 
							
								        Produce a unified diff (smaller, but harder to read).
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    <%%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 string
							 | 
						|||
| 
								 | 
							
								import time
							 | 
						|||
| 
								 | 
							
								import getopt
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# Notification command
							 | 
						|||
| 
								 | 
							
								MAILCMD = '/bin/mail -s "CVS: %(SUBJECT)s" %(PEOPLE)s 2>&1 > /dev/null'
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# Diff trimming stuff
							 | 
						|||
| 
								 | 
							
								DIFF_HEAD_LINES = 20
							 | 
						|||
| 
								 | 
							
								DIFF_TAIL_LINES = 20
							 | 
						|||
| 
								 | 
							
								DIFF_TRUNCATE_IF_LARGER = 1000
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								PROGRAM = sys.argv[0]
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								def usage(code, msg=''):
							 | 
						|||
| 
								 | 
							
								    print __doc__ % globals()
							 | 
						|||
| 
								 | 
							
								    if msg:
							 | 
						|||
| 
								 | 
							
								        print msg
							 | 
						|||
| 
								 | 
							
								    sys.exit(code)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								def calculate_diff(filespec, contextlines):
							 | 
						|||
| 
								 | 
							
								    try:
							 | 
						|||
| 
								 | 
							
								        file, oldrev, newrev = string.split(filespec, ',')
							 | 
						|||
| 
								 | 
							
								    except ValueError:
							 | 
						|||
| 
								 | 
							
								        # No diff to report
							 | 
						|||
| 
								 | 
							
								        return '***** Bogus filespec: %s' % filespec
							 | 
						|||
| 
								 | 
							
								    if oldrev == 'NONE':
							 | 
						|||
| 
								 | 
							
								        try:
							 | 
						|||
| 
								 | 
							
								            if os.path.exists(file):
							 | 
						|||
| 
								 | 
							
								                fp = open(file)
							 | 
						|||
| 
								 | 
							
								            else:
							 | 
						|||
| 
								 | 
							
								                update_cmd = 'cvs -fn update -r %s -p %s' % (newrev, file)
							 | 
						|||
| 
								 | 
							
								                fp = os.popen(update_cmd)
							 | 
						|||
| 
								 | 
							
								            lines = fp.readlines()
							 | 
						|||
| 
								 | 
							
								            fp.close()
							 | 
						|||
| 
								 | 
							
								            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 -r %s -r %s %s' % (
							 | 
						|||
| 
								 | 
							
								            difftype, oldrev, newrev, file)
							 | 
						|||
| 
								 | 
							
								        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(mailcmd, filestodiff, contextlines):
							 | 
						|||
| 
								 | 
							
								    # 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)
							 | 
						|||
| 
								 | 
							
								        fp = os.popen(mailcmd, 'w')
							 | 
						|||
| 
								 | 
							
								        fp.write(sys.stdin.read())
							 | 
						|||
| 
								 | 
							
								        fp.write('\n')
							 | 
						|||
| 
								 | 
							
								        # append the diffs if available
							 | 
						|||
| 
								 | 
							
								        for file in filestodiff:
							 | 
						|||
| 
								 | 
							
								            fp.write(calculate_diff(file, contextlines))
							 | 
						|||
| 
								 | 
							
								            fp.write('\n')
							 | 
						|||
| 
								 | 
							
								        fp.close()
							 | 
						|||
| 
								 | 
							
								        # doesn't matter what code we return, it isn't waited on
							 | 
						|||
| 
								 | 
							
								        os._exit(0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# scan args for options
							 | 
						|||
| 
								 | 
							
								def main():
							 | 
						|||
| 
								 | 
							
								    contextlines = 2
							 | 
						|||
| 
								 | 
							
								    try:
							 | 
						|||
| 
								 | 
							
								        opts, args = getopt.getopt(sys.argv[1:], 'hC:cu',
							 | 
						|||
| 
								 | 
							
								                                   ['context=', 'cvsroot=', 'help'])
							 | 
						|||
| 
								 | 
							
								    except getopt.error, msg:
							 | 
						|||
| 
								 | 
							
								        usage(1, msg)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # parse the options
							 | 
						|||
| 
								 | 
							
								    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
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # 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]
							 | 
						|||
| 
								 | 
							
								    specs = string.split(args[0])
							 | 
						|||
| 
								 | 
							
								    del args[0]
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # The remaining args should be the email addresses
							 | 
						|||
| 
								 | 
							
								    if not args:
							 | 
						|||
| 
								 | 
							
								        usage(1, 'No recipients specified')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # Now do the mail command
							 | 
						|||
| 
								 | 
							
								    PEOPLE = string.join(args)
							 | 
						|||
| 
								 | 
							
								    mailcmd = MAILCMD % vars()
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    print 'Mailing %s...' % PEOPLE
							 | 
						|||
| 
								 | 
							
								    if specs == ['-', 'Imported', 'sources']:
							 | 
						|||
| 
								 | 
							
								        return
							 | 
						|||
| 
								 | 
							
								    if specs[-3:] == ['-', 'New', 'directory']:
							 | 
						|||
| 
								 | 
							
								        del specs[-3:]
							 | 
						|||
| 
								 | 
							
								    print 'Generating notification message...'
							 | 
						|||
| 
								 | 
							
								    blast_mail(mailcmd, specs[1:], contextlines)
							 | 
						|||
| 
								 | 
							
								    print 'Generating notification message... done.'
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								if __name__ == '__main__':
							 | 
						|||
| 
								 | 
							
								    main()
							 | 
						|||
| 
								 | 
							
								    sys.exit(0)
							 |