Merge "Allow merge failures to have unique reporters."
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index 24de765..7274342 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -706,6 +706,11 @@
project-pyflakes are only executed if project-merge succeeds. This
can help avoid running unnecessary jobs.
+The special job named ``noop`` is internal to Zuul and will always
+return ``SUCCESS`` immediately. This can be useful if you require
+that all changes be processed by a pipeline but a project has no jobs
+that can be run on it.
+
.. seealso:: The OpenStack Zuul configuration for a comprehensive example: https://github.com/openstack-infra/config/blob/master/modules/openstack_project/files/zuul/layout.yaml
Project Templates
diff --git a/etc/status/public_html/app.js b/etc/status/public_html/app.js
index 2f9d3b7..b4c82f8 100644
--- a/etc/status/public_html/app.js
+++ b/etc/status/public_html/app.js
@@ -17,8 +17,9 @@
// under the License.
(function ($) {
- var $container, $msg, $msgWrap, $indicator, $queueInfo, $queueEventsNum, $queueResultsNum, $pipelines,
- prevHtml, xhr, zuul, $jq,
+ var $container, $msg, $msgWrap, $indicator, $queueInfo, $queueEventsNum,
+ $queueResultsNum, $pipelines, $jq;
+ var xhr, prevHtml, zuul,
demo = location.search.match(/[?&]demo=([^?&]*)/),
source = demo ?
'./status-' + (demo[1] || 'basic') + '.json-sample' :
@@ -67,8 +68,10 @@
$('#zuul-version-span').text(data['zuul_version']);
}
if ('last_reconfigured' in data) {
- var last_reconfigured = new Date(data['last_reconfigured']);
- $('#last-reconfigured-span').text(last_reconfigured.toString());
+ var last_reconfigured =
+ new Date(data['last_reconfigured']);
+ $('#last-reconfigured-span').text(
+ last_reconfigured.toString());
}
$.each(data.pipelines, function (i, pipeline) {
@@ -82,10 +85,12 @@
}
$queueEventsNum.text(
- data.trigger_event_queue ? data.trigger_event_queue.length : '0'
+ data.trigger_event_queue ?
+ data.trigger_event_queue.length : '0'
);
$queueResultsNum.text(
- data.result_event_queue ? data.result_event_queue.length : '0'
+ data.result_event_queue ?
+ data.result_event_queue.length : '0'
);
})
.fail(function (err, jqXHR, errMsg) {
@@ -102,7 +107,8 @@
format: {
change: function (change) {
- var html = '<div class="well well-small zuul-change"><ul class="nav nav-list">',
+ var html = '<div class="well well-small zuul-change">' +
+ '<ul class="nav nav-list">',
id = change.id,
url = change.url;
@@ -140,10 +146,12 @@
}
html += '<li class="zuul-change-job">';
html += job.url !== null ?
- '<a href="' + job.url + '" class="zuul-change-job-link">' :
+ '<a href="' + job.url + '" ' +
+ 'class="zuul-change-job-link">' :
'<span class="zuul-change-job-link">';
html += job.name;
- html += ' <span class="' + resultClass + '">' + result + '</span>';
+ html += ' <span class="' + resultClass + '">' + result +
+ '</span>';
if (job.voting === false) {
html += ' <span class="muted">(non-voting)</span>';
}
@@ -159,12 +167,15 @@
var html = '<div class="zuul-pipeline span4"><h3>' +
pipeline.name + '</h3>';
if (typeof pipeline.description === 'string') {
- html += '<p><small>' + pipeline.description + '</small></p>';
+ html += '<p><small>' + pipeline.description +
+ '</small></p>';
}
- $.each(pipeline.change_queues, function (queueNum, changeQueue) {
+ $.each(pipeline.change_queues,
+ function (queueNum, changeQueue) {
$.each(changeQueue.heads, function (headNum, changes) {
- if (pipeline.change_queues.length > 1 && headNum === 0) {
+ if (pipeline.change_queues.length > 1 &&
+ headNum === 0) {
var name = changeQueue.name;
html += '<p>Queue: <abbr title="' + name + '">';
if (name.length > 32) {
@@ -173,9 +184,11 @@
html += name + '</abbr></p>';
}
$.each(changes, function (changeNum, change) {
- // If there are multiple changes in the same head it means they're connected
+ // If there are multiple changes in the same head
+ // it means they're connected
if (changeNum > 0) {
- html += '<div class="zuul-change-arrow">↑</div>';
+ html += '<div class="zuul-change-arrow">' +
+ '↑</div>';
}
html += zuul.format.change(change);
});
@@ -216,25 +229,34 @@
});
$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.
+ // 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
+ // Fade in the content
+ $container.addClass('zuul-container-ready');
});
});
$(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>');
+ $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>');
- $zuulVersion = $('<p>Zuul version: <span id="zuul-version-span"></span></p>');
- $lastReconf = $('<p>Last reconfigured: <span id="last-reconfigured-span"></span></p>');
+ $zuulVersion = $('<p>Zuul version: <span id="zuul-version-span">' +
+ '</span></p>');
+ $lastReconf = $('<p>Last reconfigured: ' +
+ '<span id="last-reconfigured-span"></span></p>');
- $container = $('#zuul-container').append($msgWrap, $indicator, $queueInfo, $pipelines, $zuulVersion, $lastReconf);
+ $container = $('#zuul-container').append($msgWrap, $indicator,
+ $queueInfo, $pipelines,
+ $zuulVersion, $lastReconf);
zuul.schedule();
diff --git a/requirements.txt b/requirements.txt
index 4e9f29c..f14441b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,5 +12,5 @@
extras
statsd>=1.0.0,<3.0
voluptuous>=0.7
-gear>=0.5.1,<1.0.0
+gear>=0.5.4,<1.0.0
apscheduler>=2.1.1,<3.0
diff --git a/tests/fixtures/layout-no-jobs.yaml b/tests/fixtures/layout-no-jobs.yaml
index ee8dc62..e860ad5 100644
--- a/tests/fixtures/layout-no-jobs.yaml
+++ b/tests/fixtures/layout-no-jobs.yaml
@@ -38,6 +38,6 @@
- name: org/project
merge-mode: cherry-pick
check:
- - noop
+ - gate-noop
gate:
- - noop
+ - gate-noop
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 496d468..351854d 100755
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -795,6 +795,7 @@
self.init_repo("org/layered-project")
self.init_repo("org/node-project")
self.init_repo("org/conflict-project")
+ self.init_repo("org/noop-project")
self.statsd = FakeStatsd()
os.environ['STATSD_HOST'] = 'localhost'
@@ -2654,6 +2655,19 @@
self.assertEqual(len(self.history), 10)
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 1)
+ def test_noop_job(self):
+ "Test that the internal noop job works"
+ A = self.fake_gerrit.addFakeChange('org/noop-project', 'master', 'A')
+ A.addApproval('CRVW', 2)
+ self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+ self.waitUntilSettled()
+
+ self.assertEqual(len(self.gearman_server.getQueue()), 0)
+ self.assertTrue(self.sched._areAllBuildsComplete())
+ self.assertEqual(len(self.history), 0)
+ self.assertEqual(A.data['status'], 'MERGED')
+ self.assertEqual(A.reported, 2)
+
def test_zuul_refs(self):
"Test that zuul refs exist and have the right changes"
self.worker.hold_jobs_in_build = True
@@ -2858,6 +2872,11 @@
def test_stuck_job_cleanup(self):
"Test that pending jobs are cleaned up if removed from layout"
+ # This job won't be registered at startup because it is not in
+ # the standard layout, but we need it to already be registerd
+ # for when we reconfigure, as that is when Zuul will attempt
+ # to run the new job.
+ self.worker.registerFunction('build:gate-noop')
self.gearman_server.hold_jobs_in_queue = True
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
A.addApproval('CRVW', 2)
@@ -2870,13 +2889,13 @@
self.sched.reconfigure(self.config)
self.waitUntilSettled()
- self.gearman_server.release('noop')
+ self.gearman_server.release('gate-noop')
self.waitUntilSettled()
self.assertEqual(len(self.gearman_server.getQueue()), 0)
self.assertTrue(self.sched._areAllBuildsComplete())
self.assertEqual(len(self.history), 1)
- self.assertEqual(self.history[0].name, 'noop')
+ self.assertEqual(self.history[0].name, 'gate-noop')
self.assertEqual(self.history[0].result, 'SUCCESS')
def test_file_jobs(self):
diff --git a/zuul/cmd/server.py b/zuul/cmd/server.py
index 79a2538..13e6283 100755
--- a/zuul/cmd/server.py
+++ b/zuul/cmd/server.py
@@ -137,6 +137,7 @@
if not os.path.exists(path):
raise Exception("Unable to find job list: %s" % path)
jobs = set()
+ jobs.add('noop')
for line in open(path):
v = line.strip()
if v:
diff --git a/zuul/launcher/gearman.py b/zuul/launcher/gearman.py
index 081db2b..9ab1f61 100644
--- a/zuul/launcher/gearman.py
+++ b/zuul/launcher/gearman.py
@@ -290,6 +290,11 @@
build = Build(job, uuid)
build.parameters = params
+ if job.name == 'noop':
+ build.result = 'SUCCESS'
+ self.sched.onBuildCompleted(build)
+ return build
+
gearman_job = gear.Job(name, json.dumps(params),
unique=uuid)
build.__gearman_job = gearman_job