]> git.p6c8.net - selfforum.git/blob - CVSROOT/syncmail
syncmail added for cvs mail support
[selfforum.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 Usage:
29
30 %(PROGRAM)s [options] <%%S> email-addr [email-addr ...]
31
32 Where options is:
33
34 --cvsroot=<path>
35 Use <path> as the environment variable CVSROOT. Otherwise this
36 variable must exist in the environment.
37
38 --help
39 -h
40 Print this text.
41
42 --context=#
43 -C #
44 Include # lines of context around lines that differ (default: 2).
45
46 -c
47 Produce a context diff (default).
48
49 -u
50 Produce a unified diff (smaller, but harder to read).
51
52 <%%S>
53 CVS %%s loginfo expansion. When invoked by CVS, this will be a single
54 string containing the directory the checkin is being made in, relative
55 to $CVSROOT, followed by the list of files that are changing. If the
56 %%s in the loginfo file is %%{sVv}, context diffs for each of the
57 modified files are included in any email messages that are generated.
58
59 email-addrs
60 At least one email address.
61
62 """
63
64 import os
65 import sys
66 import string
67 import time
68 import getopt
69
70 # Notification command
71 MAILCMD = '/bin/mail -s "CVS: %(SUBJECT)s" %(PEOPLE)s 2>&1 > /dev/null'
72
73 # Diff trimming stuff
74 DIFF_HEAD_LINES = 20
75 DIFF_TAIL_LINES = 20
76 DIFF_TRUNCATE_IF_LARGER = 1000
77
78 PROGRAM = sys.argv[0]
79
80
81
82 def usage(code, msg=''):
83 print __doc__ % globals()
84 if msg:
85 print msg
86 sys.exit(code)
87
88
89
90 def calculate_diff(filespec, contextlines):
91 try:
92 file, oldrev, newrev = string.split(filespec, ',')
93 except ValueError:
94 # No diff to report
95 return '***** Bogus filespec: %s' % filespec
96 if oldrev == 'NONE':
97 try:
98 if os.path.exists(file):
99 fp = open(file)
100 else:
101 update_cmd = 'cvs -fn update -r %s -p %s' % (newrev, file)
102 fp = os.popen(update_cmd)
103 lines = fp.readlines()
104 fp.close()
105 lines.insert(0, '--- NEW FILE: %s ---\n' % file)
106 except IOError, e:
107 lines = ['***** Error reading new file: ',
108 str(e), '\n***** file: ', file, ' cwd: ', os.getcwd()]
109 elif newrev == 'NONE':
110 lines = ['--- %s DELETED ---\n' % file]
111 else:
112 # This /has/ to happen in the background, otherwise we'll run into CVS
113 # lock contention. What a crock.
114 if contextlines > 0:
115 difftype = "-C " + str(contextlines)
116 else:
117 difftype = "-u"
118 diffcmd = '/usr/bin/cvs -f diff -kk %s -r %s -r %s %s' % (
119 difftype, oldrev, newrev, file)
120 fp = os.popen(diffcmd)
121 lines = fp.readlines()
122 sts = fp.close()
123 # ignore the error code, it always seems to be 1 :(
124 ## if sts:
125 ## return 'Error code %d occurred during diff\n' % (sts >> 8)
126 if len(lines) > DIFF_TRUNCATE_IF_LARGER:
127 removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES
128 del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES]
129 lines.insert(DIFF_HEAD_LINES,
130 '[...%d lines suppressed...]\n' % removedlines)
131 return string.join(lines, '')
132
133
134
135 def blast_mail(mailcmd, filestodiff, contextlines):
136 # cannot wait for child process or that will cause parent to retain cvs
137 # lock for too long. Urg!
138 if not os.fork():
139 # in the child
140 # give up the lock you cvs thang!
141 time.sleep(2)
142 fp = os.popen(mailcmd, 'w')
143 fp.write(sys.stdin.read())
144 fp.write('\n')
145 # append the diffs if available
146 for file in filestodiff:
147 fp.write(calculate_diff(file, contextlines))
148 fp.write('\n')
149 fp.close()
150 # doesn't matter what code we return, it isn't waited on
151 os._exit(0)
152
153
154
155 # scan args for options
156 def main():
157 contextlines = 2
158 try:
159 opts, args = getopt.getopt(sys.argv[1:], 'hC:cu',
160 ['context=', 'cvsroot=', 'help'])
161 except getopt.error, msg:
162 usage(1, msg)
163
164 # parse the options
165 for opt, arg in opts:
166 if opt in ('-h', '--help'):
167 usage(0)
168 elif opt == '--cvsroot':
169 os.environ['CVSROOT'] = arg
170 elif opt in ('-C', '--context'):
171 contextlines = int(arg)
172 elif opt == '-c':
173 if contextlines <= 0:
174 contextlines = 2
175 elif opt == '-u':
176 contextlines = 0
177
178 # What follows is the specification containing the files that were
179 # modified. The argument actually must be split, with the first component
180 # containing the directory the checkin is being made in, relative to
181 # $CVSROOT, followed by the list of files that are changing.
182 if not args:
183 usage(1, 'No CVS module specified')
184 SUBJECT = args[0]
185 specs = string.split(args[0])
186 del args[0]
187
188 # The remaining args should be the email addresses
189 if not args:
190 usage(1, 'No recipients specified')
191
192 # Now do the mail command
193 PEOPLE = string.join(args)
194 mailcmd = MAILCMD % vars()
195
196 print 'Mailing %s...' % PEOPLE
197 if specs == ['-', 'Imported', 'sources']:
198 return
199 if specs[-3:] == ['-', 'New', 'directory']:
200 del specs[-3:]
201 print 'Generating notification message...'
202 blast_mail(mailcmd, specs[1:], contextlines)
203 print 'Generating notification message... done.'
204
205
206
207 if __name__ == '__main__':
208 main()
209 sys.exit(0)

patrick-canterino.de