Zuul status page: Redesign and fix bugs

status.html:
- Added eventqueue-length status (was already present in production
  but not committed here yet).
- HTML5 markup.
- Remove unused jquery-visibility.min.js and jquery-graphite.js.

status.js:
- Clean up (various js best practices and consistent coding style)
- Use empty() instead of html('').
- Use text() for text instead of html().
  html() will trigger the parser where text will simply create
  a text node with the string literal, much faster, safer and
  semantically correct.
- Fix implied global variable leak 'result'.
- Fix reference error that crashes/freezes the page
  Property data.trigger_event_queue and data.trigger_event_queue
  can be undefined, in which case data.trigger_event_queue.length
  causes an uncaught TypeError to be thrown.
- Use a closure instead of polluting global scope.
- Rewrite object oriented.
- Added 'demo' feature for easy local testing.

Downstream commits at
https://gerrit.wikimedia.org/r/#/q/project:integration/docroot+topic:zuul-js+branch:master+owner:Krinkle+is:merged,n,z

Change-Id: Iddd4e2787f2e2eb27bf428f733fbb8b4a9d162d5
Reviewed-on: https://review.openstack.org/26416
Reviewed-by: James E. Blair <corvus@inaugust.com>
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
This commit is contained in:
Timo Tijhof 2013-04-09 01:32:29 +02:00 committed by Jenkins
parent 80edd5a8fa
commit 51516cd2f3
9 changed files with 907 additions and 239 deletions

View File

@ -1,87 +0,0 @@
<!--
Copyright 2013 OpenStack Foundation
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
lang="en">
<head>
<style type="text/css">
#pipeline-container {
margin: 0 auto;
}
.change {
border: 1px solid;
margin-top: 10px;
padding: 2px;
}
.change > .header {
background: #cccccc;
color: black;
margin: -2px -2px 2px -2px;
padding: 4px;
}
.change > .header > .changeid {
float: right;
}
.job {
display: block;
}
.pipeline {
float: left;
width: 25em;
padding: 4px;
}
.pipeline > .header {
background: #0000cc;
color: white;
}
.arrow {
text-align: center;
font-size: 16pt;
line-height: 1.0;
}
.result_success {
color: #007f00;
}
.result_failure {
color: #cf2f19;
}
.result_unstable {
color: #e39f00;
}
</style>
<title>Zuul Status</title>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery-visibility.min.js"></script>
<script type="text/javascript" src="jquery-graphite.js"></script>
<script type="text/javascript" src="status.js"></script>
</head>
<body>
<h1> Zuul Status </h1>
<div id="message-container">
<p id="message"/>
</div>
<div id="pipeline-container">
</div>
</body>
</html>

View File

@ -1,152 +0,0 @@
// Copyright 2012 OpenStack Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
window.zuul_enable_status_updates = true;
function format_pipeline(data) {
var html = '<div class="pipeline"><h3 class="subhead">'+
data['name']+'</h3>';
if (data['description'] != null) {
html += '<p>'+data['description']+'</p>';
}
$.each(data['change_queues'], function(change_queue_i, change_queue) {
$.each(change_queue['heads'], function(head_i, head) {
if (data['change_queues'].length > 1 && head_i == 0) {
html += '<div> Change queue: ';
var name = change_queue['name'];
html += '<a title="' + name + '">';
if (name.length > 32) {
name = name.substr(0,32) + '...';
}
html += name + '</a></div>'
}
$.each(head, function(change_i, change) {
if (change_i > 0) {
html += '<div class="arrow">&uarr;</div>'
}
html += format_change(change);
});
});
});
html += '</div>';
return html;
}
function format_change(change) {
var html = '<div class="change"><div class="header">';
html += '<span class="project">'+change['project']+'</span>';
var id = change['id'];
var url = change['url'];
if (id.length == 40) {
id = id.substr(0,7);
}
html += '<span class="changeid">';
if (url != null) {
html += '<a href="'+url+'">';
}
html += id;
if (url != null) {
html += '</a>';
}
html += '</span></div><div class="jobs">';
$.each(change['jobs'], function(i, job) {
result = job['result'];
var result_class = "result";
if (result == null) {
if (job['url'] != null) {
result = 'in progress';
} else {
result = 'queued';
}
} else if (result == 'SUCCESS') {
result_class += " result_success";
} else if (result == 'FAILURE') {
result_class += " result_failure";
} else if (result == 'LOST') {
result_class += " result_unstable";
} else if (result == 'UNSTABLE') {
result_class += " result_unstable";
}
html += '<span class="job">';
if (job['url'] != null) {
html += '<a href="'+job['url']+'">';
}
html += job['name'];
if (job['url'] != null) {
html += '</a>';
}
html += ': <span class="'+result_class+'">'+result+'</span>';
if (job['voting'] == false) {
html += ' (non-voting)';
}
html += '</span>';
});
html += '</div></div>';
return html;
}
function update_timeout() {
if (!window.zuul_enable_status_updates) {
setTimeout(update_timeout, 5000);
return;
}
update();
setTimeout(update_timeout, 5000);
}
function update() {
var html = '';
$.getJSON('/status.json', function(data) {
if ('message' in data) {
$("#message-container").attr('class', 'topMessage');
$("#message").html(data['message']);
} else {
$("#message-container").removeClass('topMessage');
$("#message").html('');
}
html += '<br style="clear:both"/>';
$.each(data['pipelines'], function(i, pipeline) {
html = html + format_pipeline(pipeline);
});
html += '<br style="clear:both"/>';
$("#pipeline-container").html(html);
});
}
$(function() {
update_timeout();
$(document).on({
'show.visibility': function() {
window.zuul_enable_status_updates = true;
update();
},
'hide.visibility': function() {
window.zuul_enable_status_updates = false;
}
});
});

