1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5(function(){ 6 7var lkgrURL = 'http://chromium-status.appspot.com/lkgr'; 8 9// Interval at which to reload the non-CL bot status. 10var botStatusRefreshIntervalInMs = 60 * 1000; 11// Interval at which to check for LKGR updates. 12var lkgrRefreshIntervalInMs = 60 * 1000; 13 14function getClassForTryJobResult(result) { 15 // Some win bots seem to report a null result while building. 16 if (result === null) 17 result = buildbot.RUNNING; 18 19 switch (parseInt(result)) { 20 case buildbot.RUNNING: 21 return "running"; 22 23 case buildbot.SUCCESS: 24 return "success"; 25 26 case buildbot.WARNINGS: 27 return "warnings"; 28 29 case buildbot.FAILURE: 30 return "failure"; 31 32 case buildbot.SKIPPED: 33 return "skipped"; 34 35 case buildbot.EXCEPTION: 36 return "exception"; 37 38 case buildbot.RETRY: 39 return "retry"; 40 41 case buildbot.NOT_STARTED: 42 default: 43 return "never"; 44 } 45} 46 47// Remove try jobs that have been supplanted by newer runs. 48function filterOldTryJobs(tryJobs) { 49 var latest = {}; 50 tryJobs.forEach(function(tryJob) { 51 if (!latest[tryJob.builder] || 52 latest[tryJob.builder].buildnumber < tryJob.buildnumber) 53 latest[tryJob.builder] = tryJob; 54 }); 55 56 var result = []; 57 tryJobs.forEach(function(tryJob) { 58 if (tryJob.buildnumber == latest[tryJob.builder].buildnumber) 59 result.push(tryJob); 60 }); 61 62 return result; 63} 64 65function createTryJobAnchorTitle(tryJob, fullTryJob) { 66 var title = tryJob.builder; 67 68 if (!fullTryJob) 69 return title; 70 71 var stepText = []; 72 if (fullTryJob.currentStep) 73 stepText.push("running " + fullTryJob.currentStep.name); 74 75 if (fullTryJob.results == buildbot.FAILURE && fullTryJob.text) { 76 stepText.push(fullTryJob.text.join(" ")); 77 } else { 78 // Sometimes a step can fail without setting the try job text. Look 79 // through all the steps to identify if this is the case. 80 var text = []; 81 fullTryJob.steps.forEach(function(step) { 82 if (step.results[0] == buildbot.FAILURE) 83 text.push(step.results[1][0]); 84 }); 85 86 if (text.length > 0) { 87 text.unshift("failure"); 88 stepText.push(text.join(" ")); 89 } 90 } 91 92 if (stepText.length > 0) 93 title += ": " + stepText.join("; "); 94 95 return title; 96} 97 98function createPatchsetStatusElement(patchset) { 99 var table = document.createElement("div"); 100 table.className = "issue-status"; 101 102 var tryJobs = filterOldTryJobs(patchset.try_job_results); 103 tryJobs.forEach(function(tryJob) { 104 var key = tryJob.builder + "-" + tryJob.buildnumber; 105 var fullTryJob = patchset.full_try_job_results && 106 patchset.full_try_job_results[key]; 107 108 var tryJobAnchor = document.createElement("a"); 109 tryJobAnchor.textContent = " "; 110 tryJobAnchor.title = createTryJobAnchorTitle(tryJob, fullTryJob); 111 tryJobAnchor.className = "issue-status-build " + 112 getClassForTryJobResult(tryJob.result); 113 tryJobAnchor.target = "_blank"; 114 tryJobAnchor.href = tryJob.url; 115 table.appendChild(tryJobAnchor); 116 }); 117 118 return table; 119} 120 121function getLastFullPatchsetWithTryJobs(issue) { 122 var index = issue.patchsets.length - 1; 123 var fullPatchsets = issue.full_patchsets; 124 while (index >= 0 && 125 (!fullPatchsets || 126 !fullPatchsets[issue.patchsets[index]] || 127 !fullPatchsets[issue.patchsets[index]].try_job_results || 128 fullPatchsets[issue.patchsets[index]].try_job_results.length == 0)) { 129 index--; 130 } 131 132 return index >= 0 ? fullPatchsets[issue.patchsets[index]] : null; 133} 134 135function createTryStatusRow(issue) { 136 var table = document.getElementById("status-table"); 137 138 // Order by decreasing issue number. 139 var position = 140 document.getElementsByClassName("trunk-status-row")[0].rowIndex; 141 while (position > 0 && 142 parseInt(issue.issue) > 143 parseInt(table.rows[position - 1].getAttribute("data-issue"))) { 144 position--; 145 } 146 147 var row = table.insertRow(position); 148 row.setAttribute("data-issue", issue.issue); 149 150 return row; 151} 152 153function updateIssueDisplay(issue) { 154 var codereviewBaseURL = "https://codereview.chromium.org"; 155 156 var lastFullPatchset = getLastFullPatchsetWithTryJobs(issue); 157 158 var row = document.querySelector("*[data-issue='" + issue.issue + "']"); 159 if (!lastFullPatchset) { 160 if (row) 161 row.parentNode.removeChild(row); 162 return; 163 } 164 165 if (!row) 166 row = createTryStatusRow(issue); 167 168 var label = row.childNodes[0] || row.insertCell(-1); 169 var status = row.childNodes[1] || row.insertCell(-1); 170 171 label.className = "status-label"; 172 var clAnchor = label.childNodes[0] || 173 label.appendChild(document.createElement("a")); 174 clAnchor.textContent = "CL " + issue.issue; 175 clAnchor.href = codereviewBaseURL + "/" + issue.issue; 176 clAnchor.title = issue.subject; 177 if (lastFullPatchset && lastFullPatchset.message) 178 clAnchor.title += " | " + lastFullPatchset.message; 179 clAnchor.target = "_blank"; 180 181 var statusElement = createPatchsetStatusElement(lastFullPatchset); 182 if (status.childElementCount < 1) 183 status.appendChild(statusElement); 184 else 185 status.replaceChild(statusElement, status.firstChild); 186} 187 188function removeIssueDisplay(issueNumber) { 189 var row = document.querySelector("*[data-issue='" + issueNumber + "']"); 190 row.parentNode.removeChild(row); 191} 192 193function addTryStatusRows() { 194 buildbot.getActiveIssues().forEach(updateIssueDisplay); 195} 196 197function updateLKGR(lkgr) { 198 var link = document.getElementById('link_lkgr'); 199 link.textContent = 'LKGR (' + lkgr + ')'; 200} 201 202function addBotStatusRow(bot, className) { 203 var table = document.getElementById("status-table"); 204 205 var baseURL = "http://build.chromium.org/p/chromium" + 206 (bot.id != "" ? "." + bot.id : ""); 207 var consoleURL = baseURL + "/console"; 208 var statusURL = baseURL + "/horizontal_one_box_per_builder"; 209 210 var row = table.insertRow(-1); 211 row.className = "trunk-status-row " + className; 212 var label = row.insertCell(-1); 213 label.className = "status-label"; 214 var labelAnchor = document.createElement("a"); 215 labelAnchor.href = consoleURL; 216 labelAnchor.target = "_blank"; 217 labelAnchor.id = "link_" + bot.id; 218 labelAnchor.textContent = bot.label; 219 label.appendChild(labelAnchor); 220 221 var status = row.insertCell(-1); 222 status.className = "trunk-status-cell"; 223 var statusIframe = document.createElement("iframe"); 224 statusIframe.scrolling = "no"; 225 statusIframe.src = statusURL; 226 status.appendChild(statusIframe); 227} 228 229function addBotStatusRows() { 230 var closerBots = [ 231 {id: "", label: "Chromium"}, 232 {id: "win", label: "Win"}, 233 {id: "mac", label: "Mac"}, 234 {id: "linux", label: "Linux"}, 235 {id: "chromiumos", label: "ChromiumOS"}, 236 {id: "chrome", label: "Official"}, 237 {id: "memory", label: "Memory"} 238 ]; 239 240 var otherBots = [ 241 {id: "lkgr", label: "LKGR"}, 242 {id: "perf", label: "Perf"}, 243 {id: "memory.fyi", label: "Memory FYI"}, 244 {id: "gpu", label: "GPU"}, 245 {id: "gpu.fyi", label: "GPU FYI"} 246 ]; 247 248 closerBots.forEach(function(bot) { 249 addBotStatusRow(bot, "closer-status-row"); 250 }); 251 252 otherBots.forEach(function(bot) { 253 addBotStatusRow(bot, "other-status-row"); 254 }); 255} 256 257function fillStatusTable() { 258 addBotStatusRows(); 259 addTryStatusRows(); 260} 261 262function main() { 263 buildbot.requestURL(lkgrURL, "text", updateLKGR); 264 fillStatusTable(); 265 266 buildbot.getActiveIssues().setEventCallback(function(request) { 267 // NOTE(wittman): It doesn't appear that we can reliably detect closing of 268 // the popup and remove the event callback, so ensure the popup window is 269 // displayed before processing the event. 270 if (!chrome.extension.getViews({type: "popup"})) 271 return; 272 273 switch (request.event) { 274 case "issueUpdated": 275 case "issueAdded": 276 updateIssueDisplay(buildbot.getActiveIssues().getIssue(request.issue)); 277 break; 278 279 case "issueRemoved": 280 removeIssueDisplay(request.issue); 281 break; 282 } 283 }); 284 285 setInterval(function() { 286 buildbot.requestURL(lkgrURL, "text", updateLKGR); 287 }, lkgrRefreshIntervalInMs); 288 289 setInterval(function() { 290 var botStatusElements = 291 document.getElementsByClassName("trunk-status-iframe"); 292 for (var i = 0; i < botStatusElements.length; i++) 293 // Force a reload of the iframe in a way that doesn't cause cross-domain 294 // policy violations. 295 botStatusElements.item(i).src = botStatusElements.item(i).src; 296 }, botStatusRefreshIntervalInMs); 297} 298 299main(); 300 301})(); 302