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
·216 lines (179 loc) · 6.92 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
#!/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, sys, os, re
import cPickle
from email.message import Message
from email.utils import formatdate
from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
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")
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 = "Try submission %(revision)s" % locals()
result_msg = "was successfully completed"
elif result == WARNINGS:
subject = "Try submission %(revision)s - warnings" % locals()
result_msg = "completed with warnings"
elif result == EXCEPTION:
subject = "Try submission %(revision)s - errors" % locals()
result_msg = "hit a buildbot exception"
elif result == FAILURE:
subject = "Try submission %(revision)s - errors" % locals()
result_msg = "failed to complete"
else:
subject = "Try submission %(revision)s - errors" % locals()
result_msg = "had unknown problem (%s)" % result
text = """\
Your Try 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" % locals()
else:
text += "Please check <a href=\"https://tbpl.mozilla.org/?tree=Try&rev=%(revision)s\">Tinderbox Pushlog</a> for your logs.\n" % locals()
text = re.sub("\n", "<br>\n", text)
headers = {"In-Reply-To": "<try-%(revision)s>" % locals(),
"References": "<try-%(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.set_defaults(
to_author=False,
to=[],
from_=None,
log_url=None
)
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("-n", "--no-emails", dest="silence", help="request no emails at all", action="store_true")
tm_parser.set_defaults(
all_emails=False,
silence=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()
# if silence, never make the message
# if all emails, alway make the message
# else default is failures only
msgdict = None
# Generate the message
if tm_options.silence:
print "No email going out for this result: %s (silence=%s)" % (result, tm_options.silence)
else:
if tm_options.all_emails:
msgdict = makeTryMessage(build, log_url)
else:
if result != SUCCESS:
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()
s.sendmail(options.from_, options.to, msg.as_string())
sys.exit(exit_code)