-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
build.py
238 lines (192 loc) · 8.66 KB
/
build.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
235
236
237
from twisted.web import html
from twisted.web.util import Redirect, DeferredResource
from twisted.internet import defer, reactor
import urllib, time
from twisted.python import log
from buildbot.status.web.base import HtmlResource, \
css_classes, path_to_build, path_to_builder, path_to_slave, \
getAndCheckProperties, path_to_authfail
from buildbot.status.web.step import StepsResource
from buildbot import util, interfaces
# /builders/$builder/builds/$buildnum
class StatusResourceBuild(HtmlResource):
addSlash = True
def __init__(self, build_status):
HtmlResource.__init__(self)
self.build_status = build_status
def getTitle(self, request):
return ("Buildbot: %s Build #%d" %
(self.build_status.getBuilder().getName(),
self.build_status.getNumber()))
def content(self, req, cxt):
b = self.build_status
status = self.getStatus(req)
cxt['b'] = b
cxt['path_to_builder'] = path_to_builder(req, b.getBuilder())
if not b.isFinished():
step = b.getCurrentStep()
if not step:
cxt['current_step'] = "[waiting for Lock]"
else:
if step.isWaitingForLocks():
cxt['current_step'] = "%s [waiting for Lock]" % step.getName()
else:
cxt['current_step'] = step.getName()
when = b.getETA()
if when is not None:
cxt['when'] = util.formatInterval(when)
cxt['when_time'] = time.strftime("%H:%M:%S",
time.localtime(time.time() + when))
else:
cxt['result_css'] = css_classes[b.getResults()]
if b.getTestResults():
cxt['tests_link'] = req.childLink("tests")
ss = cxt['ss'] = b.getSourceStamp()
if ss.branch is None and ss.revision is None and ss.patch is None and not ss.changes:
cxt['most_recent_rev_build'] = True
got_revision = None
try:
got_revision = b.getProperty("got_revision")
except KeyError:
pass
if got_revision:
cxt['got_revision'] = str(got_revision)
try:
cxt['slave_url'] = path_to_slave(req, status.getSlave(b.getSlavename()))
except KeyError:
pass
cxt['steps'] = []
for s in b.getSteps():
step = {'name': s.getName() }
cxt['steps'].append(step)
if s.isFinished():
step['css_class'] = css_classes[s.getResults()[0]]
(start, end) = s.getTimes()
step['time_to_run'] = util.formatInterval(end - start)
elif s.isStarted():
if s.isWaitingForLocks():
step['css_class'] = "waiting"
step['time_to_run'] = "waiting for locks"
else:
step['css_class'] = "running"
step['time_to_run'] = "running"
else:
step['css_class'] = "not_started"
step['time_to_run'] = ""
step['link'] = req.childLink("steps/%s" % urllib.quote(s.getName()))
step['text'] = " ".join(s.getText())
step['urls'] = map(lambda x:dict(url=x[1],logname=x[0]), s.getURLs().items())
step['logs']= []
for l in s.getLogs():
logname = l.getName()
step['logs'].append({ 'link': req.childLink("steps/%s/logs/%s" %
(urllib.quote(s.getName()),
urllib.quote(logname))),
'name': logname })
ps = cxt['properties'] = []
for name, value, source in b.getProperties().asList():
value = str(value)
p = { 'name': name, 'value': value, 'source': source}
if len(value) > 500:
p['short_value'] = value[:500]
ps.append(p)
cxt['responsible_users'] = list(b.getResponsibleUsers())
(start, end) = b.getTimes()
cxt['start'] = time.ctime(start)
if end:
cxt['end'] = time.ctime(end)
cxt['elapsed'] = util.formatInterval(end - start)
else:
now = util.now()
cxt['elapsed'] = util.formatInterval(now - start)
cxt['exactly'] = (ss.revision is not None) or b.getChanges()
cxt['build_url'] = path_to_build(req, b)
cxt['authz'] = self.getAuthz(req)
template = req.site.buildbot_service.templates.get_template("build.html")
return template.render(**cxt)
def stop(self, req, auth_ok=False):
# check if this is allowed
if not auth_ok:
if not self.getAuthz(req).actionAllowed('stopBuild', req, self.build_status):
return Redirect(path_to_authfail(req))
b = self.build_status
log.msg("web stopBuild of build %s:%s" % \
(b.getBuilder().getName(), b.getNumber()))
name = req.args.get("username", ["<unknown>"])[0]
comments = req.args.get("comments", ["<no reason specified>"])[0]
# html-quote both the username and comments, just to be safe
reason = ("The web-page 'stop build' button was pressed by "
"'%s': %s\n" % (html.escape(name), html.escape(comments)))
c = interfaces.IControl(self.getBuildmaster(req))
bldrc = c.getBuilder(self.build_status.getBuilder().getName())
if bldrc:
bldc = bldrc.getBuild(self.build_status.getNumber())
if bldc:
bldc.stopBuild(reason)
# we're at http://localhost:8080/svn-hello/builds/5/stop?[args] and
# we want to go to: http://localhost:8080/svn-hello
r = Redirect(path_to_builder(req, self.build_status.getBuilder()))
d = defer.Deferred()
reactor.callLater(1, d.callback, r)
return DeferredResource(d)
def rebuild(self, req):
# check auth
if not self.getAuthz(req).actionAllowed('forceBuild', req, self.build_status.getBuilder()):
return Redirect(path_to_authfail(req))
# get a control object
c = interfaces.IControl(self.getBuildmaster(req))
bc = c.getBuilder(self.build_status.getBuilder().getName())
b = self.build_status
builder_name = b.getBuilder().getName()
log.msg("web rebuild of build %s:%s" % (builder_name, b.getNumber()))
name = req.args.get("username", ["<unknown>"])[0]
comments = req.args.get("comments", ["<no reason specified>"])[0]
reason = ("The web-page 'rebuild' button was pressed by "
"'%s': %s\n" % (name, comments))
extraProperties = getAndCheckProperties(req)
if not bc or not b.isFinished() or extraProperties is None:
log.msg("could not rebuild: bc=%s, isFinished=%s"
% (bc, b.isFinished()))
# TODO: indicate an error
else:
bc.rebuildBuild(b, reason, extraProperties)
# we're at
# http://localhost:8080/builders/NAME/builds/5/rebuild?[args]
# Where should we send them?
#
# Ideally it would be to the per-build page that they just started,
# but we don't know the build number for it yet (besides, it might
# have to wait for a current build to finish). The next-most
# preferred place is somewhere that the user can see tangible
# evidence of their build starting (or to see the reason that it
# didn't start). This should be the Builder page.
r = Redirect(path_to_builder(req, self.build_status.getBuilder()))
d = defer.Deferred()
reactor.callLater(1, d.callback, r)
return DeferredResource(d)
def getChild(self, path, req):
if path == "stop":
return self.stop(req)
if path == "rebuild":
return self.rebuild(req)
if path == "steps":
return StepsResource(self.build_status)
return HtmlResource.getChild(self, path, req)
# /builders/$builder/builds
class BuildsResource(HtmlResource):
addSlash = True
def __init__(self, builder_status):
HtmlResource.__init__(self)
self.builder_status = builder_status
def content(self, req, cxt):
return "subpages shows data for each build"
def getChild(self, path, req):
try:
num = int(path)
except ValueError:
num = None
if num is not None:
build_status = self.builder_status.getBuild(num)
if build_status:
return StatusResourceBuild(build_status)
return HtmlResource.getChild(self, path, req)