Merge "Factor out common code between cli utilities"
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index ef6259c..21d3bae 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -476,6 +476,10 @@
A boolean value (``true`` or ``false``) that indicates whether the change
must be open or closed in order to be enqueued.
+ **current-patchset**
+ A boolean value (``true`` or ``false``) that indicates whether the change
+ must be the current patchset in order to be enqueued.
+
**status**
A string value that corresponds with the status of the change
reported by the trigger. For example, when using the Gerrit
diff --git a/etc/status/public_html/index.html b/etc/status/public_html/index.html
index aac5024..8884069 100644
--- a/etc/status/public_html/index.html
+++ b/etc/status/public_html/index.html
@@ -21,64 +21,7 @@
<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-change {
- margin-bottom: 10px;
- }
-
- .zuul-change-id {
- float: right;
- }
-
- .zuul-job-result {
- float: right;
- width: 70px;
- height: 15px;
- margin: 2px 0 0 0;
- }
-
- .zuul-change-total-result {
- height: 10px;
- width: 100px;
- margin: 5px 0 0 0;
- }
-
- .zuul-spinner,
- .zuul-spinner:hover {
- opacity: 0;
- transition: opacity 0.5s ease-out;
- cursor: default;
- pointer-events: none;
- }
-
- .zuul-spinner-on,
- .zuul-spinner-on:hover {
- opacity: 1;
- transition-duration: 0.2s;
- cursor: progress;
- }
-
- .zuul-change-cell {
- padding-left: 5px;
- }
-
- .zuul-change-job {
- padding: 2px 8px;
- }
-
- .zuul-job-name {
- font-size: small;
- }
-
- .zuul-non-voting-desc {
- font-size: smaller;
- }
-
- .zuul-patchset-header {
- font-size: small;
- padding: 8px 12px;
- }
- </style>
+ <link rel="stylesheet" href="styles/zuul.css" />
</head>
<body>
<div class="container">
diff --git a/etc/status/public_html/styles/zuul.css b/etc/status/public_html/styles/zuul.css
new file mode 100644
index 0000000..e833f4b
--- /dev/null
+++ b/etc/status/public_html/styles/zuul.css
@@ -0,0 +1,56 @@
+.zuul-change {
+ margin-bottom: 10px;
+}
+
+.zuul-change-id {
+ float: right;
+}
+
+.zuul-job-result {
+ float: right;
+ width: 70px;
+ height: 15px;
+ margin: 2px 0 0 0;
+}
+
+.zuul-change-total-result {
+ height: 10px;
+ width: 100px;
+ margin: 5px 0 0 0;
+}
+
+.zuul-spinner,
+.zuul-spinner:hover {
+ opacity: 0;
+ transition: opacity 0.5s ease-out;
+ cursor: default;
+ pointer-events: none;
+}
+
+.zuul-spinner-on,
+.zuul-spinner-on:hover {
+ opacity: 1;
+ transition-duration: 0.2s;
+ cursor: progress;
+}
+
+.zuul-change-cell {
+ padding-left: 5px;
+}
+
+.zuul-change-job {
+ padding: 2px 8px;
+}
+
+.zuul-job-name {
+ font-size: small;
+}
+
+.zuul-non-voting-desc {
+ font-size: smaller;
+}
+
+.zuul-patchset-header {
+ font-size: small;
+ padding: 8px 12px;
+}
\ No newline at end of file
diff --git a/tests/fixtures/layout-current-patchset.yaml b/tests/fixtures/layout-current-patchset.yaml
new file mode 100644
index 0000000..dc8f768
--- /dev/null
+++ b/tests/fixtures/layout-current-patchset.yaml
@@ -0,0 +1,24 @@
+includes:
+ - python-file: custom_functions.py
+
+pipelines:
+ - name: check
+ manager: IndependentPipelineManager
+ require:
+ current-patchset: True
+ trigger:
+ gerrit:
+ - event: patchset-created
+ - event: comment-added
+ success:
+ gerrit:
+ verified: 1
+ failure:
+ gerrit:
+ verified: -1
+
+projects:
+ - name: org/project
+ merge-mode: cherry-pick
+ check:
+ - project-check
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index f66c2fe..f5070bb 100755
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -132,6 +132,7 @@
self.upstream_root = upstream_root
self.addPatchset()
self.data['submitRecords'] = self.getSubmitRecords()
+ self.open = True
def add_fake_change_to_repo(self, msg, fn, large):
path = os.path.join(self.upstream_root, self.project)
@@ -221,6 +222,23 @@
"reason": ""}
return event
+ def getChangeCommentEvent(self, patchset):
+ event = {"type": "comment-added",
+ "change": {"project": self.project,
+ "branch": self.branch,
+ "id": "I5459869c07352a31bfb1e7a8cac379cabfcb25af",
+ "number": str(self.number),
+ "subject": self.subject,
+ "owner": {"name": "User Name"},
+ "url": "https://hostname/3"},
+ "patchSet": self.patchsets[patchset - 1],
+ "author": {"name": "User Name"},
+ "approvals": [{"type": "Code-Review",
+ "description": "Code-Review",
+ "value": "0"}],
+ "comment": "This is a comment"}
+ return event
+
def addApproval(self, category, value, username='jenkins',
granted_on=None):
if not granted_on:
@@ -4063,3 +4081,35 @@
self.getJobFromHistory('experimental-project-test').result,
'SUCCESS')
self.assertEqual(A.reported, 1)
+
+ def test_old_patchset_doesnt_trigger(self):
+ "Test that jobs never run against old patchsets"
+ self.config.set('zuul', 'layout_config',
+ 'tests/fixtures/layout-current-patchset.yaml')
+ self.sched.reconfigure(self.config)
+ self.registerJobs()
+ # Create two patchsets and let their tests settle out. Then
+ # comment on first patchset and check that no additional
+ # jobs are run.
+ A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+ # Added because the layout file really wants an approval but this
+ # doesn't match anyways.
+ self.fake_gerrit.addEvent(A.addApproval('CRVW', 1))
+ self.waitUntilSettled()
+ A.addPatchset()
+ self.fake_gerrit.addEvent(A.addApproval('CRVW', 1))
+ self.waitUntilSettled()
+
+ old_history_count = len(self.history)
+ self.assertEqual(old_history_count, 2) # one job for each ps
+ self.fake_gerrit.addEvent(A.getChangeCommentEvent(1))
+ self.waitUntilSettled()
+
+ # Assert no new jobs ran after event for old patchset.
+ self.assertEqual(len(self.history), old_history_count)
+
+ # The last thing we did was add an event for a change then do
+ # nothing with a pipeline, so it will be in the cache;
+ # clean it up so it does not fail the test.
+ for pipeline in self.sched.layout.pipelines.values():
+ pipeline.trigger.maintainCache([])
diff --git a/zuul/layoutvalidator.py b/zuul/layoutvalidator.py
index 3e0a0ab..9a448a3 100644
--- a/zuul/layoutvalidator.py
+++ b/zuul/layoutvalidator.py
@@ -71,6 +71,7 @@
require = {'approval': toList(require_approval),
'open': bool,
+ 'current-patchset': bool,
'status': toList(str)}
window = v.All(int, v.Range(min=0))
diff --git a/zuul/model.py b/zuul/model.py
index c5c5c7d..39d004d 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -1158,8 +1158,10 @@
class ChangeishFilter(object):
- def __init__(self, open=None, statuses=[], approvals=[]):
+ def __init__(self, open=None, current_patchset=None,
+ statuses=[], approvals=[]):
self.open = open
+ self.current_patchset = current_patchset
self.statuses = statuses
self.approvals = approvals
@@ -1176,10 +1178,12 @@
if self.open is not None:
ret += ' open: %s' % self.open
+ if self.current_patchset is not None:
+ ret += ' current-patchset: %s' % self.current_patchset
if self.statuses:
ret += ' statuses: %s' % ', '.join(self.statuses)
if self.approvals:
- ret += ' approvals: %s' % ', '.join(str(self.approvals))
+ ret += ' approvals: %s' % str(self.approvals)
ret += '>'
return ret
@@ -1189,6 +1193,10 @@
if self.open != change.open:
return False
+ if self.current_patchset is not None:
+ if self.current_patchset != change.is_current_patchset:
+ return False
+
if self.statuses:
if change.status not in self.statuses:
return False
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 55b1624..a7160c7 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -276,9 +276,11 @@
if 'require' in conf_pipeline:
require = conf_pipeline['require']
- f = ChangeishFilter(open=require.get('open'),
- statuses=toList(require.get('status')),
- approvals=toList(require.get('approval')))
+ f = ChangeishFilter(
+ open=require.get('open'),
+ current_patchset=require.get('current-patchset'),
+ statuses=toList(require.get('status')),
+ approvals=toList(require.get('approval')))
manager.changeish_filters.append(f)
# TODO: move this into triggers (may require pluggable