| Index: tryconsole/static/builds.js
|
| ===================================================================
|
| --- tryconsole/static/builds.js (revision 0)
|
| +++ tryconsole/static/builds.js (revision 0)
|
| @@ -0,0 +1,358 @@
|
| +// Copyright (c) 2009-2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +/**
|
| + * @fileoverview Setups everything needed to show current build status.
|
| + */
|
| +
|
| +/**
|
| + * Namespace for functions related to the status of builds.
|
| + */
|
| +var builds = {};
|
| +
|
| +/**
|
| + * A function to aggregate together two build stage results.
|
| + * @param {string} a A build result.
|
| + * @param {string} b A build result.
|
| + * @return {string} The combined build result.
|
| + */
|
| +builds.statusJoin = function(a, b) {
|
| + if (a == 'exception' || b == 'exception') return 'exception';
|
| + if (a == 'failure' || b == 'failure') return 'failure';
|
| + if (a == 'running' || b == 'running') return 'running';
|
| + if (a == 'success' || b == 'success') return 'success';
|
| + return 'running';
|
| +};
|
| +
|
| +/**
|
| + * Mapping from build stage status to color name.
|
| + * @private
|
| + */
|
| +builds.BUILD_COLOR_MAP_ = {
|
| + 'failure': 'red',
|
| + 'warnings': 'orange',
|
| + 'running': 'yellow',
|
| + 'success': 'green',
|
| + 'exception': 'magenta',
|
| + '': 'gray',
|
| +};
|
| +
|
| +/**
|
| + * Decode the raw result of a build provided by build_info2.
|
| + * @param {string} rawResults a The raw info from build_info2 about a build.
|
| + * @return {!Array.<!Object>} A list of build names and links.
|
| + */
|
| +builds.decodeResults = function(rawResults) {
|
| + var rer = /<li><a href="([^"]*)">([^<]*)<\/a><\/li>/g;
|
| + var results = [];
|
| + var mr;
|
| + while (mr = rer.exec(rawResults)) {
|
| + var result = {
|
| + 'name': mr[2],
|
| + 'link': mr[1]
|
| + };
|
| + results.push(result);
|
| + }
|
| + return results;
|
| +};
|
| +
|
| +/**
|
| + * Decode the build steps portion of a build result (the stages).
|
| + * @param {string} logText The text of the steps and logs stage.
|
| + * @return {list of dicts} A list of dicts encoding each build stage and its
|
| + * current status.
|
| + */
|
| +builds.decodeBuildSteps = function(logText) {
|
| + var re = /<li><span class="([^"]*)"><a href="([^"]*)">([^<]*)<\/a>[^\]]*\[([^\]]*)\] \[([0-9]+) seconds\]<\/span>[ ]*<ol>[ ]*((?:<li><a href[^>]*>[^<]*<\/a><\/li>[ ]*)*)<\/ol>/g;
|
| + var steps = [];
|
| + var m;
|
| + while (m = re.exec(logText)) {
|
| + var name = m[4];
|
| + if (!name.length) name = m[3];
|
| + name = name.replace(/<.*>/, '');
|
| + var color;
|
| + if (m[1] in builds.BUILD_COLOR_MAP_) {
|
| + color = builds.BUILD_COLOR_MAP_[m[1]];
|
| + } else {
|
| + color = 'magenta';
|
| + }
|
| + var step = {
|
| + 'name': name,
|
| + 'status': m[1],
|
| + 'time': parseInt(m[5], 10),
|
| + 'color': color,
|
| + 'link': m[2],
|
| + 'results': builds.decodeResults(m[6]),
|
| + };
|
| + steps.push(step);
|
| + }
|
| + return steps;
|
| +};
|
| +
|
| +/**
|
| + * Summarize the overall status of a build based on the result of each stage.
|
| + * @param {list of dicts} steps A list of dicts describing the status of each
|
| + * build stage.
|
| + * @return {string} A status string (such as 'success').
|
| + */
|
| +builds.summarizeStatus = function(steps) {
|
| + var st = 'unknown';
|
| + var stepsLen = steps.length;
|
| + for (var s = 0; s < stepsLen; s++) {
|
| + st = builds.statusJoin(st, steps[s]['status']);
|
| + }
|
| + return st;
|
| +};
|
| +
|
| +/**
|
| + * Update the current list of builds based on the latest data and query.
|
| + * @param {string} id The id of the <div> containing the list of builds.
|
| + * @param {string} limitId The id of the prompt containing the current query.
|
| + * @param {dict} data Dictionary containing information on all builds.
|
| + */
|
| +builds.redrawUpdate = function(id, limitId, data) {
|
| + // Get limit string if any.
|
| + var limitStr = '';
|
| + var limitElement = document.getElementById(limitId);
|
| + if (limitElement) {
|
| + limitStr = limitElement.value.toLowerCase();
|
| + }
|
| +
|
| + // Get dirty bit.
|
| + dirty = data['dirty'];
|
| + if (limitStr != data['last_limit']) dirty = true;
|
| + data['dirty'] = false;
|
| + data['last_limit'] = limitStr;
|
| +
|
| + // Quit now if not dirty.
|
| + if (!dirty) {
|
| + window.setTimeout(function() {
|
| + builds.redrawUpdate(id, limitId, data);
|
| + }, 30);
|
| + return;
|
| + }
|
| +
|
| + // Do the update.
|
| + var jobs = {};
|
| + var offset = 0;
|
| + // Update job set.
|
| + for (var bid in data) {
|
| + // Skip short ones (used for other purposes).
|
| + if (bid.length < 15) continue;
|
| + // Get out item.
|
| + var b = data[bid];
|
| + // Skip empty ones.
|
| + if (!('last_checked' in b)) continue;
|
| + // Skip old busted builds for now.
|
| + if (!('changed_at' in b)) continue;
|
| + // Skip if doesn't match limit string.
|
| + if (b['raw_reason'].toLowerCase().search(limitStr) < 0 &&
|
| + b['account_name'].toLowerCase().search(limitStr) < 0 &&
|
| + b['builder_name'].toLowerCase().search(limitStr) < 0) continue;
|
| + // Prepare a key.
|
| + var key = b['raw_reason'] + '|' + b['changed_at'];
|
| + if (!(key in jobs)) {
|
| + jobs[key] = {
|
| + 'title': b['patch_name'],
|
| + 'username': b['username'],
|
| + 'submitted_at': '' + b['changed_at'],
|
| + 'builds': [],
|
| + 'offset': offset,
|
| + 'status': 'unknown',
|
| + };
|
| + offset++;
|
| + }
|
| + var steps = builds.decodeBuildSteps(b['steps_and_logfiles']);
|
| + item = {
|
| + 'title': b['builder_name'],
|
| + 'eta': b['etaSeconds'],
|
| + 'steps': steps,
|
| + 'status': builds.summarizeStatus(steps),
|
| + 'link_base': b['master_url'] + '/builders/' + b['builder_name'],
|
| + 'build_number': b['build_number'],
|
| + };
|
| + jobs[key]['builds'].push(item);
|
| + jobs[key]['status'] = builds.statusJoin(jobs[key]['status'],
|
| + item['status']);
|
| + }
|
| +
|
| + // Flatten dict.
|
| + jobList = [];
|
| + for (var j in jobs) {
|
| + jobList.push(jobs[j]);
|
| + }
|
| + jobList = jobList.sort(function(a, b) {
|
| + var aRunning = a['status'] == 'running';
|
| + var bRunning = b['status'] == 'running';
|
| + if (aRunning && !bRunning) return -1;
|
| + if (bRunning && !aRunning) return 1;
|
| + return a['offset'] - b['offset']});
|
| +
|
| + // Clip if > 50
|
| + if (jobList.length > 50) {
|
| + jobList = jobList.slice(0, 50);
|
| + }
|
| +
|
| + // Format into html.
|
| + var html = '<dl class="job">';
|
| + var jobListLen = jobList.length;
|
| + for (var job = 0; job < jobListLen; job++) {
|
| + var jobi = jobList[job];
|
| + html += '<dt class="' + jobi['status'] + '"><b>' +
|
| + jobi['title'] + '</b> - ' + jobi['username'] +
|
| + ' - <i>' + jobi['submitted_at'] +
|
| + '</i></dt><dd><dl class="build">';
|
| + var buildsLen = jobi['builds'].length;
|
| + for (var build = 0; build < buildsLen; build++) {
|
| + var buildi = jobi['builds'][build];
|
| + var etaMin = Math.floor(buildi['eta'] / 60);
|
| + var etaSec = Math.floor(buildi['eta'] % 60);
|
| + var link = buildi['link_base'] + '/builds/' + buildi['build_number'];
|
| + html += '<dt><a href="' + link + '" target="_blank">' +
|
| + buildi['title'] + '</a>';
|
| + if (etaMin || etaSec) {
|
| + html += '- <i>ETA ' + etaMin + ' minutes, ' +
|
| + etaSec + ' seconds</i>';
|
| + }
|
| + html += '</dt>' +
|
| + '<dd class="' + buildi['status'] + '">' +
|
| + '<table border="1" cellpadding="4" rules="groups" frame="box">';
|
| + var stepsLen = buildi['steps'].length;
|
| + for (var step = 0; step < stepsLen; step++) {
|
| + html += '<colgroup></colgroup>';
|
| + }
|
| + html += '<tr>';
|
| + for (var step = 0; step < stepsLen; step++) {
|
| + html += '<td align="center">';
|
| + var stepi = buildi['steps'][step];
|
| + if (stepi['results'].length == 1) {
|
| + var link = stepi['results'][0]['link'];
|
| + } else {
|
| + var link = stepi['link'];
|
| + }
|
| + link = buildi['link_base'] + '/builds/' + link;
|
| + if (stepi['color'] != 'gray') {
|
| + html += '<a href="' + link + '" target="_blank">';
|
| + }
|
| + html += '<img src="/static/' + stepi['color'] +
|
| + '.png" width="32" height="32">';
|
| + if (stepi['color'] != 'gray') {
|
| + html += '</a>';
|
| + }
|
| + html += '</td>';
|
| + }
|
| + html += '</tr>' + '<tr>';
|
| + for (var step = 0; step < stepsLen; step++) {
|
| + var stepi = buildi['steps'][step];
|
| + html += '<td align="center">' +
|
| + '<a style="font-size:50%">' + stepi['name'] + '</a>' +
|
| + '</td>';
|
| + }
|
| + html += '</tr></table></dd>';
|
| + }
|
| + html += '</dl></dd>';
|
| + }
|
| + html += '</dl>';
|
| + var e = document.getElementById(id);
|
| + e.innerHTML = html;
|
| +
|
| + // Do it again.
|
| + window.setTimeout(function() {
|
| + builds.redrawUpdate(id, limitId, data);
|
| + }, 30);
|
| +};
|
| +
|
| +/**
|
| + * Update the cached status of a single build (in the background).
|
| + * @param {string} bid The string id of the build (datastore key).
|
| + * @param {dict} data Dictionary containing information on all builds.
|
| + */
|
| +builds.updateOne = function(bid, data) {
|
| + // Fetch a info on this one build.
|
| + http.getText(
|
| + '/build_info2?id=' + bid,
|
| + function(text) {
|
| + if (text != null) {
|
| + // Decode it.
|
| + var rows = text.split('\n');
|
| + var rowsLen = rows.length;
|
| + for (var r = 0; r < rowsLen; r++) {
|
| + var row = rows[r];
|
| + var mid = row.search(' ');
|
| + if (mid < 0) continue;
|
| + var key = row.substr(0, mid);
|
| + var value = row.substr(mid + 1);
|
| + data[bid][key] = value;
|
| + }
|
| + }
|
| + // Put it back in if end is none.
|
| + if (!('end' in data[bid]) || data[bid]['end'] == 'None') {
|
| + data['pending'].push(bid);
|
| + }
|
| + // Note the change.
|
| + data['dirty'] = true;
|
| + });
|
| +};
|
| +
|
| +/**
|
| + * Update the cached status of all pending builds (in the background).
|
| + * @param {dict} data Dictionary containing information on all builds.
|
| + */
|
| +builds.update = function(data) {
|
| + // Request all pending builds.
|
| + var pendingLen = data['pending'].length;
|
| + for (var bidi = 0; bidi < pendingLen; bidi++) {
|
| + var bid = data['pending'][bidi];
|
| + builds.updateOne(bid, data);
|
| + }
|
| + data['pending'] = [];
|
| +};
|
| +
|
| +/**
|
| + * Setup the list of builds to manage themselves.
|
| + * @param {string} id The id of the <div> to contain the list of builds.
|
| + * @param {string} limitId The id of the prompt containing the current query.
|
| + * @param {dict} data Dictionary containing information on all builds.
|
| + */
|
| +builds.setup = function(id, limitId, data) {
|
| + // Setup blank pending set.
|
| + if (!('pending' in data)) {
|
| + data['pending'] = [];
|
| + data['last_limit'] = '';
|
| + data['dirty'] = true;
|
| + data['fresh_start'] = true;
|
| + builds.redrawUpdate(id, limitId, data);
|
| + }
|
| + // Pick url to use.
|
| + if (data['fresh_start']) {
|
| + data['fresh_start'] = false;
|
| + url = '/list_builds?initial=1';
|
| + } else {
|
| + url = '/list_builds';
|
| + }
|
| + // Fetch a set of builds.
|
| + http.getText(url,
|
| + function(text) {
|
| + if (text != null) {
|
| + // Update set of ids.
|
| + var buildIds = text.split('\n');
|
| + var buildIdsLen = buildIds.length;
|
| + for (var bid = 0; bid < buildIdsLen; bid++) {
|
| + var bidv = buildIds[bid];
|
| + if (bidv == '') continue;
|
| + if (!(bidv in data)) {
|
| + data[bidv] = {};
|
| + data['pending'].push(bidv);
|
| + }
|
| + }
|
| + }
|
| + // Update each build.
|
| + builds.update(data);
|
| + // Do it again.
|
| + window.setTimeout(function() {
|
| + builds.setup(id, limitId, data);
|
| + }, 20000);
|
| + });
|
| +};
|
| +
|
|
|
| Property changes on: tryconsole/static/builds.js
|
| ___________________________________________________________________
|
| Added: svn:mergeinfo
|
|
|
|
|