Skip to content

Commit 4bfcdd7

Browse files
Merge pull request trafodion#3 from alchen99/master
Merge upstream changes
2 parents 1543bc5 + 6b37482 commit 4bfcdd7

File tree

14 files changed

+584
-31
lines changed

14 files changed

+584
-31
lines changed

doc/source/zuul.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,47 @@ each job as it builds a list from the project specification.
809809
file patterns listed here. This field is treated as a regular
810810
expression and multiple expressions may be listed.
811811

812+
**skip-if (optional)**
813+
814+
This job should not be run if all the patterns specified by the
815+
optional fields listed below match on their targets. When multiple
816+
sets of parameters are provided, this job will be skipped if any set
817+
matches. For example: ::
818+
819+
jobs:
820+
- name: check-tempest-dsvm-neutron
821+
skip-if:
822+
- project: ^openstack/neutron$
823+
branch: ^stable/juno$
824+
all-files-match-any:
825+
- ^neutron/tests/.*$
826+
- ^tools/.*$
827+
- all-files-match-any:
828+
- ^doc/.*$
829+
- ^.*\.rst$
830+
831+
With this configuration, the job would be skipped for a neutron
832+
patchset for the stable/juno branch provided that every file in the
833+
change matched at least one of the specified file regexes. The job
834+
will also be skipped for any patchset that modified only the doc
835+
tree or rst files.
836+
837+
*project* (optional)
838+
The regular expression to match against the project of the change.
839+
840+
*branch* (optional)
841+
The regular expression to match against the branch or ref of the
842+
change.
843+
844+
*all-files-match-any* (optional)
845+
A list of regular expressions intended to match the files involved
846+
in the change. This parameter will be considered matching a
847+
change only if all files in a change match at least one of these
848+
expressions.
849+
850+
The pattern for '/COMMIT_MSG' is always matched on and does not
851+
have to be included.
852+
812853
**voting (optional)**
813854
Boolean value (``true`` or ``false``) that indicates whatever
814855
a job is voting or not. Default: ``true``.

etc/status/public_html/jquery.zuul.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,11 @@
316316
var $enqueue_time = $('<small />').addClass('time')
317317
.attr('title', 'Elapsed Time').html(enqueue_time);
318318

319-
var $right = $('<div />')
320-
.addClass('col-xs-4 text-right')
321-
.append($remaining_time, $('<br />'), $enqueue_time);
319+
var $right = $('<div />');
320+
if (change.live === true) {
321+
$right.addClass('col-xs-4 text-right')
322+
.append($remaining_time, $('<br />'), $enqueue_time);
323+
}
322324