3
etc/status/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
public_html/jquery.min.js
public_html/jquery-visibility.min.js
public_html/bootstrap

27
etc/status/README.rst Normal file
View File

@ -0,0 +1,27 @@
Zuul Status
====
Zuul Status is a web portal for a Zuul server.
Set up
------------
The markup generated by the javascript is fairly generic so it should be easy
to drop into an existing portal. All it needs is
``<div id="id="zuul-container"></div>``.
Having said that, the markup is optimised for Twitter Bootstrap, though it in
no way depends on Boostrap and any element using a bootstrap class has a
``zuul-`` prefixed class alongside it.
The script depends on jQuery (tested with version 1.8 and 1.9).
The script optimises updates by stopping when the page is not visible.
This is done by listerning to ``show`` and ``hide`` events emitted by the
Page Visibility plugin for jQuery. If you don't want to load this plugin you
can undo undo this optimisation by removing the 9 lines using this on the
bottom of ``app.js``
To automatically fetch the latest versions of jQuery, the Page Visibility
plugin and Twitter Boostrap, run the ``fetch-dependencies.sh`` script.
The default ``index.html`` references these.

View File

@ -0,0 +1,9 @@
#!/bin/bash
BASE_DIR=$(cd $(dirname $0); pwd)
echo "Destination: $BASE_DIR/public_html"
echo "Fetching jquery.min.js..."
curl --silent http://code.jquery.com/jquery.min.js > $BASE_DIR/public_html/jquery.min.js
echo "Fetching jquery-visibility.min.js..."
curl --silent https://raw.github.com/mathiasbynens/jquery-visibility/master/jquery-visibility.min.js > $BASE_DIR/public_html/jquery-visibility.min.js
echo "Fetching bootstrap..."
curl --silent http://twitter.github.io/bootstrap/assets/bootstrap.zip > bootstrap.zip && unzip -q -o bootstrap.zip -d $BASE_DIR/public_html/ && rm bootstrap.zip

View File

