This repository has been archived by the owner on Jun 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
/
try_mailer.py
executable file
·234 lines (192 loc) · 7.37 KB
/
try_mailer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/python
"""%prog [options] host builder_path build_number
Uploads logs to the given host, and then sends an email to the build's owner
"""
import subprocess
import sys
import os
import re
import cPickle
from email.message import Message
from email.utils import formatdate
from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION, RETRY
def getBuild(builder_path, build_number):
build_path = os.path.join(builder_path, build_number)
if not os.path.exists(build_path):
raise ValueError("Couldn't find %s" % build_path)
class FakeBuilder:
basedir = builder_path
name = os.path.basename(builder_path)
build = cPickle.load(open(build_path))
build.builder = FakeBuilder()
return build
def uploadLog(args):
"""Uploads the build log, and returns the URL to it"""
my_dir = os.path.abspath(os.path.dirname(__file__))
cmd = [sys.executable, "%s/log_uploader.py" % my_dir] + args
devnull = open(os.devnull)
print "Running", cmd
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=devnull,)
retcode = proc.wait()
output = proc.stdout.read().strip()
print output
# Look for URLs
url = re.search("http://\S+", output)
if url:
return url.group(), retcode
return None, retcode
def makeTryMessage(build, log_url):
builder = build.builder.name
props = build.getProperties()
if 'who' in props:
who = props['who']
else:
users = build.getResponsibleUsers()
if users:
who = users[0]
else:
raise ValueError("I don't know who did this build")
branch = props['branch']
tree = "Try"
if 'comm' in branch:
tree = "Thunderbird-Try"
if 'got_revision' in props:
revision = props['got_revision'][:12]
elif 'revision' in props:
revision = props['revision'][:12]
else:
revision = 'unknown'
if 'test' in builder:
task = 'test'
else:
task = 'build'
result = build.getResults()
if result == SUCCESS:
subject = "%(tree)s submission %(revision)s" % locals()
result_msg = "was successfully completed"
elif result == WARNINGS:
subject = "%(tree)s submission %(revision)s - warnings" % locals()
result_msg = "completed with warnings"
elif result == EXCEPTION:
subject = "%(tree)s submission %(revision)s - errors" % locals()
result_msg = "hit a buildbot exception"
elif result == FAILURE:
subject = "%(tree)s submission %(revision)s - errors" % locals()
result_msg = "failed to complete"
elif result == RETRY:
subject = "Try submission %(revision)s - retried" % locals()
result_msg = "is being automatically retried"
else:
subject = "%(tree)s submission %(revision)s - errors" % locals()
result_msg = "had unknown problem (%s)" % result
text = """\
Your %(tree)s Server %(task)s (%(revision)s) %(result_msg)s on builder %(builder)s.\n\n""" % locals()
if 'packageUrl' in props:
url = props['packageUrl'].replace('://stage', '://ftp')
text += "It should be available for download at <a href=\"%(url)s\">%(url)s</a>\n\n" % locals()
if task == 'test':
text += "Summary of test results:\n\n"
for log in build.getLogs():
if 'summary' not in log.getName():
continue
summary = log.getText().replace('TinderboxPrint:', '')
summary = summary.replace('<br>', '')
summary = re.sub("\n\n*", "\n", summary)
text += '%s\n\n' % summary
if log_url:
log_url = log_url.replace('://stage', '://ftp')
text += "The full log for this %(task)s run is available at <a href=\"%(log_url)s\">%(log_url)s</a>.\n\n" % locals()
text += "For an overview of all results see <a href=\"https://tbpl.mozilla.org/?tree=%(tree)s&rev=%(revision)s\">TBPL</a>.\n" % locals()
text = re.sub("\n", "<br>\n", text)
headers = {"In-Reply-To": "<%(branch)s-%(revision)s>" % locals(),
"References": "<%(branch)s-%(revision)s>" % locals(),
}
return dict(
subject=subject,
body=text,
headers=headers,
author=who,
type='html',
)
def formatMessage(msgdict, from_, to):
m = Message()
m.set_payload(msgdict['body'])
m.set_type('text/%s' % msgdict.get('type', 'plain'))
m['Date'] = formatdate(localtime=True)
m['Subject'] = msgdict['subject']
m['From'] = from_
m['To'] = ", ".join(to)
for k, v in msgdict['headers'].items():
if k not in m:
m[k] = v
return m
if __name__ == '__main__':
from argparse import ArgumentParser
from smtplib import SMTP
parser = ArgumentParser()
parser.add_argument("-f", "--from", dest="from_",
help="from email address", required=True)
parser.add_argument(
"-t", "--to", dest="to", help="to email address", action='append')
parser.add_argument("--to-author", dest="to_author", help="send mail to build's owner", action="store_true")
parser.add_argument(
"--log-url", dest="log_url", help="url to uploaded log")
parser.add_argument("--relay", dest="relayhost", help="smtp host to send mail through")
parser.set_defaults(
to_author=False,
to=[],
from_=None,
log_url=None,
relayhost='mail.build.mozilla.org'
)
options, args = parser.parse_known_args()
if not options.to and not options.to_author:
parser.error("You must specify --to, or --to-author")
if options.log_url:
log_url = options.log_url
exit_code = 0
else:
log_url, exit_code = uploadLog(args)
print
tm_parser = ArgumentParser()
tm_parser.add_argument("-e", "--all-emails", dest="all_emails",
help="request all emails", action="store_true")
tm_parser.add_argument("-f", "--failure-emails", dest="failure", help="request failure emails only", action="store_true")
tm_parser.set_defaults(
all_emails=False,
failure=False,
)
builder_path, build_number = args[-2:]
build = getBuild(builder_path, build_number)
# check the commit message for syntax regarding email prefs
match = re.search("try: ", build.source.changes[-1].comments)
comment_args = ""
if match:
comment_args = build.source.changes[-1].comments.split(
"try: ")[1].split()
tm_options, args = tm_parser.parse_known_args(comment_args)
# Let's check the results to see if we need the message
result = build.getResults()
# default is silence, never create a message
# if failure, send failure emails only
# if all emails, alway make the message
msgdict = None
# Generate the message
if tm_options.all_emails:
msgdict = makeTryMessage(build, log_url)
elif tm_options.failure:
if result != SUCCESS and result != RETRY:
msgdict = makeTryMessage(build, log_url)
# Send it!
if msgdict != None:
if options.to_author:
options.to.append(msgdict['author'])
msg = formatMessage(msgdict, options.from_, options.to)
print msg
s = SMTP()
s.connect(options.relayhost)
s.sendmail(options.from_, options.to, msg.as_string())
sys.exit(exit_code)