5 # This script was derrived from syncmail,
6 # Copyright (c) 2002-2006 Barry Warsaw, Fred Drake, and contributors
8 # http://cvs-syncmail.cvs.sourceforge.net
10 # The script was re-written with the sole pupose of providing updates to
11 # Buildbot master by Andy Howell
13 # Options handling done right by djmitche
19 Construct message and send to stdout for testing
21 The rest of the command line arguments are:
24 CVS %%{sVv} loginfo expansion. When invoked by CVS, this will be a single
25 string containing the files that are changing.
28 __version__
= '$Revision: 1.3 $'
40 from cStringIO
import StringIO
41 from email
.Utils
import formataddr
46 # pwd is not available on Windows..
54 """I stand in for smtplib for testing purposes.
57 """I stand in for smtplib.SMTP connection for testing purposes.
58 I copy the message to stdout.
62 def connect(self
, mailhost
, mailport
):
64 def sendmail(self
, address
, email
, msg
):
65 sys
.stdout
.write( msg
)
68 rfc822_specials_re
= re
.compile(r
'[\(\)\<\>\@\,\;\:\\\"\.\[\]]')
71 if name
and rfc822_specials_re
.search(name
):
72 return '"%s"' % name
.replace('"', '\\"')
76 def send_mail(options
):
77 # Create the smtp connection to the localhost
78 conn
= options
.smtplib
.SMTP()
79 conn
.connect(options
.mailhost
, options
.mailport
)
81 pwinfo
= pwd
.getpwuid(os
.getuid())
88 domain
= options
.fromhost
90 # getfqdn is not good for use in unit tests
92 domain
= 'testing.com'
94 domain
= socket
.getfqdn()
95 address
= '%s@%s' % (user
, domain
)
97 datestamp
= time
.strftime('%a, %d %b %Y %H:%M:%S +0000',
98 time
.gmtime(time
.time()))
99 fileList
= ' '.join(map(str, options
.files
))
101 vars = {'author' : formataddr((name
, address
)),
102 'email' : options
.email
,
103 'subject' : 'cvs update for project %s' % options
.project
,
104 'version' : __version__
,
109 To: %(email)s''' % vars
111 print >> s
, 'Reply-To: %s' % options
.replyto
115 X-Mailer: Python buildbot-cvs-mail %(version)s
117 print >> s
, 'Cvsmode: %s' % options
.cvsmode
118 print >> s
, 'Category: %s' % options
.category
119 print >> s
, 'CVSROOT: %s' % options
.cvsroot
120 print >> s
, 'Files: %s' % fileList
122 print >> s
, 'Path: %s' % options
.path
123 print >> s
, 'Project: %s' % options
.project
124 s
.write(sys
.stdin
.read())
126 resp
= conn
.sendmail(address
, options
.email
, s
.getvalue())
129 def fork_and_send_mail(options
):
130 # cannot wait for child process or that will cause parent to retain cvs
131 # lock for too long. Urg!
134 # give up the lock you cvs thang!
140 This script is used to provide email notifications of changes to the CVS
141 repository to a buildbot master. It is invoked via a CVS loginfo file (see
142 $CVSROOT/CVSROOT/loginfo). See the Buildbot manual for more information.
144 usage
="%prog [options] %{sVv}"
145 parser
= optparse
.OptionParser(description
=description
,
147 add_help_option
=True,
150 parser
.add_option("-C", "--category", dest
='category', metavar
="CAT",
151 help=textwrap
.dedent("""\
152 Category for change. This becomes the Change.category attribute, which
153 can be used within the buildmaster to filter changes.
155 parser
.add_option("-c", "--cvsroot", dest
='cvsroot', metavar
="PATH",
156 help=textwrap
.dedent("""\
157 CVSROOT for use by buildbot slaves to checkout code.
158 This becomes the Change.repository attribute.
159 Exmaple: :ext:myhost:/cvsroot
161 parser
.add_option("-e", "--email", dest
='email', metavar
="EMAIL",
162 help=textwrap
.dedent("""\
163 Email address of the buildbot.
165 parser
.add_option("-f", "--fromhost", dest
='fromhost', metavar
="HOST",
166 help=textwrap
.dedent("""\
167 The hostname that email messages appear to be coming from. The From:
168 header of the outgoing message will look like user@hostname. By
169 default, hostname is the machine's fully qualified domain name.
171 parser
.add_option("-m", "--mailhost", dest
='mailhost', metavar
="HOST",
173 help=textwrap
.dedent("""\
174 The hostname of an available SMTP server. The default is
177 parser
.add_option("--mailport", dest
='mailport', metavar
="PORT",
178 default
=25, type="int",
179 help=textwrap
.dedent("""\
180 The port number of SMTP server. The default is '25'.
182 parser
.add_option("-q", "--quiet", dest
='verbose', action
="store_false",
184 help=textwrap
.dedent("""\
185 Don't print as much status to stdout.
187 parser
.add_option("-p", "--path", dest
='path', metavar
="PATH",
188 help=textwrap
.dedent("""\
189 The path for the files in this update. This comes from the %p parameter
190 in loginfo for CVS version 1.12.x. Do not use this for CVS version 1.11.x
192 parser
.add_option("-P", "--project", dest
='project', metavar
="PROJ",
193 help=textwrap
.dedent("""\
194 The project for the source. Often set to the CVS module being modified. This becomes
195 the Change.project attribute.
197 parser
.add_option("-R", "--reply-to", dest
='replyto', metavar
="ADDR",
198 help=textwrap
.dedent("""\
199 Add a "Reply-To: ADDR" header to the email message.
201 parser
.add_option("-t", "--testing", action
="store_true", dest
="amTesting", default
=False)
202 parser
.set_defaults(smtplib
=smtplib
)
205 options
, args
= parser
.parse_args()
207 # rest of command line are the files.
209 if options
.path
is None:
210 options
.cvsmode
= '1.11'
212 options
.cvsmode
= '1.12'
214 if options
.cvsroot
is None:
215 parser
.error('--cvsroot is required')
216 if options
.email
is None:
217 parser
.error('--email is required')
219 # set up for unit tests
220 if options
.amTesting
:
222 options
.smtplib
= SmtplibMock
226 # scan args for options
228 options
= get_options()
231 print 'Mailing %s...' % options
.email
232 print 'Generating notification message...'
233 if options
.amTesting
:
236 fork_and_send_mail(options
)
239 print 'Generating notification message... done.'
242 if __name__
== '__main__':