323325
var $header = $('<div />')
324326
.addClass('row')
@@ -840,7 +842,9 @@
840842
});
841843
$.each(change_queue.heads, function(head_i, head) {
842844
$.each(head, function(change_i, change) {
843-
count += 1;
845+
if (change.live === true) {
846+
count += 1;
847+
}
844848
var idx = tree.indexOf(change.id);
845849
if (idx > -1) {
846850
change._tree_index = idx;

requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ PyYAML>=3.1.0
55
Paste
66
WebOb>=1.2.3,<1.3
77
paramiko>=1.8.0
8-
GitPython>=0.3.2.1
9-
lockfile>=0.8
8+
GitPython>=0.3.3
109
ordereddict
1110
python-daemon>=2.0.4
1211
extras

tests/base.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,11 +811,11 @@ def get_auth(self):
811811
return endpoint, ''
812812

813813

814-
class ZuulTestCase(testtools.TestCase):
814+
class BaseTestCase(testtools.TestCase):
815815
log = logging.getLogger("zuul.test")
816816

817817
def setUp(self):
818-
super(ZuulTestCase, self).setUp()
818+
super(BaseTestCase, self).setUp()
819819
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
820820
try:
821821
test_timeout = int(test_timeout)
@@ -839,6 +839,12 @@ def setUp(self):
839839
level=logging.DEBUG,
840840
format='%(asctime)s %(name)-32s '
841841
'%(levelname)-8s %(message)s'))
842+
843+
844+
class ZuulTestCase(BaseTestCase):
845+
846+
def setUp(self):
847+
super(ZuulTestCase, self).setUp()
842848
if USE_TEMPDIR:
843849
tmp_root = self.useFixture(fixtures.TempDir(
844850
rootdir=os.environ.get("ZUUL_TEST_ROOT"))

tests/fixtures/layout-skip-if.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
pipelines:
2+
- name: check
3+
manager: IndependentPipelineManager
4+
trigger:
5+
gerrit:
6+
- event: patchset-created
7+
success:
8+
gerrit:
9+
verified: 1
10+
failure:
11+
gerrit:
12+
verified: -1
13+
14+
15+
jobs:
16+
# Defining a metajob will validate that the skip-if attribute of the
17+
# metajob is correctly copied to the job.
18+
- name: ^.*skip-if$
19+
skip-if:
20+
- project: ^org/project$
21+
branch: ^master$
22+
all-files-match-any:
23+
- ^README$
24+
- name: project-test-skip-if
25+
26+
projects:
27+
- name: org/project
28+
check:
29+
- project-test-skip-if

tests/test_change_matcher.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Copyright 2015 Red Hat, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from zuul import change_matcher as cm
16+
from zuul import model
17+
18+
from tests.base import BaseTestCase
19+
20+
21+
class BaseTestMatcher(BaseTestCase):
22+
23+
project = 'project'
24+
25+
def setUp(self):
26+
super(BaseTestMatcher, self).setUp()
27+
self.change = model.Change(self.project)
28+
29+
30+
class TestAbstractChangeMatcher(BaseTestMatcher):
31+
32+
def test_str(self):
33+
matcher = cm.ProjectMatcher(self.project)
34+
self.assertEqual(str(matcher), '{ProjectMatcher:project}')
35+
36+
def test_repr(self):
37+
matcher = cm.ProjectMatcher(self.project)
38+
self.assertEqual(repr(matcher), '<ProjectMatcher project>')
39+
40+
41+
class TestProjectMatcher(BaseTestMatcher):
42+
43+
def test_matches_returns_true(self):
44+
matcher = cm.ProjectMatcher(self.project)
45+
self.assertTrue(matcher.matches(self.change))
46+
47+
def test_matches_returns_false(self):
48+
matcher = cm.ProjectMatcher('not_project')
49+
self.assertFalse(matcher.matches(self.change))
50+
51+
52+
class TestBranchMatcher(BaseTestMatcher):
53+
54+
def setUp(self):
55+
super(TestBranchMatcher, self).setUp()
56+
self.matcher = cm.BranchMatcher('foo')
57+
58+
def test_matches_returns_true_on_matching_branch(self):
59+
self.change.branch = 'foo'
60+
self.assertTrue(self.matcher.matches(self.change))
61+
62+
def test_matches_returns_true_on_matching_ref(self):
63+
self.change.branch = 'bar'
64+
self.change.ref = 'foo'
65+
self.assertTrue(self.matcher.matches(self.change))
66+
67+
def test_matches_returns_false_for_no_match(self):
68+
self.change.branch = 'bar'
69+
self.change.ref = 'baz'
70+
self.assertFalse(self.matcher.matches(self.change))
71+
72+
def test_matches_returns_false_for_missing_attrs(self):
73+
delattr(self.change, 'branch')
74+
# ref is by default not an attribute
75+
self.assertFalse(self.matcher.matches(self.change))
76+
77+
78+
class TestFileMatcher(BaseTestMatcher):
79+
80+
def setUp(self):
81+
super(TestFileMatcher, self).setUp()
82+
self.matcher = cm.FileMatcher('filename')
83+
84+
def test_matches_returns_true(self):
85+
self.change.files = ['filename']
86+
self.assertTrue(self.matcher.matches(self.change))
87+
88+
def test_matches_returns_false_when_no_files(self):
89+
self.assertFalse(self.matcher.matches(self.change))
90+
91+
def test_matches_returns_false_when_files_attr_missing(self):
92+
delattr(self.change, 'files')
93+
self.assertFalse(self.matcher.matches(self.change))
94+
95+
96+
class TestAbstractMatcherCollection(BaseTestMatcher):
97+
98+
def test_str(self):
99+
matcher = cm.MatchAll([cm.FileMatcher('foo')])
100+
self.assertEqual(str(matcher), '{MatchAll:{FileMatcher:foo}}')
101+
102+
def test_repr(self):
103+
matcher = cm.MatchAll([])
104+
self.assertEqual(repr(matcher), '<MatchAll>')
105+
106+
107+
class TestMatchAllFiles(BaseTestMatcher):
108+
109+
def setUp(self):
110+
super(TestMatchAllFiles, self).setUp()
111+
self.matcher = cm.MatchAllFiles([cm.FileMatcher('^docs/.*$')])
112+
113+
def _test_matches(self, expected, files=None):
114+
if files is not None:
115+
self.change.files = files
116+
self.assertEqual(expected, self.matcher.matches(self.change))
117+
118+
def test_matches_returns_false_when_files_attr_missing(self):
119+
delattr(self.change, 'files')
120+
self._test_matches(False)
121+
122+
def test_matches_returns_false_when_no_files(self):
123+
self._test_matches(False)
124+
125+
def test_matches_returns_false_when_not_all_files_match(self):
126+
self._test_matches(False, files=['docs/foo', 'foo/bar'])
127+
128+
def test_matches_returns_true_when_commit_message_matches(self):
129+
self._test_matches(True, files=['/COMMIT_MSG'])
130+
131+
def test_matches_returns_true_when_all_files_match(self):
132+
self._test_matches(True, files=['docs/foo'])
133+
134+
135+
class TestMatchAll(BaseTestMatcher):
136+
137+
def test_matches_returns_true(self):
138+
matcher = cm.MatchAll([cm.ProjectMatcher(self.project)])
139+
self.assertTrue(matcher.matches(self.change))
140+
141+
def test_matches_returns_false_for_missing_matcher(self):
142+
matcher = cm.MatchAll([cm.ProjectMatcher('not_project')])
143+
self.assertFalse(matcher.matches(self.change))
144+
145+
146+
class TestMatchAny(BaseTestMatcher):
147+
148+
def test_matches_returns_true(self):
149+
matcher = cm.MatchAny([cm.ProjectMatcher(self.project)])
150+
self.assertTrue(matcher.matches(self.change))
151+
152+
def test_matches_returns_false(self):
153+
matcher = cm.MatchAny([cm.ProjectMatcher('not_project')])
154+
self.assertFalse(matcher.matches(self.change))

tests/test_model.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2015 Red Hat, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from zuul import change_matcher as cm
16+
from zuul import model
17+
18+
from tests.base import BaseTestCase
19+
20+
21+
class TestJob(BaseTestCase):
22+
23+
@property
24+
def job(self):
25+
job = model.Job('job')
26+
job.skip_if_matcher = cm.MatchAll([
27+
cm.ProjectMatcher('^project$'),
28+
cm.MatchAllFiles([cm.FileMatcher('^docs/.*$')]),
29+
])
30+
return job
31+
32+
def test_change_matches_returns_false_for_matched_skip_if(self):
33+
change = model.Change('project')
34+
change.files = ['docs/foo']
35+
self.assertFalse(self.job.changeMatches(change))
36+
37+
def test_change_matches_returns_true_for_unmatched_skip_if(self):
38+
change = model.Change('project')
39+
change.files = ['foo']
40+
self.assertTrue(self.job.changeMatches(change))
41+
42+
def test_copy_retains_skip_if(self):
43+
job = model.Job('job')
44+
job.copy(self.job)
45+
self.assertTrue(job.skip_if_matcher)
46+
47+
def _assert_job_booleans_are_not_none(self, job):
48+
self.assertIsNotNone(job.voting)
49+
self.assertIsNotNone(job.hold_following_changes)
50+
51+
def test_job_sets_defaults_for_boolean_attributes(self):
52+
job = model.Job('job')
53+
self._assert_job_booleans_are_not_none(job)
54+
55+
def test_metajob_does_not_set_defaults_for_boolean_attributes(self):
56+
job = model.Job('^job')
57+
self.assertIsNone(job.voting)
58+
self.assertIsNone(job.hold_following_changes)
59+
60+
def test_metajob_copy_does_not_set_undefined_boolean_attributes(self):
61+
job = model.Job('job')
62+
metajob = model.Job('^job')
63+
job.copy(metajob)
64+
self._assert_job_booleans_are_not_none(job)

0 commit comments

Comments
 (0)