@ -0,0 +1,241 @@
// Client script for Zuul status page
//
// Copyright 2012 OpenStack Foundation
// Copyright 2013 Timo Tijhof
// Copyright 2013 Wikimedia Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
(function ($) {
var $container, $msg, $msgWrap, $indicator, $queueInfo, $queueEventsNum, $queueResultsNum, $pipelines,
prevHtml, xhr, zuul, $jq,
demo = location.search.match(/[?&]demo=([^?&]*)/),
source = demo ?
'./status-' + (demo[1] || 'basic') + '.json-sample' :
'/zuul/status.json';
zuul = {
enabled: true,
schedule: function () {
if (!zuul.enabled) {
setTimeout(zuul.schedule, 5000);
return;
}
zuul.update().complete(function () {
setTimeout(zuul.schedule, 5000);
});
},
/** @return {jQuery.Promise} */
update: function () {
// Cancel the previous update if it hasn't completed yet.
if (xhr) {
xhr.abort();
}
zuul.emit('update-start');
xhr = $.ajax({
url: source,
dataType: 'json',
cache: false
})
.done(function (data) {
var html = '';
data = data || {};
if ('message' in data) {
$msg.html(data.message);
$msgWrap.removeClass('zuul-msg-wrap-off');
} else {
$msg.empty();
$msgWrap.addClass('zuul-msg-wrap-off');
}
$.each(data.pipelines, function (i, pipeline) {
html += zuul.format.pipeline(pipeline);
});
// Only re-parse the DOM if we have to
if (html !== prevHtml) {
prevHtml = html;
$pipelines.html(html);
}
$queueEventsNum.text(
data.trigger_event_queue ? data.trigger_event_queue.length : '0'
);
$queueResultsNum.text(
data.result_event_queue ? data.result_event_queue.length : '0'
);
})
.fail(function (err, jqXHR, errMsg) {
$msg.text(source + ': ' + errMsg).show();
$msgWrap.removeClass('zuul-msg-wrap-off');
})
.complete(function () {
xhr = undefined;
zuul.emit('update-end');
});
return xhr;
},
format: {
change: function (change) {
var html = '<div class="well well-small zuul-change"><ul class="nav nav-list">',
id = change.id,
url = change.url;
html += '<li class="nav-header">' + change.project;
if (id.length === 40) {
id = id.substr(0, 7);
}
html += ' <span class="zuul-change-id">';
if (url !== null) {
html += '<a href="' + url + '">';
}
html += id;
if (url !== null) {
html += '</a>';
}
html += '</span></li>';
$.each(change.jobs, function (i, job) {
var result = job.result ? job.result.toLowerCase() : null,
resultClass = 'zuul-result label';
if (result === null) {
result = job.url ? 'in progress' : 'queued';
}
switch (result) {
case 'success':
resultClass += ' label-success';
break;
case 'failure':
resultClass += ' label-important';
break;
case 'lost':
case 'unstable':
resultClass += ' label-warning';
break;
}
html += '<li class="zuul-change-job">';
html += job.url !== null ?
'<a href="' + job.url + '" class="zuul-change-job-link">' :
'<span class="zuul-change-job-link">';
html += job.name;
html += ' <span class="' + resultClass + '">' + result + '</span>';
if (job.voting === false) {
html += ' <span class="muted">(non-voting)</span>';
}
html += job.url !== null ? '</a>' : '</span>';
html += '</li>';
});
html += '</ul></div>';
return html;
},
pipeline: function (pipeline) {
var html = '<div class="zuul-pipeline span4"><h3>' +
pipeline.name + '</h3>';
if (typeof pipeline.description === 'string') {
html += '<p><small>' + pipeline.description + '</small></p>';
}
$.each(pipeline.change_queues, function (queueNum, changeQueue) {
$.each(changeQueue.heads, function (headNum, changes) {
if (pipeline.change_queues.length > 1 && headNum === 0) {
var name = changeQueue.name;
html += '<p>Queue: <abbr title="' + name + '">';
if (name.length > 32) {
name = name.substr(0, 32) + '...';
}
html += name + '</abbr></p>';
}
$.each(changes, function (changeNum, change) {
// If there are multiple changes in the same head it means they're connected
if (changeNum > 0) {
html += '<div class="zuul-change-arrow">&uarr;</div>';
}
html += zuul.format.change(change);
});
});
});
html += '</div>';
return html;
}
},
emit: function () {
$jq.trigger.apply($jq, arguments);
return this;
},
on: function () {
$jq.on.apply($jq, arguments);
return this;
},
one: function () {
$jq.one.apply($jq, arguments);
return this;
}
};
$jq = $(zuul);
$jq.on('update-start', function () {
$container.addClass('zuul-container-loading');
$indicator.addClass('zuul-spinner-on');
});
$jq.on('update-end', function () {
$container.removeClass('zuul-container-loading');
setTimeout(function () {
$indicator.removeClass('zuul-spinner-on');
}, 550);
});
$jq.one('update-end', function () {
// Do this asynchronous so that if the first update adds a message, it will not animate
// while we fade in the content. Instead it simply appears with the rest of the content.
setTimeout(function () {
$container.addClass('zuul-container-ready'); // Fades in the content
});
});
$(function ($) {
$msg = $('<div class="zuul-msg alert alert-error"></div>');
$msgWrap = $msg.wrap('<div class="zuul-msg-wrap zuul-msg-wrap-off"></div>').parent();
$indicator = $('<span class="btn pull-right zuul-spinner">updating <i class="icon-refresh"></i></span>');
$queueInfo = $('<p>Queue lengths: <span>0</span> events, <span>0</span> results.</p>');
$queueEventsNum = $queueInfo.find('span').eq(0);
$queueResultsNum = $queueEventsNum.next();
$pipelines = $('<div class="row"></div>');
$container = $('#zuul-container').append($msgWrap, $indicator, $queueInfo, $pipelines);
zuul.schedule();
$(document).on({
'show.visibility': function () {
zuul.enabled = true;
zuul.update();
},
'hide.visibility': function () {
zuul.enabled = false;
}
});
});
}(jQuery));

