Branch for diff refactoring
[dbsrgits/SQL-Translator.git] / CVSROOT / syncmail
1 #!/usr/bin/python
2 #  -*- Python -*-
3
4 """Complicated notification for CVS checkins.
5
6 This script is used to provide email notifications of changes to the CVS
7 repository.  These email changes will include context diffs of the changes.
8 Really big diffs will be trimmed.
9
10 This script is run from a CVS loginfo file (see $CVSROOT/CVSROOT/loginfo).  To
11 set this up, create a loginfo entry that looks something like this:
12
13     mymodule /path/to/this/script %%s some-email-addr@your.domain
14
15 In this example, whenever a checkin that matches `mymodule' is made, this
16 script is invoked, which will generate the diff containing email, and send it
17 to some-email-addr@your.domain.
18
19     Note: This module used to also do repository synchronizations via
20     rsync-over-ssh, but since the repository has been moved to SourceForge,
21     this is no longer necessary.  The syncing functionality has been ripped
22     out in the 3.0, which simplifies it considerably.  Access the 2.x versions
23     to refer to this functionality.  Because of this, the script is misnamed.
24
25 It no longer makes sense to run this script from the command line.  Doing so
26 will only print out this usage information.
27
28
29 Usage:
30
31     %(PROGRAM)s [options] <%%S> email-addr [email-addr ...]
32
33 Where options is:
34
35     --cvsroot=<path>
36         Use <path> as the environment variable CVSROOT.  Otherwise this
37         variable must exist in the environment.
38
39     --help
40     -h
41         Print this text.
42
43     <%%S>
44         CVS %%s loginfo expansion.  When invoked by CVS, this will be a single
45         string containing the directory the checkin is being made in, relative
46         to $CVSROOT, followed by the list of files that are changing.  If the
47         %%s in the loginfo file is %%{sVv}, context diffs for each of the
48         modified files are included in any email messages that are generated.
49
50     email-addrs
51         At least one email address.
52
53 """
54
55 import os
56 import sys
57 import string
58 import time
59 import getopt
60
61 # Notification command
62 MAILCMD = '/bin/mail -s "CVS: %(SUBJECT)s" %(PEOPLE)s 2>&1 > /dev/null'
63
64 # Diff trimming stuff
65 DIFF_HEAD_LINES = 20
66 DIFF_TAIL_LINES = 20
67 DIFF_TRUNCATE_IF_LARGER = 1000
68
69 PROGRAM = sys.argv[0]
70
71
72 \f
73 def usage(code, msg=''):
74     print __doc__ % globals()
75     if msg:
76         print msg
77     sys.exit(code)
78
79
80 \f
81 def calculate_diff(filespec):
82     try:
83         file, oldrev, newrev = string.split(filespec, ',')
84     except ValueError:
85         # No diff to report
86         return '***** Bogus filespec: %s' % filespec
87     if oldrev == 'NONE':
88         try:
89             if os.path.exists(file):
90                 fp = open(file)
91             else:
92                 update_cmd = 'cvs -fn update -r %s -p %s' % (newrev, file)
93                 fp = os.popen(update_cmd)
94             lines = fp.readlines()
95             fp.close()
96             lines.insert(0, '--- NEW FILE ---\n')
97         except IOError, e:
98             lines = ['***** Error reading new file: ',
99                      str(e), '\n***** file: ', file, ' cwd: ', os.getcwd()]
100     elif newrev == 'NONE':
101         lines = ['--- %s DELETED ---\n' % file]
102     else:
103         # This /has/ to happen in the background, otherwise we'll run into CVS
104         # lock contention.  What a crock.
105         diffcmd = '/usr/bin/cvs -f diff -kk -C 2 -w -r %s -r %s %s' % (
106             oldrev, newrev, file)
107         fp = os.popen(diffcmd)
108         lines = fp.readlines()
109         sts = fp.close()
110         # ignore the error code, it always seems to be 1 :(
111 ##        if sts:
112 ##            return 'Error code %d occurred during diff\n' % (sts >> 8)
113     if len(lines) > DIFF_TRUNCATE_IF_LARGER:
114         removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES
115         del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES]
116         lines.insert(DIFF_HEAD_LINES,
117                      '[...%d lines suppressed...]\n' % removedlines)
118     return string.join(lines, '')
119
120
121 \f
122 def blast_mail(mailcmd, filestodiff):
123     # cannot wait for child process or that will cause parent to retain cvs
124     # lock for too long.  Urg!
125     if not os.fork():
126         # in the child
127         # give up the lock you cvs thang!
128         time.sleep(2)
129         fp = os.popen(mailcmd, 'w')
130         fp.write(sys.stdin.read())
131         fp.write('\n')
132         # append the diffs if available
133         for file in filestodiff:
134             fp.write(calculate_diff(file))
135             fp.write('\n')
136         fp.close()
137         # doesn't matter what code we return, it isn't waited on
138         os._exit(0)
139
140
141 \f
142 # scan args for options
143 def main():
144     try:
145         opts, args = getopt.getopt(sys.argv[1:], 'h', ['cvsroot=', 'help'])
146     except getopt.error, msg:
147         usage(1, msg)
148
149     # parse the options
150     for opt, arg in opts:
151         if opt in ('-h', '--help'):
152             usage(0)
153         elif opt == '--cvsroot':
154             os.environ['CVSROOT'] = arg
155
156     # What follows is the specification containing the files that were
157     # modified.  The argument actually must be split, with the first component
158     # containing the directory the checkin is being made in, relative to
159     # $CVSROOT, followed by the list of files that are changing.
160     if not args:
161         usage(1, 'No CVS module specified')
162     SUBJECT = args[0]
163     specs = string.split(args[0])
164     del args[0]
165
166     # The remaining args should be the email addresses
167     if not args:
168         usage(1, 'No recipients specified')
169
170     # Now do the mail command
171     PEOPLE = string.join(args)
172     mailcmd = MAILCMD % vars()
173
174     print 'Mailing %s...' % PEOPLE
175     if specs == ['-', 'Imported', 'sources']:
176         return
177     if specs[-3:] == ['-', 'New', 'directory']:
178         del specs[-3:]
179     print 'Generating notification message...'
180     blast_mail(mailcmd, specs[1:])
181     print 'Generating notification message... done.'
182
183
184 \f
185 if __name__ == '__main__':
186     main()
187     sys.exit(0)