Package buildbot :: Package status :: Package web :: Module build
[frames] | no frames]

Source Code for Module buildbot.status.web.build

  1   
  2  from twisted.web import html 
  3  from twisted.web.util import Redirect, DeferredResource 
  4  from twisted.internet import defer, reactor 
  5   
  6  import urllib, time 
  7  from twisted.python import log 
  8  from buildbot.status.web.base import HtmlResource, make_row, make_stop_form, \ 
  9       make_extra_property_row, css_classes, path_to_builder, path_to_slave, \ 
 10       make_name_user_passwd_form, getAndCheckProperties 
 11   
 12   
 13  from buildbot.status.web.tests import TestsResource 
 14  from buildbot.status.web.step import StepsResource 
 15  from buildbot import version, util 
 16   
 17  # /builders/$builder/builds/$buildnum 
18 -class StatusResourceBuild(HtmlResource):
19 addSlash = True 20
21 - def __init__(self, build_status, build_control, builder_control):
22 HtmlResource.__init__(self) 23 self.build_status = build_status 24 self.build_control = build_control 25 self.builder_control = builder_control
26
27 - def getTitle(self, request):
28 return ("Buildbot: %s Build #%d" % 29 (html.escape(self.build_status.getBuilder().getName()), 30 self.build_status.getNumber()))
31
32 - def body(self, req):
33 b = self.build_status 34 status = self.getStatus(req) 35 projectName = status.getProjectName() 36 projectURL = status.getProjectURL() 37 data = ('<div class="title"><a href="%s">%s</a></div>\n' 38 % (self.path_to_root(req), projectName)) 39 builder_name = b.getBuilder().getName() 40 data += ("<h1><a href=\"%s\">Builder %s</a>: Build #%d</h1>\n" 41 % (path_to_builder(req, b.getBuilder()), 42 builder_name, b.getNumber())) 43 44 if not b.isFinished(): 45 data += "<h2>Build In Progress</h2>" 46 when = b.getETA() 47 if when is not None: 48 when_time = time.strftime("%H:%M:%S", 49 time.localtime(time.time() + when)) 50 data += "<div>ETA %ds (%s)</div>\n" % (when, when_time) 51 52 if self.build_control is not None: 53 stopURL = urllib.quote(req.childLink("stop")) 54 data += make_stop_form(stopURL, self.isUsingUserPasswd(req)) 55 56 if b.isFinished(): 57 # Results map loosely to css_classes 58 results = b.getResults() 59 data += "<h2>Results:</h2>\n" 60 text = " ".join(b.getText()) 61 data += '<span class="%s">%s</span>\n' % (css_classes[results], 62 text) 63 if b.getTestResults(): 64 url = req.childLink("tests") 65 data += "<h3><a href=\"%s\">test results</a></h3>\n" % url 66 67 ss = b.getSourceStamp() 68 data += "<h2>SourceStamp:</h2>\n" 69 data += " <ul>\n" 70 if ss.branch: 71 data += " <li>Branch: %s</li>\n" % html.escape(ss.branch) 72 if ss.revision: 73 data += " <li>Revision: %s</li>\n" % html.escape(str(ss.revision)) 74 if ss.patch: 75 data += " <li>Patch: YES</li>\n" # TODO: provide link to .diff 76 if ss.changes: 77 data += " <li>Changes: see below</li>\n" 78 if (ss.branch is None and ss.revision is None and ss.patch is None 79 and not ss.changes): 80 data += " <li>build of most recent revision</li>\n" 81 got_revision = None 82 try: 83 got_revision = b.getProperty("got_revision") 84 except KeyError: 85 pass 86 if got_revision: 87 got_revision = str(got_revision) 88 if len(got_revision) > 40: 89 got_revision = "[revision string too long]" 90 data += " <li>Got Revision: %s</li>\n" % got_revision 91 data += " </ul>\n" 92 93 # TODO: turn this into a table, or some other sort of definition-list 94 # that doesn't take up quite so much vertical space 95 try: 96 slaveurl = path_to_slave(req, status.getSlave(b.getSlavename())) 97 data += "<h2>Buildslave:</h2>\n <a href=\"%s\">%s</a>\n" % (html.escape(slaveurl), html.escape(b.getSlavename())) 98 except KeyError: 99 data += "<h2>Buildslave:</h2>\n %s\n" % html.escape(b.getSlavename()) 100 data += "<h2>Reason:</h2>\n%s\n" % html.escape(b.getReason()) 101 102 data += "<h2>Steps and Logfiles:</h2>\n" 103 # TODO: 104 # urls = self.original.getURLs() 105 # ex_url_class = "BuildStep external" 106 # for name, target in urls.items(): 107 # text.append('[<a href="%s" class="%s">%s</a>]' % 108 # (target, ex_url_class, html.escape(name))) 109 data += "<ol>\n" 110 for s in b.getSteps(): 111 name = s.getName() 112 time_to_run = 0 113 (start, end) = s.getTimes() 114 if start and end: 115 time_to_run = end - start 116 if s.isFinished(): 117 css_class = css_classes[s.getResults()[0]] 118 elif s.isStarted(): 119 css_class = "running" 120 else: 121 css_class = "" 122 data += (' <li><span class="%s"><a href=\"%s\">%s</a> [%s] [%d seconds]</span>\n' 123 % (css_class, 124 req.childLink("steps/%s" % urllib.quote(name)), 125 name, 126 " ".join(s.getText()), 127 time_to_run)) 128 data += " <ol>\n" 129 if s.getLogs(): 130 for logfile in s.getLogs(): 131 logname = logfile.getName() 132 logurl = req.childLink("steps/%s/logs/%s" % 133 (urllib.quote(name), 134 urllib.quote(logname))) 135 data += (" <li><a href=\"%s\">%s</a></li>\n" % 136 (logurl, logfile.getName())) 137 if s.getURLs(): 138 for url in s.getURLs().items(): 139 logname = url[0] 140 logurl = url[1] 141 data += (' <li><a href="%s">%s</a></li>\n' % 142 (logurl, html.escape(logname))) 143 data += "</ol>\n" 144 data += " </li>\n" 145 146 data += "</ol>\n" 147 148 data += "<h2>Build Properties:</h2>\n" 149 data += "<table><tr><th valign=\"left\">Name</th><th valign=\"left\">Value</th><th valign=\"left\">Source</th></tr>\n" 150 for name, value, source in b.getProperties().asList(): 151 value = str(value) 152 if len(value) > 500: 153 value = value[:500] + " .. [property value too long]" 154 data += "<tr>" 155 data += "<td>%s</td>" % html.escape(name) 156 data += "<td>%s</td>" % html.escape(value) 157 data += "<td>%s</td>" % html.escape(source) 158 data += "</tr>\n" 159 data += "</table>" 160 161 data += "<h2>Blamelist:</h2>\n" 162 if list(b.getResponsibleUsers()): 163 data += " <ol>\n" 164 for who in b.getResponsibleUsers(): 165 data += " <li>%s</li>\n" % html.escape(who) 166 data += " </ol>\n" 167 else: 168 data += "<div>no responsible users</div>\n" 169 170 171 (start, end) = b.getTimes() 172 data += "<h2>Timing</h2>\n" 173 data += "<table>\n" 174 data += "<tr><td>Start</td><td>%s</td></tr>\n" % time.ctime(start) 175 if end: 176 data += "<tr><td>End</td><td>%s</td></tr>\n" % time.ctime(end) 177 data += "<tr><td>Elapsed</td><td>%s</td></tr>\n" % util.formatInterval(end - start) 178 else: 179 now = util.now() 180 data += "<tr><td>Elapsed</td><td>%s</td></tr>\n" % util.formatInterval(now - start) 181 data += "</table>\n" 182 183 if ss.changes: 184 data += "<h2>All Changes</h2>\n" 185 data += "<ol>\n" 186 for c in ss.changes: 187 data += "<li>" + c.asHTML() + "</li>\n" 188 data += "</ol>\n" 189 #data += html.PRE(b.changesText()) # TODO 190 191 if b.isFinished() and self.builder_control is not None: 192 data += "<h3>Resubmit Build:</h3>\n" 193 # can we rebuild it exactly? 194 exactly = (ss.revision is not None) or b.getChanges() 195 if exactly: 196 data += ("<p>This tree was built from a specific set of \n" 197 "source files, and can be rebuilt exactly</p>\n") 198 else: 199 data += ("<p>This tree was built from the most recent " 200 "revision") 201 if ss.branch: 202 data += " (along some branch)" 203 data += (" and thus it might not be possible to rebuild it \n" 204 "exactly. Any changes that have been committed \n" 205 "after this build was started <b>will</b> be \n" 206 "included in a rebuild.</p>\n") 207 rebuildURL = urllib.quote(req.childLink("rebuild")) 208 data += ('<form method="post" action="%s" class="command rebuild">\n' 209 % rebuildURL) 210 data += make_name_user_passwd_form(self.isUsingUserPasswd(req)) 211 data += make_extra_property_row(1) 212 data += make_extra_property_row(2) 213 data += make_extra_property_row(3) 214 data += make_row("Reason for re-running build:", 215 "<input type='text' name='comments' />") 216 data += '<input type="submit" value="Rebuild" />\n' 217 data += '</form>\n' 218 219 data += self.footer(status, req) 220 221 return data
222
223 - def stop(self, req):
224 if self.isUsingUserPasswd(req): 225 if not self.authUser(req): 226 return Redirect("../../../authfailed") 227 b = self.build_status 228 c = self.build_control 229 log.msg("web stopBuild of build %s:%s" % \ 230 (b.getBuilder().getName(), b.getNumber())) 231 name = req.args.get("username", ["<unknown>"])[0] 232 comments = req.args.get("comments", ["<no reason specified>"])[0] 233 # html-quote both the username and comments, just to be safe 234 reason = ("The web-page 'stop build' button was pressed by " 235 "'%s': %s\n" % (html.escape(name), html.escape(comments))) 236 if c: 237 c.stopBuild(reason) 238 # we're at http://localhost:8080/svn-hello/builds/5/stop?[args] and 239 # we want to go to: http://localhost:8080/svn-hello 240 r = Redirect("../..") 241 d = defer.Deferred() 242 reactor.callLater(1, d.callback, r) 243 return DeferredResource(d)
244
245 - def rebuild(self, req):
246 if self.isUsingUserPasswd(req): 247 if not self.authUser(req): 248 return Redirect("../../../authfailed") 249 b = self.build_status 250 bc = self.builder_control 251 builder_name = b.getBuilder().getName() 252 log.msg("web rebuild of build %s:%s" % (builder_name, b.getNumber())) 253 name = req.args.get("username", ["<unknown>"])[0] 254 comments = req.args.get("comments", ["<no reason specified>"])[0] 255 reason = ("The web-page 'rebuild' button was pressed by " 256 "'%s': %s\n" % (html.escape(name), html.escape(comments))) 257 extraProperties = getAndCheckProperties(req) 258 if not bc or not b.isFinished() or extraProperties is None: 259 log.msg("could not rebuild: bc=%s, isFinished=%s" 260 % (bc, b.isFinished())) 261 # TODO: indicate an error 262 else: 263 bc.resubmitBuild(b, reason, extraProperties) 264 # we're at 265 # http://localhost:8080/builders/NAME/builds/5/rebuild?[args] 266 # Where should we send them? 267 # 268 # Ideally it would be to the per-build page that they just started, 269 # but we don't know the build number for it yet (besides, it might 270 # have to wait for a current build to finish). The next-most 271 # preferred place is somewhere that the user can see tangible 272 # evidence of their build starting (or to see the reason that it 273 # didn't start). This should be the Builder page. 274 r = Redirect("../..") # the Builder's page 275 d = defer.Deferred() 276 reactor.callLater(1, d.callback, r) 277 return DeferredResource(d)
278
279 - def getChild(self, path, req):
280 if path == "stop": 281 return self.stop(req) 282 if path == "rebuild": 283 return self.rebuild(req) 284 if path == "steps": 285 return StepsResource(self.build_status) 286 if path == "tests": 287 return TestsResource(self.build_status) 288 289 return HtmlResource.getChild(self, path, req)
290 291 # /builders/$builder/builds
292 -class BuildsResource(HtmlResource):
293 addSlash = True 294
295 - def __init__(self, builder_status, builder_control):
296 HtmlResource.__init__(self) 297 self.builder_status = builder_status 298 self.builder_control = builder_control
299
300 - def getChild(self, path, req):
301 try: 302 num = int(path) 303 except ValueError: 304 num = None 305 if num is not None: 306 build_status = self.builder_status.getBuild(num) 307 if build_status: 308 if self.builder_control: 309 build_control = self.builder_control.getBuild(num) 310 else: 311 build_control = None 312 return StatusResourceBuild(build_status, build_control, 313 self.builder_control) 314 315 return HtmlResource.getChild(self, path, req)
316