View File

@ -0,0 +1,141 @@
<!--
Copyright 2013 OpenStack Foundation
Copyright 2013 Timo Tijhof
Copyright 2013 Wikimedia Foundation
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
-->
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<title>Zuul Status</title>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="bootstrap/css/bootstrap-responsive.min.css">
<style>
.zuul-container {
transition-property: opacity, background-color;
transition-duration: 1s;
transition-timing-function: ease-in-out;
clear: both;
opacity: 0;
cursor: progress;
min-height: 400px;
background-color: #f8ffaa;
}
.zuul-container-ready {
opacity: 1;
cursor: auto;
background-color: #fff;
}
.zuul-spinner,
.zuul-spinner:hover {
opacity: 0;
transition: opacity 3s ease-out;
cursor: default;
pointer-events: none;
}
.zuul-spinner-on,
.zuul-spinner-on:hover {
opacity: 1;
transition-duration: 0.4s;
cursor: progress;
}
.zuul-change-arrow {
text-align: center;
font-size: 16pt;
line-height: 1.0;
}
.zuul-change-id {
text-transform: none;
}
.zuul-change-job a {
overflow: auto;
}
.zuul-result {
text-shadow: none;
font-weight: normal;
background-color: #E9E9E9;
color: #555;
}
.zuul-result.label-success {
background-color: #CDF0CD;
color: #468847;
}
.zuul-result.label-important {
background-color: #F1DBDA;
color: #B94A48;
}
.zuul-result.label-warning {
background-color: #F3E6D4;
color: #F89406;
}
.zuul-msg-wrap {
max-height: 150px;
overflow: hidden;
}
.zuul-container-ready .zuul-msg-wrap {
transition: max-height 1s ease-in;
}
.zuul-msg-wrap-off {
max-height: 0;
}
.zuul-msg p {
margin: 0;
}
/**
* Styles for Bootstrap only
* (remove when not using Bootstrap)
*/
.zuul-change-id {
float: right;
}
.zuul-change-job-link {
overflow: auto;
display: block;
}
.zuul-result {
float: right;
}
</style>
</head>
<body>
<div class="container">
<h1>Zuul Status</h1>
<p>Real-time status monitor of Zuul, the pipeline manager between Gerrit and Jenkins.</p>
<div class="zuul-container" id="zuul-container"></div>
</div>
<script src="jquery.min.js"></script>
<script src="jquery-visibility.min.js"></script>
<script src="app.js"></script>
</body>
</html>

View File

