| OLD | NEW |
| (Empty) |
| 1 URL: http://buildbot.net/trac | |
| 2 GIT: git://github.com/nsylvain/buildbot.git in branch "chromium" | |
| 3 Sources: http://github.com/nsylvain/buildbot/tree/chromium | |
| 4 Version: 0.7.12 | |
| 5 License: GNU General Public License (GPL) Version 2 | |
| 6 | |
| 7 This is a forked copy of buildbot v0.7.12 with the modifications living | |
| 8 in github. To list the local modifications, use: | |
| 9 git log nsylvain/chromium ^v0.7.12 | |
| 10 | |
| 11 | |
| 12 The following change has been made to fix issues that are now fixed in | |
| 13 a newer version of the upstream repository. | |
| 14 | |
| 15 --- buildbot/status/web/console.py (revision 54225) | |
| 16 +++ buildbot/status/web/console.py (working copy) | |
| 17 @@ -431,11 +431,8 @@ | |
| 18 builds.append(devBuild) | |
| 19 | |
| 20 # Now break if we have enough builds. | |
| 21 - current_revision = self.getChangeForBuild( | |
| 22 - builder.getBuild(-1), revision) | |
| 23 - if self.comparator.isRevisionEarlier( | |
| 24 - devBuild, current_revision): | |
| 25 - break | |
| 26 + if int(got_rev) < int(revision): | |
| 27 + break; | |
| 28 | |
| 29 | |
| 30 Then made the following embelishment to allow the old behavior for | |
| 31 time based comparators. | |
| 32 | |
| 33 --- third_party/buildbot_7_12/buildbot/status/web/console.py (revision 68693) | |
| 34 +++ third_party/buildbot_7_12/buildbot/status/web/console.py (working copy) | |
| 35 @@ -450,8 +450,15 @@ | |
| 36 builds.append(devBuild) | |
| 37 | |
| 38 # Now break if we have enough builds. | |
| 39 - if int(got_rev) < int(revision): | |
| 40 - break; | |
| 41 + if self.comparator.getSortingKey() == "when": | |
| 42 + current_revision = self.getChangeForBuild( | |
| 43 + builder.getBuild(-1), revision) | |
| 44 + if self.comparator.isRevisionEarlier( | |
| 45 + devBuild, current_revision): | |
| 46 + break | |
| 47 + else: | |
| 48 + if int(got_rev) < int(revision): | |
| 49 + break; | |
| 50 | |
| 51 | |
| 52 build = build.getPreviousBuild() | |
| 53 | |
| 54 | |
| 55 Ignore invalid utf-8 strings in logs. | |
| 56 | |
| 57 --- third_party/buildbot_7_12/buildbot/status/web/console.py | |
| 58 +++ third_party/buildbot_7_12/buildbot/status/web/console.py | |
| 59 @@ -891,8 +891,11 @@ class ConsoleStatusResource(HtmlResource): | |
| 60 subs["date"] = revision.date | |
| 61 comment = revision.comments or "" | |
| 62 subs["comments"] = comment.replace('<', '<').replace('>', '>'
) | |
| 63 - comment_quoted = urllib.quote(subs["comments"].encode("utf-8")) | |
| 64 - | |
| 65 + try: | |
| 66 + comment_quoted = urllib.quote( | |
| 67 + subs["comments"].decode("utf-8", "ignore").encode( | |
| 68 + "ascii", "xmlcharrefreplace")) | |
| 69 + except UnicodeEncodeError: | |
| 70 + # TODO(maruel): Figure out what's happening. | |
| 71 + comment_quoted = urllib.quote(subs["comments"].encode("utf-8")) | |
| 72 json += ( "{'revision': '%s', 'date': '%s', 'comments': '%s'," | |
| 73 "'results' : " ) % (subs["revision"], subs["date"], | |
| 74 comment_quoted) | |
| 75 | |
| 76 | |
| 77 The following patch was ported back from upstream adding log names and URLs to | |
| 78 the JSON output. | |
| 79 | |
| 80 --- third_party/buildbot_7_12/buildbot/status/builder.py (revision 70505) | |
| 81 +++ third_party/buildbot_7_12/buildbot/status/builder.py (working copy) | |
| 82 @@ -1132,9 +1132,9 @@ | |
| 83 result['eta'] = self.getETA() | |
| 84 result['urls'] = self.getURLs() | |
| 85 result['step_number'] = self.step_number | |
| 86 - # TODO(maruel): Move that to a sub-url or just publish the log_url | |
| 87 - # instead. | |
| 88 - #result['logs'] = self.getLogs() | |
| 89 + result['logs'] = [[l.getName(), | |
| 90 + self.build.builder.status.getURLForThing(l)] | |
| 91 + for l in self.getLogs()] | |
| 92 return result | |
| 93 | |
| 94 | |
| 95 @@ -1565,8 +1565,8 @@ | |
| 96 result['slave'] = self.getSlavename() | |
| 97 # TODO(maruel): Add. | |
| 98 #result['test_results'] = self.getTestResults() | |
| 99 - # TODO(maruel): Include the url? It's too heavy otherwise. | |
| 100 - #result['logs'] = self.getLogs() | |
| 101 + result['logs'] = [[l.getName(), | |
| 102 + self.builder.status.getURLForThing(l)] for l in self.getLogs()] | |
| 103 result['eta'] = self.getETA() | |
| 104 result['steps'] = [bss.asDict() for bss in self.steps] | |
| 105 if self.getCurrentStep(): | |
| 106 | |
| 107 | |
| 108 The following patch disables rss and atom output. | |
| 109 | |
| 110 --- a/third_party/buildbot_7_12/buildbot/status/web/baseweb.py | |
| 111 +++ b/third_party/buildbot_7_12/buildbot/status/web/baseweb.py | |
| 112 @@ -594,8 +594,9 @@ class WebStatus(service.MultiService): | |
| 113 root.putChild(name, child_resource) | |
| 114 | |
| 115 status = self.getStatus() | |
| 116 - root.putChild("rss", Rss20StatusResource(status)) | |
| 117 - root.putChild("atom", Atom10StatusResource(status)) | |
| 118 + # Disabled from Chromium. | |
| 119 + # root.putChild("rss", Rss20StatusResource(status)) | |
| 120 + # root.putChild("atom", Atom10StatusResource(status)) | |
| 121 root.putChild("json", JsonStatusResource(status)) | |
| 122 | |
| 123 self.site.resource = root | |
| 124 | |
| 125 | |
| 126 Fixes c.number is None | |
| 127 --- a/master/buildbot/status/web/status_json.py | |
| 128 +++ b/master/buildbot/status/web/status_json.py | |
| 129 @@ -547,15 +547,13 @@ class ChangesJsonResource(JsonResource): | |
| 130 def __init__(self, status, changes): | |
| 131 JsonResource.__init__(self, status) | |
| 132 for c in changes: | |
| 133 - # TODO(maruel): Problem with multiple changes with the same number. | |
| 134 - # Probably try server hack specific so we could fix it on this side | |
| 135 - # instead. But there is still the problem with multiple pollers fro
m | |
| 136 - # different repo where the numbers could clash. | |
| 137 - number = str(c.number) | |
| 138 - while number in self.children: | |
| 139 - # TODO(maruel): Do something better? | |
| 140 - number = str(int(c.number)+1) | |
| 141 - self.putChild(number, ChangeJsonResource(status, c)) | |
| 142 + # c.number can be None or clash another change if the change was | |
| 143 + # generated inside buildbot or if using multiple pollers. | |
| 144 + if c.number is not None and str(c.number) not in self.children: | |
| 145 + self.putChild(str(c.number), ChangeJsonResource(status, c)) | |
| 146 + else: | |
| 147 + # Temporary hack since it creates information exposure. | |
| 148 + self.putChild(str(id(c)), ChangeJsonResource(status, c)) | |
| 149 | |
| 150 def asDict(self, request): | |
| 151 """Don't throw an exception when there is no child.""" | |
| 152 | |
| 153 | |
| 154 Add extra parameters to HttpStatusPush as a very basic authentication mechanism. | |
| 155 --- a/third_party/buildbot_7_12/buildbot/status/status_push.py | |
| 156 +++ b/third_party/buildbot_7_12/buildbot/status/status_push.py | |
| 157 @@ -321,7 +321,7 @@ class HttpStatusPush(StatusPush): | |
| 158 | |
| 159 def __init__(self, serverUrl, debug=None, maxMemoryItems=None, | |
| 160 maxDiskItems=None, chunkSize=200, maxHttpRequestSize=2**20, | |
| 161 - **kwargs): | |
| 162 + extra_post_params=None, **kwargs): | |
| 163 """ | |
| 164 @serverUrl: Base URL to be used to push events notifications. | |
| 165 @maxMemoryItems: Maximum number of items to keep queued in memory. | |
| 166 @@ -334,6 +334,7 @@ class HttpStatusPush(StatusPush): | |
| 167 """ | |
| 168 # Parameters. | |
| 169 self.serverUrl = serverUrl | |
| 170 + self.extra_post_params = extra_post_params or {} | |
| 171 self.debug = debug | |
| 172 self.chunkSize = chunkSize | |
| 173 self.lastPushWasSuccessful = True | |
| 174 @@ -371,7 +372,9 @@ class HttpStatusPush(StatusPush): | |
| 175 packets = json.dumps(items, indent=2, sort_keys=True) | |
| 176 else: | |
| 177 packets = json.dumps(items, separators=(',',':')) | |
| 178 - data = urllib.urlencode({'packets': packets}) | |
| 179 + params = {'packets': packets} | |
| 180 + params.update(self.extra_post) | |
| 181 + data = urllib.urlencode(params) | |
| 182 if (not self.maxHttpRequestSize or | |
| 183 len(data) < self.maxHttpRequestSize): | |
| 184 return (data, items) | |
| 185 @@ -389,6 +392,8 @@ class HttpStatusPush(StatusPush): | |
| 186 def pushHttp(self): | |
| 187 """Do the HTTP POST to the server.""" | |
| 188 (encoded_packets, items) = self.popChunk() | |
| 189 + if not self.serverUrl: | |
| 190 + return | |
| 191 | |
| 192 def Success(result): | |
| 193 """Queue up next push.""" | |
| 194 | |
| 195 | |
| 196 Add pendingBuilds | |
| 197 --- a/third_party/buildbot_7_12/buildbot/status/builder.py | |
| 198 +++ b/third_party/buildbot_7_12/buildbot/status/builder.py | |
| 199 @@ -2171,10 +2171,7 @@ class BuilderStatus(styles.Versioned): | |
| 200 result['cachedBuilds'] = cached_builds | |
| 201 result['currentBuilds'] = current_builds | |
| 202 result['state'] = self.getState()[0] | |
| 203 - # BuildRequestStatus doesn't have a number so display the SourceStamp. | |
| 204 - result['pendingBuilds'] = [ | |
| 205 - b.getSourceStamp().asDict() for b in self.getPendingBuilds() | |
| 206 - ] | |
| 207 + result['pendingBuilds'] = len(self.getPendingBuilds()) | |
| 208 return result | |
| 209 | |
| 210 | |
| 211 diff --git a/third_party/buildbot_7_12/buildbot/status/web/status_json.py b/thir
d_party/buildbot_7_12/buildbot/status/web/status_json. | |
| 212 index e3aaafe..6c272a6 100644 | |
| 213 --- a/third_party/buildbot_7_12/buildbot/status/web/status_json.py | |
| 214 +++ b/third_party/buildbot_7_12/buildbot/status/web/status_json.py | |
| 215 @@ -344,6 +344,20 @@ class HelpResource(HtmlResource): | |
| 216 return self.text | |
| 217 | |
| 218 | |
| 219 +class BuilderPendingBuildsJsonResource(JsonResource): | |
| 220 + help = """Describe pending builds for a builder. | |
| 221 +""" | |
| 222 + title = 'Builder' | |
| 223 + | |
| 224 + def __init__(self, status, builder_status): | |
| 225 + JsonResource.__init__(self, status) | |
| 226 + self.builder_status = builder_status | |
| 227 + | |
| 228 + def asDict(self, request): | |
| 229 + # buildbot.status.builder.BuilderStatus | |
| 230 + return [b.asDict() for b in self.builder_status.getPendingBuilds()] | |
| 231 + | |
| 232 + | |
| 233 class BuilderJsonResource(JsonResource): | |
| 234 help = """Describe a single builder. | |
| 235 """ | |
| 236 @@ -355,6 +369,9 @@ class BuilderJsonResource(JsonResource): | |
| 237 self.putChild('builds', BuildsJsonResource(status, builder_status)) | |
| 238 self.putChild('slaves', BuilderSlavesJsonResources(status, | |
| 239 builder_status)) | |
| 240 + self.putChild( | |
| 241 + 'pendingBuilds', | |
| 242 + BuilderPendingBuildsJsonResource(status, builder_status)) | |
| 243 | |
| 244 def asDict(self, request): | |
| 245 # buildbot.status.builder.BuilderStatus | |
| 246 | |
| 247 | |
| 248 Increase console customization build range: | |
| 249 | |
| 250 --- status/web/console.py (revision 75203) | |
| 251 +++ status/web/console.py (working copy) | |
| 252 @@ -971,10 +971,10 @@ | |
| 253 # Keep only the revisions we care about. | |
| 254 # By default we process the last 40 revisions. | |
| 255 # If a dev name is passed, we look for the changes by this person in th
e | |
| 256 - # last 80 revisions. | |
| 257 + # last 160 revisions. | |
| 258 numRevs = 40 | |
| 259 if devName: | |
| 260 - numRevs *= 2 | |
| 261 + numRevs *= 4 | |
| 262 numBuilds = numRevs | |
| 263 | |
| 264 | |
| 265 The following patches add support for ANSI to HTML conversion of logs | |
| 266 | |
| 267 Index: status/web/ansi2html.py | |
| 268 =================================================================== | |
| 269 --- status/web/ansi2html.py (revision 0) | |
| 270 +++ status/web/ansi2html.py (revision 0) | |
| 271 @@ -0,0 +1,161 @@ | |
| 272 +# Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 273 +# Use of this source code is governed by a BSD-style license that can be | |
| 274 +# found in the LICENSE file. | |
| 275 + | |
| 276 +import cStringIO | |
| 277 +import re | |
| 278 + | |
| 279 +DEFAULTS = { | |
| 280 + 'color' : 'white', | |
| 281 + 'background-color' : 'black', | |
| 282 + 'font-weight' : 'normal', | |
| 283 + 'text-decoration' : 'none', | |
| 284 +} | |
| 285 + | |
| 286 + | |
| 287 +class Ansi2HTML: | |
| 288 + """Class for converting text streams with ANSI codes into html""" | |
| 289 + | |
| 290 + ANSIEscape = '[' | |
| 291 + | |
| 292 + ANSIAttributes = { | |
| 293 + 0 : ['color:' + DEFAULTS['color'], | |
| 294 + 'font-weight:' + DEFAULTS['font-weight'], | |
| 295 + 'text-decoration:' + DEFAULTS['text-decoration'], | |
| 296 + 'background-color:' + DEFAULTS['background-color']], # reset | |
| 297 + 1 : ['font-weight:bold'], | |
| 298 + 2 : ['font-weight:lighter'], | |
| 299 + 4 : ['text-decoration:underline'], | |
| 300 + 5 : ['text-decoration:blink'], | |
| 301 + 7 : [], # invert attribute? | |
| 302 + 8 : [], # invisible attribute? | |
| 303 + 30 : ['color:black'], | |
| 304 + 31 : ['color:red'], | |
| 305 + 32 : ['color:green'], | |
| 306 + 33 : ['color:yellow'], | |
| 307 + 34 : ['color:blue'], | |
| 308 + 35 : ['color:magenta'], | |
| 309 + 36 : ['color:cyan'], | |
| 310 + 37 : ['color:white'], | |
| 311 + 39 : ['color:' + DEFAULTS['color']], | |
| 312 + 40 : ['background-color:black'], | |
| 313 + 41 : ['background-color:red'], | |
| 314 + 42 : ['background-color:green'], | |
| 315 + 43 : ['background-color:yellow'], | |
| 316 + 44 : ['background-color:blue'], | |
| 317 + 45 : ['background-color:magenta'], | |
| 318 + 46 : ['background-color:cyan'], | |
| 319 + 47 : ['background-color:white'], | |
| 320 + 49 : ['background-color:' + DEFAULTS['background-color']], | |
| 321 + } | |
| 322 + | |
| 323 + def __init__(self): | |
| 324 + self.ctx = {} | |
| 325 + # Send a 0 code, resetting ctx to defaults. | |
| 326 + self.attrib('0') | |
| 327 + # Prepare a regexp recognizing all ANSI codes. | |
| 328 + code_src = '|'.join(self.ANSICodes) | |
| 329 + # This captures non-greedy code argument and code itself, both grouped. | |
| 330 + self.code_re = re.compile("(.*?)(" + code_src + ")") | |
| 331 + | |
| 332 + def noop(self, arg): | |
| 333 + """Noop code, for ANSI codes that have no html equivalent.""" | |
| 334 + return '' | |
| 335 + | |
| 336 + def attrib(self, arg): | |
| 337 + """Text atribute code""" | |
| 338 + if arg == '': | |
| 339 + # Apparently, empty code argument means reset (0). | |
| 340 + arg = '0' | |
| 341 + for attr in arg.split(";"): | |
| 342 + try: | |
| 343 + for change in self.ANSIAttributes[int(attr)]: | |
| 344 + pieces = change.split(":") | |
| 345 + self.ctx[pieces[0]] = pieces[1] | |
| 346 + except KeyError: | |
| 347 + # Invalid key? Hmmm. | |
| 348 + return 'color:red">ANSI code not found: ' + \ | |
| 349 + arg + '<font style="color:' + self.ctx['color'] | |
| 350 + return self.printStyle() | |
| 351 + | |
| 352 + ANSICodes = { | |
| 353 + 'H' : noop, # cursor_pos, # ESC[y,xH - Cursor position y,x | |
| 354 + 'A' : noop, # cursor_up, # ESC[nA - Cursor Up n lines | |
| 355 + 'B' : noop, # cursor_down, # ESC[nB - Cursor Down n lines | |
| 356 + 'C' : noop, # cursor_forward, # ESC[nC - Cursor Forward n characters | |
| 357 + 'D' : noop, # cursor_backward, # ESC[nD - Cursor Backward n characters | |
| 358 + 'f' : noop, # cursor_xy, # ESC[y;xf - Cursor pos y,x (infrequent) | |
| 359 + 'R' : noop, # cursor_report, # ESC[y;xR - Cursor position report y,x | |
| 360 + 'n' : noop, # device_status, # ESC[6n - Dev status report (cursor pos) | |
| 361 + 's' : noop, # save_cursor, # ESC[s - Save cursor position | |
| 362 + 'u' : noop, # restore_cursor, # ESC[u - Restore cursor position | |
| 363 + 'J' : noop, # clrscr, # ESC[2J - Erase display | |
| 364 + 'K' : noop, # erase2eol, # ESC[K - Erase to end of line | |
| 365 + 'L' : noop, # insertlines, # ESC[nL - Inserts n blank lines at cursor | |
| 366 + 'M' : noop, # deletelines, # ESC[nM - Deletes n lines including cursor | |
| 367 + '@' : noop, # insertchars, # ESC[n@ - Inserts n blank chars at cursor | |
| 368 + 'P' : noop, # deletechars, # ESC[nP - Deletes n chars including cursor | |
| 369 + 'y' : noop, # translate, # ESC[n;ny - Output char translate | |
| 370 + 'p' : noop, # key_reassign, #ESC["str"p - Keyboard Key Reassignment | |
| 371 + 'm' : attrib, # ESC[n;n;...nm - Set attributes | |
| 372 + } | |
| 373 + | |
| 374 + def printStyle(self, showDefaults=False): | |
| 375 + """Returns a text representing the style of the current context.""" | |
| 376 + style = '' | |
| 377 + for attr in DEFAULTS: | |
| 378 + if self.ctx[attr] != DEFAULTS[attr] or showDefaults: | |
| 379 + style += attr + ':' + self.ctx[attr] + ';' | |
| 380 + return style | |
| 381 + | |
| 382 + def printHtmlHeader(self, title): | |
| 383 + text = '<html><head><title>%s</title></head>' % title | |
| 384 + text += '<body bgcolor="%s"><pre>' % DEFAULTS['background-color'] | |
| 385 + return text | |
| 386 + | |
| 387 + def printHtmlFooter(self): | |
| 388 + return '</pre></body></html>' | |
| 389 + | |
| 390 + def printHeader(self): | |
| 391 + """Envelopes everything into defaults <font> tag and opens a stub.""" | |
| 392 + self.attrib("0") # this means reset to default | |
| 393 + return '<font style="%s"><font>' % self.printStyle(showDefaults=True) | |
| 394 + | |
| 395 + def printFooter(self): | |
| 396 + """Closes both stub and envelope font tags.""" | |
| 397 + return '</font></font>' | |
| 398 + | |
| 399 + def parseBlock(self, string): | |
| 400 + """Takes a block of text and transform into html""" | |
| 401 + output = cStringIO.StringIO() | |
| 402 + skipfirst = True | |
| 403 + # Splitting by ANSIEscape turns the line into following elements: | |
| 404 + # arg,code,text | |
| 405 + # First two change the context, text is carried. | |
| 406 + for block in string.split(self.ANSIEscape): | |
| 407 + if not block: | |
| 408 + # First block is empty -> the line starts with escape code. | |
| 409 + skipfirst = False | |
| 410 + continue | |
| 411 + | |
| 412 + if skipfirst: | |
| 413 + # The line doesn't start with escape code -> skip first block. | |
| 414 + output.write(block) | |
| 415 + skipfirst = False | |
| 416 + continue | |
| 417 + | |
| 418 + match = self.code_re.match(block) | |
| 419 + if not match: | |
| 420 + # If there's no match, it is the line end. Don't parse it. | |
| 421 + output.write(block) | |
| 422 + continue | |
| 423 + | |
| 424 + parseFunc = self.ANSICodes[match.group(2)] | |
| 425 + # Replace ANSI codes with </font><font> sequence | |
| 426 + output.write('</font><font style="') | |
| 427 + output.write(parseFunc(self, match.group(1))) | |
| 428 + output.write('">') | |
| 429 + # Output the text | |
| 430 + output.write(block.split(match.group(2),1)[1]) | |
| 431 + | |
| 432 + return output.getvalue() | |
| 433 | |
| 434 | |
| 435 Index: status/web/logs.py | |
| 436 =================================================================== | |
| 437 --- status/web/logs.py (revision 95541) | |
| 438 +++ status/web/logs.py (working copy) | |
| 439 @@ -9,6 +9,7 @@ | |
| 440 from buildbot import interfaces | |
| 441 from buildbot.status import builder | |
| 442 from buildbot.status.web.base import IHTMLLog, HtmlResource | |
| 443 +from buildbot.status.web.ansi2html import Ansi2HTML | |
| 444 | |
| 445 | |
| 446 textlog_stylesheet = """ | |
| 447 @@ -59,7 +60,7 @@ | |
| 448 # it, so we can afford to track the request in the Resource. | |
| 449 implements(IHTMLLog) | |
| 450 | |
| 451 - asText = False | |
| 452 + printAs = "html" | |
| 453 subscribed = False | |
| 454 | |
| 455 def __init__(self, original): | |
| 456 @@ -67,9 +68,13 @@ | |
| 457 self.original = original | |
| 458 | |
| 459 def getChild(self, path, req): | |
| 460 - if path == "text": | |
| 461 - self.asText = True | |
| 462 + if path == "ansi": | |
| 463 + self.ansiParser = Ansi2HTML() | |
| 464 + | |
| 465 + if path == "text" or path == "ansi": | |
| 466 + self.printAs = path | |
| 467 return self | |
| 468 + | |
| 469 return HtmlResource.getChild(self, path, req) | |
| 470 | |
| 471 def htmlHeader(self, request): | |
| 472 @@ -80,6 +85,8 @@ | |
| 473 data += "<body vlink=\"#800080\">\n" | |
| 474 texturl = request.childLink("text") | |
| 475 data += '<a href="%s">(view as text)</a><br />\n' % texturl | |
| 476 + ansiurl = request.childLink("ansi") | |
| 477 + data += '<a href="%s">(view as ansi)</a><br />\n' % ansiurl | |
| 478 data += "<pre>\n" | |
| 479 return data | |
| 480 | |
| 481 @@ -90,9 +97,12 @@ | |
| 482 if type >= len(builder.ChunkTypes) or type < 0: | |
| 483 # non-std channel, don't display | |
| 484 continue | |
| 485 - if self.asText: | |
| 486 + if self.printAs == "text": | |
| 487 if type != builder.HEADER: | |
| 488 data += entry | |
| 489 + elif self.printAs == "ansi": | |
| 490 + if type != builder.HEADER: | |
| 491 + data += self.ansiParser.parseBlock(entry) | |
| 492 else: | |
| 493 data += spanfmt % (builder.ChunkTypes[type], | |
| 494 html.escape(entry)) | |
| 495 @@ -104,7 +114,7 @@ | |
| 496 return data | |
| 497 | |
| 498 def render_HEAD(self, request): | |
| 499 - if self.asText: | |
| 500 + if self.printAs == "text": | |
| 501 request.setHeader("content-type", "text/plain") | |
| 502 else: | |
| 503 request.setHeader("content-type", "text/html") | |
| 504 @@ -116,13 +126,16 @@ | |
| 505 def render_GET(self, req): | |
| 506 self.req = req | |
| 507 | |
| 508 - if self.asText: | |
| 509 + if self.printAs == "text": | |
| 510 req.setHeader("content-type", "text/plain") | |
| 511 else: | |
| 512 req.setHeader("content-type", "text/html") | |
| 513 | |
| 514 - if not self.asText: | |
| 515 + if self.printAs == "html": | |
| 516 req.write(self.htmlHeader(req)) | |
| 517 + if self.printAs == "ansi": | |
| 518 + req.write(self.ansiParser.printHtmlHeader("Log File Contents")) | |
| 519 + req.write(self.ansiParser.printHeader()) | |
| 520 | |
| 521 self.original.subscribeConsumer(ChunkConsumer(req, self)) | |
| 522 return server.NOT_DONE_YET | |
| 523 @@ -131,8 +144,11 @@ | |
| 524 if not self.req: | |
| 525 return | |
| 526 try: | |
| 527 - if not self.asText: | |
| 528 + if self.printAs == "html": | |
| 529 self.req.write(self.htmlFooter()) | |
| 530 + if self.printAs == "ansi": | |
| 531 + self.req.write(self.ansiParser.printFooter()) | |
| 532 + self.req.write(self.ansiParser.printHtmlFooter()) | |
| 533 self.req.finish() | |
| 534 except pb.DeadReferenceError: | |
| 535 pass | |
| 536 | |
| 537 | |
| 538 | |
| 539 Fix chrome-bot mis-syncs. | |
| 540 | |
| 541 Index: buildbot/changes/svnpoller.py | |
| 542 =================================================================== | |
| 543 --- buildbot/changes/svnpoller.py | |
| 544 +++ buildbot/changes/svnpoller.py | |
| 545 @@ -367,6 +367,19 @@ class SVNPoller(base.ChangeSource, util.ComparableMixin): | |
| 546 break | |
| 547 new_logentries.append(el) | |
| 548 new_logentries.reverse() # return oldest first | |
| 549 + | |
| 550 + # If the newest commit's author is chrome-bot, skip this commit. This | |
| 551 + # is a guard to ensure that we don't poll on our mirror while it could | |
| 552 + # be mid-sync. In that case, the author data could be wrong and would | |
| 553 + # look like it was a commit by chrome-bot@google.com. A downside: the | |
| 554 + # chrome-bot account may have a legitimate commit. This should not | |
| 555 + # happen generally, so we're okay waiting to see it until there's a | |
| 556 + # later commit with a non-chrome-bot author. | |
| 557 + if len(new_logentries) > 0: | |
| 558 + if new_logentries[-1].getAttribute("author") == 'chrome-bot@google.co
m': | |
| 559 + new_logentries.pop(-1) | |
| 560 + mostRecent = int(logentries[1].getAttribute("revision")) | |
| 561 + | |
| 562 return (mostRecent, new_logentries) | |
| 563 | |
| 564 def get_new_logentries(self, logentries): | |
| OLD | NEW |