YUI(Yconfig).use(
'gallery-base64', 'node', 'history', 'io', 'cookie', 'querystring-stringify-simple', 'json',
'linaro-overlay-utils', 'linaro-trivia', 'jsonp', 'datatable',
function (Y) {
var history = new Y.HistoryHash();
Y.on(
'history:change',
function (e) {
if (e.src === Y.HistoryHash.SRC_HASH) {
// URL changed
if (typeof getDownloadURL.downloadUrl != "undefined")
{
delete getDownloadURL.downloadUrl;
}
displayJobByNumber(e.changed.build.newVal);
}
}
);
var allBuilds;
var buildNumber = history.get('build');
var fetchLatest = (buildNumber === undefined);
var jobData = 'actions[parameters[name,value]],timestamp,duration,artifacts[displayPath,fileName,relativePath],result,number';
var buildName = rawBuildName.replace('/', '_').substring(1);
var treeExp = 'actions[parameterDefinitions[defaultParameterValue[value],name]],builds[number],queueItem[params],description';
if (fetchLatest) {
treeExp += ',lastBuild[' + jobData + ']';
}
var jobUrl = globalConfig.jenkinsURL + "/job/" + buildName;
function patternInName (name, list) {
// Check for patterns from 'list' in 'name'.
// If found returns 'true' otherwise 'false'.
for (var i = 0; i < list.length; i++) {
if (name.indexOf(list[i])>=0)
{
return true;
}
}
return false;
}
function receivedTestResultJSON (response, loadingNode) {
var columns = [
{label:"Name", formatter: function (o) {
return '' + o.data.name + '';
}
},
{label:"Pass", formatter: function (o) { return o.data.results.pass || 0; } },
{label:"Fail", formatter: function (o) { return o.data.results.fail || 0; } },
{label:"Measurement", formatter: function (o) {
var measurements = o.data.results.measurements;
if (!measurements || (measurements.length == 0)){
return " ";
}
var contents = new Array();
var index = 0;
for (var index=0; index < measurements.length; index++){
var item = measurements[index];
var oneLine = "
";
oneLine = oneLine + "" + item.item +" | ";
oneLine = oneLine + "" + item.measurement +" | ";
oneLine = oneLine + "" + item.units +" | ";
oneLine = oneLine + "
";
contents[index] = oneLine;
}
return "";
}}
];
var recordset = new Y.Recordset({records: response.test_runs});
var newTable = Y.Node.create('');
loadingNode.replace(newTable);
var dt = new Y.DataTable.Base(
{
columnset: columns
});
dt.set('recordset', recordset);
dt.render(newTable);
}
function receivedJobJSON (response, job_info_json_url) {
var newNode = Y.Node.create('');
newNode.appendChild(Y.Node.create('').setContent('Status: ' + response.status));
var linkNode = Y.Node.create('Job details
');
linkNode.one('a').setAttribute('href', job_info_json_url);
if (response.results_link) {
var loadingString = Y.Node.create('Loading
');
newNode.appendChild(loadingString);
var resultLink = Y.Node.create('Result details');
resultLink.setAttribute('href', response.results_link);
linkNode.appendChild(document.createTextNode(' '));
linkNode.appendChild(resultLink);
Y.jsonp(
response.results_link + 'json?callback={callback}',
{
args: [loadingString],
on: {
success: receivedTestResultJSON
}
}
);
}
newNode.appendChild(linkNode);
Y.one("#testresults").get('childNodes').remove();
Y.one("#testresults").appendChild(newNode);
}
function receivedLavaJobInfo (e, response) {
var job_info;
try {
job_info = Y.JSON.parse(response.responseText);
} catch (e) {
Y.one("#testresults").setContent("Unexpected error getting LAVA job id");
console.error("Exception parsing (expected) lava-job-id file: ", e, response.responseText);
return;
}
if (typeof job_info.lava_url == "undefined") {
Y.one("#testresults").setContent("This build did not undergo LAVA testing.");
return;
}
var job_info_json_url = job_info.lava_url;
if (job_info_json_url.substr(job_info_json_url.length - 1) != '/') {
job_info_json_url = job_info_json_url + '/';
}
job_info_json_url = job_info_json_url + 'scheduler/job/' + job_info.job_id;
Y.one("#testresults").append("
Requesting LAVA job information...");
Y.jsonp(
job_info_json_url + '/json?callback={callback}',
{
args: [job_info_json_url],
on: {
success: receivedJobJSON,
failure: function (e) {
// TODO: This should check for 403 first, but wouldn't
// work due to lp:1003817
Y.one("#testresults").append('
To see test results, please '
+ 'login '
+ 'to LAVA and refresh this page. '
+ '(Test results are available to Linaro members).');
}
}
}
);
}
function getProxyURL ()
{
return '/api/proxy-remote-file?url=';
}
function getDownloadURL(results)
{
/* Work out where to download from. The assumption is that all
downloads will end up on snapshots.linaro.org soon, but we want to
detect if they haven't moved. If we can't find a build on
snapshots and we can find it locally, point the download location
to the local build. If we can't find it locally either, keep pointing
at snapshots.
*/
if (typeof getDownloadURL.downloadUrl != "undefined")
{
return getDownloadURL.downloadUrl; // Return cached result
}
var downloadUrl = 'http://snapshots.linaro.org/android/' + rawBuildName + '/' + results.number.toString() + '/';
var test_snapshots = Y.io(
'/api/test-dir-exists?url=' + downloadUrl,
{
sync: true
});
if (test_snapshots.status != 200) // snapshots.linaro.org doesn't host this build
{
// Test to see if the local server (probably android-build.linaro.org) does.
var testUrl = 'http://android-build.linaro.org/builds/' + rawBuildName + '/' + results.number.toString() + '/';
var test_local = Y.io(
'/api/test-dir-exists?url=' + testUrl,
{
sync: true
});
if (test_local.status == 200)
{
// Found a build host - update download link.
downloadUrl = testUrl;
}
}
getDownloadURL.downloadUrl = downloadUrl; // Save result to speed up future calls
return downloadUrl;
}
var element_mapping = {
status: function (results) {
return results.result || "RUNNING";
},
configuration: function (results) {
for (var i = 0; i < results.actions.length; i++) {
if (results.actions[i].parameters) {
var params = results.actions[i].parameters;
for (var j = 0; j < params.length; j++) {
if (params[j].name == "CONFIG") {
try {
return Y.Base64.decode(params[j].value);
} catch (error) {
console.error("Exception decoding base64 job config: ", error);
return 'Not Base64 encoded?';
}
}
}
}
}
return '???';
},
started: function (results) {
return Y.linaro.formatDateFromTimestamp(results.timestamp);
},
finished: function (results) {
if (results.timestamp && results.duration) {
return Y.linaro.formatDateFromTimestamp(results.timestamp + results.duration);
} else {
return '';
}
},
loglink: function (results) {
return 'Parsed - Tail - Raw';
},
testresults: function (results) {
var downloadUrl = getDownloadURL(results);
Y.io(
getProxyURL() + downloadUrl + '/lava-job-info',
{
on: { success: receivedLavaJobInfo,
failure: function(id, resp) {
Y.one('#testresults').append('
Could not load lava-job-info: ' + resp.responseText);
}
}
});
return 'Loading...';
},
output: function (results) {
/* Generate a download list */
/* In case getDownloadURL fails for some reason, we always have a link
to where new builds should be.
*/
var downloadUrl = 'http://snapshots.linaro.org/android/' + rawBuildName + '/' + results.number.toString() + '/';
handleRenderedFiles('android/' + rawBuildName + '/' +
results.number.toString());
var hiddenFilesPatterns = ['HOWTO_', 'BUILD-INFO', 'EULA', 'textile'];
Y.one('#all-output a').setAttribute('href', downloadUrl);
Y.one('#all-output a').removeClass('hidden');
Y.one('#all-output span').addClass('hidden');
downloadUrl = getDownloadURL(results);
/* Decision made about downloadUrl. Now use it. We test to see if we
can find manifest.txt, which is a list of all files available for
download. If we can't find it we try using results.artifacts.
If that fails, we display a message saying that we can't provide
a downloads list.
*/
var io_result = Y.io(
getProxyURL() + downloadUrl + '/MANIFEST',
{
sync: true
});
if (io_result.status == 200)
{
var manifest = io_result.responseText.split("\n");
// If we get webpage instead of plain text, it's likely because
// this is restricted build.
if (manifest.indexOf('') >= 0)
{
return;
}
var listNode = Y.Node.create('');
for (var i = 0; i < manifest.length; i++)
{
if (!manifest[i].match(/\S/) || patternInName(manifest[i], hiddenFilesPatterns))
{
continue;
}
var itemNode = Y.Node.create('');
itemNode.one('a').setContent(manifest[i]);
itemNode.one('a').setAttribute('href', downloadUrl + manifest[i]);
listNode.appendChild(itemNode);
}
return listNode;
}
else if (results.artifacts.length)
{
var listNode = Y.Node.create('');
for (var i = 0; i < results.artifacts.length; i++) {
var artifact = results.artifacts[i];
if (patternInName(artifact.fileName, hiddenFilesPatterns))
{
continue;
}
var itemNode = Y.Node.create('');
itemNode.one('a').setContent(artifact.displayPath);
// Strip static '/build/out' from relativePath
itemNode.one('a').setAttribute('href', downloadUrl + artifact.relativePath.substring(10));
listNode.appendChild(itemNode);
}
return listNode;
}
else
{
return Y.Node.create('Unable to find downloads list.
')
}
}
};
function displayBuild (results) {
results.buildUrl = jobUrl + '/' + results.number.toString();
for (var id in element_mapping) {
var val = element_mapping[id](results);
var targetNode = Y.one('#' + id);
if (Y.Lang.isString(val)) {
targetNode.setContent(val);
} else {
targetNode.get('childNodes').remove();
targetNode.appendChild(val);
}
}
Y.one("#olderlinks").get('childNodes').remove();
Y.one("#olderlinks").appendChild(olderlinks(results.number));
Y.one("#buildnumber").setContent(results.number);
}
function olderlinks (curNumber) {
var resultNode = Y.Node.create('');
for (var i = 0; i < allBuilds.length; i++) {
var node = Y.Node.create('');
var num = allBuilds[i].number;
node.setAttribute('id', 'oldlink-' + i.toString());
if (num != curNumber) {
var linkNode = Y.Node.create('');
linkNode.setAttribute('href', '#build=' + num.toString());
linkNode.setContent(num.toString());
node.appendChild(linkNode);
} else {
node.append('' + curNumber.toString() + '');
}
resultNode.append(node);
resultNode.append(document.createTextNode(' '));
}
return resultNode;
}
var buildConfig;
function refreshFromScratch() {
Y.io(
'../../../api/is-daily?build=' + rawBuildName,
{
on: {
success: function (e, response) {
var result = Y.JSON.parse(response.responseText);
if (result) {
Y.one('#not-built-daily').addClass('hidden');
} else {
Y.one('#not-built-daily').removeClass('hidden');
}
Y.one('#built-daily-details').removeClass('hidden');
}
}
}
);
Y.io(
jobUrl + "/api/json?tree=" + treeExp,
{
on: {
success: function (e, response) {
var job = Y.JSON.parse(response.responseText);
Y.one("#job-description").setContent(job.description);
Y.all('.build-found').removeClass('hidden');
if (job.actions.length > 0){
Y.Array.each(
job.actions[0].parameterDefinitions,
function (defn) {
if (defn.name == "CONFIG") {
try {
buildConfig = Y.Base64.decode(defn.defaultParameterValue.value);
} catch (error) {
buildConfig = '';
}
}
});
}
if (job.queueItem) {
Y.one('#queue-details').removeClass('hidden');
var queueConfig;
var encodedConfig = job.queueItem.params.replace(/^\s*CONFIG='?(\S*)'?$/,'$1');
try {
queueConfig= Y.Base64.decode(encodedConfig);
} catch (error) {
queueConfig = 'Not base64 encoded?';
}
Y.one("#queued-configuration").setContent(queueConfig);
}
if (history.get('editing')) { beginEditing(); }
if (job.builds.length) {
Y.one("#build-details").setStyle("display", "block");
allBuilds = job.builds;
if (fetchLatest) {
displayBuild(job.lastBuild);
} else {
displayJobByNumber(buildNumber);
}
} else {
Y.one("#no-build").setStyle("display", "block");
}
},
failure: function (e) {
Y.all('.build-found').addClass('hidden');
Y.one("#build-not-found").setStyle("display", "block");
}
}
}
);
}
refreshFromScratch();
function handleRenderedFiles(pathUrl) {
var rendered_files = new Object();
var howto_index = 0;
$.ajax({
url: '/api/get-textile-files-request?path=' + pathUrl,
success: function(data) {
if (data != 'null') {
data = $.parseJSON(data);
for (var key in data) {
rendered_files[key] = data[key];
}
}
},
async: false,
});
refresh_tabs(rendered_files);
}
function refresh_tabs(rendered_files) {
var count = 1;
// Remove all tabs and tab titles.
while ($("#tabs-" + count).length != 0) {
$("#tabs-" + count).remove();
count ++;
}
var count = 1;
while ($("#tabs-title-" + count).length != 0) {
$("#tabs-title-" + count).remove();
count ++;
}
try {
$("#tabs").tabs("destroy");
} catch (err) {
// Ignore. Div is not tab object yet.
}
// Add new tabs.
if (Object.keys(rendered_files).length > 0) {
var count = 1;
var li_class = "ui-state-default ui-corner-top " +
"ui-tabs-selected ui-state-active";
for (var title in rendered_files) {
if (count > 1) {
li_class = "ui-state-default ui-corner-top";
}
$("#tabs-titles").append(
'' +
'' +
title + '');
$("#tabs").append("" +
rendered_files[title] + "
");
count++;
}
$("#tabs").tabs("refresh");
$("#tabs").tabs();
}
}
function displayJobByNumber(num) {
Y.io(
jobUrl + "/" + num.toString() + "/api/json?tree=" + jobData,
{
on: {
success: function (e, response) {
displayBuild(Y.JSON.parse(response.responseText));
},
failure: function (e) {
Y.all('.build-found').addClass('hidden');
Y.one("#build-not-found").setStyle("display", "block");
}
}
}
);
}
Y.io.header('X-CSRFToken', Y.Cookie.get("csrftoken"));
Y.on(
'click',
function (e) {
var buildNow = Y.one("#build-now");
buildNow.set('disabled', true);
Y.io(
'../../../api/build-now',
{
method: "POST",
data: {
build: rawBuildName
},
on: {
failure: function (e, response) {
Y.linaro.reportError(response.responseText, {code:response.status});
},
complete: function (e, response) {
buildNow.set('disabled', false);
},
success: function (e, response) {
buildNumber = history.get('build');
fetchLatest = (buildNumber === undefined);
refreshFromScratch();
}
}
}
);
},
'#build-now');
Y.on(
'click',
function (e) {
var isDailyCurrently = Y.one('#not-built-daily').hasClass('hidden');
var toggleDaily = Y.one('#toggle-daily');
toggleDaily.set('disabled', true);
Y.io(
'../../../api/set-daily',
{
method: "POST",
data: {
build: rawBuildName,
daily: !isDailyCurrently
},
on: {
failure: function (e, response) {
Y.linaro.reportError(response.statusText);
},
complete: function (e, response) {
toggleDaily.set('disabled', false);
},
success: function (e, response) {
refreshFromScratch();
}
}
}
);
},
'#toggle-daily');
var reallyDeleteOverlay = Y.linaro.makeYesNoOverlay(
'#really-delete',
{
yes: function () {
Y.io(
'../../../api/delete',
{
method: "POST",
data: {
build: rawBuildName
},
on: {
failure: function (e, response) {
Y.linaro.reportError(response.statusText);
},
complete: function (e, response) {
reallyDeleteOverlay.hide();
},
success: function (e, response) {
// could go back to official or mine or all as appropriate here
window.location.hash = '';
window.location.pathname += '../../..';
}
}
}
);
}
}
);
Y.on(
'click',
function (e) {
reallyDeleteOverlay.show();
},
"#delete");
var editOverlay = Y.linaro.makeYesNoOverlay(
"#edit-overlay",
{
width: '80ex',
height: '40ex',
yes: function () {
var newText = editOverlay.get('srcNode').one('textarea').get('value');
Y.io(
'../../../api/edit',
{
method: "POST",
data: {
build: rawBuildName,
newText: newText
},
on: {
failure: function (e, response) {
console.log("Job POST error: " + response);
Y.linaro.reportError(response.statusText);
},
complete: function (e, response) {
editOverlay.hide();
},
success: function (e, response) {
buildConfig = newText;
}
}
}
);
}
}
);
editOverlay.on(
'visibleChange',
function (e) {
if (e.newVal) {
history.addValue('editing', 1);
} else {
history.addValue('editing', null);
}
}
);
function beginEditing () {
var textarea = editOverlay.getStdModNode(Y.WidgetStdMod.BODY).one('textarea');
textarea.set('value', buildConfig);
textarea.setStyles(
{ // XXX hand chosen fudge factors here...
width: textarea.ancestor().get('offsetWidth') - 28,
height: textarea.ancestor().get('offsetHeight') - 28
});
editOverlay.show();
}
Y.on('click', beginEditing, "#edit");
}
);