@ -0,0 +1,176 @@
{
"message": "Example error message",
"pipelines": [
{
"name": "test",
"description": "Lint and unit tests",
"change_queues": [
{
"name": "some-jobs@worker301.ci-example.org",
"heads": [
[
{
"id": "10101,1",
"url": "#!/review.example.org/r/10101",
"project": "openstack/infra/zuul",
"jobs": [
{
"name": "zuul-merge",
"url": "#!/jenkins.example.org/job/zuul-merge/201",
"result": "SUCCESS",
"voting": true
},
{
"name": "zuul-lint",
"url": "#!/jenkins.example.org/job/zuul-lint/201",
"result": "SUCCESS",
"voting": true
},
{
"name": "zuul-test",
"url": "#!/jenkins.example.org/job/zuul-test/201",
"result": "SUCCESS",
"voting": true
}
]
}
],
[
{
"id": "10103,1",
"url": "#!/review.example.org/r/10103",
"project": "google/gerrit",
"jobs": [
{
"name": "gerrit-merge",
"url": "#!/jenkins.example.org/job/gerrit-merge/203",
"result": "SUCCESS",
"voting": false
}
]
}
]
]
},
{
"name": "other-jobs@worker301.ci-example.org",
"heads": [
[
{
"id": "10102,1",
"url": "#!/review.example.org/r/10102",
"project": "google/gerrit",
"jobs": [
{
"name": "gerrit-merge",
"url": "#!/jenkins.example.org/job/gerrit-merge/202",
"result": "UNSTABLE",
"voting": false
}
]
}
],
[
{
"id": "10104,1",
"url": "#!/review.example.org/r/10104",
"project": "openstack/infra/zuul",
"jobs": [
{
"name": "zuul-merge",
"url": "#!/jenkins.example.org/job/zuul-merge/204",
"result": "SUCCESS",
"voting": true
},
{
"name": "zuul-lint",
"url": "#!/jenkins.example.org/job/zuul-lint/204",
"result": "FAILURE",
"voting": true
},
{
"name": "zuul-test",
"url": "#!/jenkins.example.org/job/zuul-test/204",
"result": null,
"voting": true
}
]
}
]
]
}
]
},
{
"name": "gate-and-submit",
"change_queues": []
},
{
"name": "postmerge",
"change_queues": [
{
"name": "some-jobs@worker301.ci-example.org",
"heads": [
[
{
"id": "7f1d65cb0f663c907698f915da01c008c7ef4748",
"url": "#!/review.example.org/r/10100",
"project": "openstack/infra/zuul",
"jobs": [
{
"name": "zuul-lint",
"url": "#!/jenkins.example.org/job/zuul-lint/200",
"result": "SUCCESS",
"voting": true
},
{
"name": "zuul-test",
"url": "#!/jenkins.example.org/job/zuul-test/200",
"result": "FAILURE",
"voting": true
},
{
"name": "zuul-regression-python2",
"url": "#!/jenkins.example.org/job/zuul-regression-python2/200",
"result": "SUCCESS",
"voting": true
},
{
"name": "zuul-regression-python3",
"url": "#!/jenkins.example.org/job/zuul-regression-python3/200",
"result": "FAILURE",
"voting": true
},
{
"name": "zuul-performance-python2",
"url": null,
"result": null,
"voting": true
},
{
"name": "zuul-performance-python3",
"url": null,
"result": null,
"voting": true
},
{
"name": "zuul-docs-publish",
"url": null,
"result": null,
"voting": true
}
]
}
]
]
}
]
}
],
"trigger_event_queue": {
"length": 0
},
"result_event_queue": {
"length": 0
}
}

View File

@ -0,0 +1,310 @@
{
"pipelines": [
{
"name": "check",
"description": "Newly uploaded patchsets enter this pipeline to receive an initial +/-1 Verified vote from Jenkins.",
"change_queues": [
{
"heads": [],
"name": "stackforge/tripleo-image-elements"
}
]
},
{
"description": "Changes that have been approved by core developers are enqueued in order in this pipeline, and .",
"change_queues": [
{
"heads": [],
"name": "openstack-detackforge/reddwarf-integration"
},
{
"heads": [],
"name": "stackforge/moniker"
},
{
"heads": [
[
{
"url": "https://review.openstack.org/26292",
"project": "openstack/quantum",
"jobs": [
{
"url": "https://jenkins.openstack.org/job/gate-quantum-docs/5501/consoleFull",
"voting": true,
"result": "SUCCESS",
"name": "gate-quantum-docs"
},
{
"url": "https://jenkins.openstack.org/job/gate-quantum-pep8/6254/consoleFull",
"voting": true,
"result": "SUCCESS",
"name": "gate-quantum-pep8"
},
{
"url": "https://jenkins.openstack.org/job/gate-quantum-python26/5876/",
"voting": true,
"result": null,
"name": "gate-quantum-python26"
},
{
"url": "https://jenkins.openstack.org/job/gate-quantum-python27/5887/",
"voting": true,
"result": null,
"name": "gate-quantum-python27"
},
{
"url": "https://jenkins.openstack.org/job/gate-tempest-devstack-vm-quantum/17548/",
"voting": true,
"result": null,
"name": "gate-tempest-devstack-vm-quantum"
}
],
"id": "26292,1"
}
]
],
"name": "openstack-dev/devstack, openstack-infra/devstack-gate, openstack/cinder, openstack/glance, openstack/horizon, openstack/keystone, openstack/nova, openstack/python-cinderclient, openstack/python-glanceclient, openstack/python-keystoneclient, openstack/python-novaclient, openstack/python-quantumclient, openstack/quantum, openstack/swift, openstack/tempest, z/tempest"
},
{
"heads": [],
"name": "openstack/ceilometer"
},
{
"heads": [],
"name": "stackforge/puppet-openstack"
},
{
"heads": [],
"name": "stackforge/puppet-cinder"
},
{
"heads": [],
"name": "stackforge/marconi"
},
{
"heads": [],
"name": "openstack-infra/config"
},
{
"heads": [],
"name": "stackforge/tripleo-image-elements"
},
{
"heads": [],
"name": "stackforge/kwapi"
},
{
"heads": [],
"name": "stackforge/python-reddwarfclient"
},
{
"heads": [],
"name": "stackforge/python-savannaclient"
},
{
"heads": [],
"name": "stackforge/python-monikerclient"
},
{
"heads": [],
"name": "stackforge/packstack"
},
{
"heads": [],
"name": "openstack/oslo.config"
},
{
"heads": [],
"name": "openstack-infra/jenkins-job-builder"
},
{
"heads": [],
"name": "stackforge/puppet-horizon"
},
{
"heads": [],
"name": "openstack/heat-cfntools"
},
{
"heads": [],
"name": "openstack/oslo-incubator"
},
{
"heads": [],
"name": "stackforge/os-config-applier"
},
{
"heads": [],
"name": "openstack/requirements"
},
{
"heads": [],
"name": "stackforge/puppet-glance"
},
{
"heads": [],
"name": "openstack-infra/gearman-plugin"
},
{
"heads": [],
"name": "stackforge/puppet-keystone"
},
{
"heads": [],
"name": "stackforge/puppet-nova"
},
{
"heads": [],
"name": "stackforge/climate"
},
{
"heads": [],
"name": "openstack/python-swiftclient"
},
{
"heads": [],
"name": "openstack/python-ceilometerclient"
},
{
"heads": [],
"name": "openstack-infra/git-review"
},
{
"heads": [],
"name": "stackforge/bufunfa"
},
{
"heads": [],
"name": "stackforge/puppet-swift"
},
{
"heads": [],
"name": "openstack-infra/statusbot"
},
{
"heads": [],
"name": "openstack/openstack-planet"
},
{
"heads": [],
"name": "openstack/python-openstackclient"
},
{
"heads": [],
"name": "stackforge/diskimage-builder"
},
{
"heads": [],
"name": "openstack-infra/gerritlib"
},
{
"heads": [],
"name": "openstack-infra/zuul"
},
{
"heads": [],
"name": "stackforge/reddwarf"
},
{
"heads": [],
"name": "openstack-dev/hacking"
},
{
"heads": [],
"name": "openstack/python-heatclient"
},
{
"heads": [],
"name": "stackforge/python-libraclient"
},
{
"heads": [],
"name": "openstack-infra/reviewday"
},
{
"heads": [],
"name": "openstack-infra/jeepyb"
},
{
"heads": [],
"name": "openstack/heat"
},
{
"heads": [],
"name": "stackforge/libra"
},
{
"heads": [],
"name": "openstack-infra/gerrit"
},
{
"heads": [],
"name": "stackforge/healthnmon"
},
{
"heads": [],
"name": "openstack-infra/gerritbot"
},
{
"heads": [],
"name": "openstack-dev/pbr"
},
{
"heads": [],
"name": "stackforge/savanna"
},
{
"heads": [],
"name": "openstack/openstack-manuals"
}
],
"name": "gate"
},
{
"description": "This pipeline runs jobs that operate after each change is merged.",
"change_queues": [
{
"heads": [],
"name": "openstack-dev/hacking, openstack-dev/openstack-qa, openstack-dev/pbr, openstack-infra/config, openstack-infra/gearman-plugin, openstack-infra/gerrit, openstack-infra/gerritbot, openstack-infra/git-review, openstack-infra/jenkins-job-builder, openstack-infra/nose-html-output, openstack-infra/reviewday, openstack-infra/statusbot, openstack-infra/zuul, openstack/api-site, openstack/ceilometer, openstack/cinder, openstack/compute-api, openstack/glance, openstack/heat, openstack/heat-cfntools, openstack/horizon, openstack/identity-api, openstack/image-api, openstack/keystone, openstack/netconn-api, openstack/nova, openstack/object-api, openstack/openstack-manuals, openstack/oslo-incubator, openstack/oslo.config, openstack/python-ceilometerclient, openstack/python-cinderclient, openstack/python-glanceclient, openstack/python-heatclient, openstack/python-keystoneclient, openstack/python-novaclient, openstack/python-openstackclient, openstack/python-quantumclient, openstack/python-swiftclient, openstack/quantum, openstack/requirements, openstack/swift, openstack/volume-api, stackforge/bufunfa, stackforge/diskimage-builder, stackforge/moniker, stackforge/os-config-applier, stackforge/python-monikerclient, stackforge/python-savannaclient, stackforge/reddwarf, stackforge/savanna, stackforge/tripleo-image-elements"
}
],
"name": "post"
},
{
"description": "This pipeline runs jobs on projects in response to pre-release tags.",
"change_queues": [
{
"heads": [],
"name": "openstack-dev/hacking, openstack-dev/pbr, openstack-infra/gerritbot, openstack-infra/gerritlib, openstack-infra/git-review, openstack-infra/jeepyb, openstack-infra/jenkins-job-builder, openstack-infra/nose-html-output, openstack-infra/reviewday, openstack-infra/statusbot, openstack-infra/zuul, openstack/ceilometer, openstack/cinder, openstack/glance, openstack/heat, openstack/heat-cfntools, openstack/horizon, openstack/keystone, openstack/nova, openstack/oslo.config, openstack/python-ceilometerclient, openstack/python-cinderclient, openstack/python-glanceclient, openstack/python-heatclient, openstack/python-keystoneclient, openstack/python-novaclient, openstack/python-openstackclient, openstack/python-quantumclient, openstack/python-swiftclient, openstack/quantum, openstack/swift, stackforge/moniker, stackforge/python-monikerclient, stackforge/python-reddwarfclient, stackforge/python-savannaclient, stackforge/savanna"
}
],
"name": "pre-release"
},
{
"description": "When a commit is tagged as a release, this pipeline runs jobs that publish archives and documentation.",
"change_queues": [
{
"heads": [],
"name": "openstack-dev/hacking, openstack-dev/openstack-qa, openstack-dev/pbr, openstack-infra/gerritbot, openstack-infra/gerritlib, openstack-infra/git-review, openstack-infra/jeepyb, openstack-infra/jenkins-job-builder, openstack-infra/nose-html-output, openstack-infra/reviewday, openstack-infra/statusbot, openstack-infra/zuul, openstack/ceilometer, openstack/cinder, openstack/glance, openstack/heat, openstack/heat-cfntools, openstack/horizon, openstack/keystone, openstack/nova, openstack/oslo-incubator, openstack/oslo.config, openstack/python-ceilometerclient, openstack/python-cinderclient, openstack/python-glanceclient, openstack/python-heatclient, openstack/python-keystoneclient, openstack/python-novaclient, openstack/python-openstackclient, openstack/python-quantumclient, openstack/python-swiftclient, openstack/quantum, openstack/swift, stackforge/moniker, stackforge/python-monikerclient, stackforge/python-reddwarfclient, stackforge/python-savannaclient, stackforge/savanna"
}
],
"name": "release"
},
{
"description": "This pipeline is used for silently testing new jobs.",
"change_queues": [
{
"heads": [],
"name": ""
}
],
"name": "silent"
}
],
"trigger_event_queue": {
"length": 0
},
"result_event_queue": {
"length": 0
}
}