Cleanup approval requirement testing
On further inspection, trigger requirements will still be required for parts of the kind of pipeline construction that we are using in OpenStack; the idea that they could be completely replaced by pipeline requirements is incorrect. To that end, un-deprecate trigger requirements and cleanup the testing of both of them so that each requirement is tested (relatively) independently. A small amount of requirement composition is also tested, though all possible combinations (a lot!) are not. Move this testing into a new file for organizational purposes. Also, support multiple vote values (eg, "+1" or "+2") when requiring an approval. Change-Id: I683c9a574ced0e27ced59e62b8059fef2dfd8b20
This commit is contained in:
parent
2c9effbfa0
commit
9c17dbf763
@ -423,13 +423,11 @@ explanation of each of the parameters::
|
||||
containing 'retrigger' somewhere in the comment text are added to a
|
||||
change.
|
||||
|
||||
*require-approval* (deprecated)
|
||||
*require-approval*
|
||||
This may be used for any event. It requires that a certain kind
|
||||
of approval be present for the current patchset of the change (the
|
||||
approval could be added by the event in question). It follows the
|
||||
same syntax as the "approval" pipeline requirement below. This
|
||||
form should be considered deprecated and the pipeline requirement
|
||||
used instead.
|
||||
same syntax as the "approval" pipeline requirement below.
|
||||
|
||||
**timer**
|
||||
This trigger will run based on a cron-style time specification.
|
||||
@ -475,7 +473,9 @@ explanation of each of the parameters::
|
||||
|
||||
Any other field is interpreted as a review category and value
|
||||
pair. For example ``verified: 1`` would require that the approval
|
||||
be for a +1 vote in the "Verified" column.
|
||||
be for a +1 vote in the "Verified" column. The value may either
|
||||
be a single value or a list: ``verified: [1, 2]`` would match
|
||||
either a +1 or +2 vote.
|
||||
|
||||
**open**
|
||||
A boolean value (``true`` or ``false``) that indicates whether the change
|
||||
|
59
tests/fixtures/layout-pipeline-requirements.yaml
vendored
59
tests/fixtures/layout-pipeline-requirements.yaml
vendored
@ -1,59 +0,0 @@
|
||||
includes:
|
||||
- python-file: custom_functions.py
|
||||
|
||||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- email-filter: jenkins@example.com
|
||||
older-than: 48h
|
||||
open: True
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: patchset-created
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: gate
|
||||
manager: DependentPipelineManager
|
||||
failure-message: Build failed. For information on how to proceed, see http://wiki.example.org/Test_Failures
|
||||
require:
|
||||
status:
|
||||
- NEW
|
||||
approval:
|
||||
- verified: 1
|
||||
username: jenkins
|
||||
newer-than: 48h
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
approval:
|
||||
- approved: 1
|
||||
- event: comment-added
|
||||
approval:
|
||||
- verified: 1
|
||||
success:
|
||||
gerrit:
|
||||
verified: 2
|
||||
submit: true
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -2
|
||||
start:
|
||||
gerrit:
|
||||
verified: 0
|
||||
precedence: high
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
merge-mode: cherry-pick
|
||||
check:
|
||||
- project-check
|
||||
gate:
|
||||
- project-gate
|
58
tests/fixtures/layout-require-approval.yaml
vendored
58
tests/fixtures/layout-require-approval.yaml
vendored
@ -1,58 +0,0 @@
|
||||
includes:
|
||||
- python-file: custom_functions.py
|
||||
|
||||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: patchset-created
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- email-filter: jenkins@example.com
|
||||
older-than: 48h
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: gate
|
||||
manager: DependentPipelineManager
|
||||
failure-message: Build failed. For information on how to proceed, see http://wiki.example.org/Test_Failures
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- verified: 1
|
||||
username: jenkins
|
||||
newer-than: 48h
|
||||
approval:
|
||||
- approved: 1
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- verified: 1
|
||||
username: jenkins
|
||||
newer-than: 48h
|
||||
approval:
|
||||
- verified: 1
|
||||
success:
|
||||
gerrit:
|
||||
verified: 2
|
||||
submit: true
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -2
|
||||
start:
|
||||
gerrit:
|
||||
verified: 0
|
||||
precedence: high
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
merge-mode: cherry-pick
|
||||
check:
|
||||
- project-check
|
||||
gate:
|
||||
- project-gate
|
@ -1,6 +1,3 @@
|
||||
includes:
|
||||
- python-file: custom_functions.py
|
||||
|
||||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
@ -19,6 +16,5 @@ pipelines:
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
merge-mode: cherry-pick
|
||||
check:
|
||||
- project-check
|
37
tests/fixtures/layout-requirement-email.yaml
vendored
Normal file
37
tests/fixtures/layout-requirement-email.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
pipelines:
|
||||
- name: pipeline
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- email-filter: jenkins@example.com
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: trigger
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- email-filter: jenkins@example.com
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project1
|
||||
pipeline:
|
||||
- project1-pipeline
|
||||
- name: org/project2
|
||||
trigger:
|
||||
- project2-trigger
|
39
tests/fixtures/layout-requirement-newer-than.yaml
vendored
Normal file
39
tests/fixtures/layout-requirement-newer-than.yaml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
pipelines:
|
||||
- name: pipeline
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- username: jenkins
|
||||
newer-than: 48h
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: trigger
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- username: jenkins
|
||||
newer-than: 48h
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project1
|
||||
pipeline:
|
||||
- project1-pipeline
|
||||
- name: org/project2
|
||||
trigger:
|
||||
- project2-trigger
|
39
tests/fixtures/layout-requirement-older-than.yaml
vendored
Normal file
39
tests/fixtures/layout-requirement-older-than.yaml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
pipelines:
|
||||
- name: pipeline
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- username: jenkins
|
||||
older-than: 48h
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: trigger
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- username: jenkins
|
||||
older-than: 48h
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project1
|
||||
pipeline:
|
||||
- project1-pipeline
|
||||
- name: org/project2
|
||||
trigger:
|
||||
- project2-trigger
|
20
tests/fixtures/layout-requirement-open.yaml
vendored
Normal file
20
tests/fixtures/layout-requirement-open.yaml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
open: True
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: patchset-created
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
check:
|
||||
- project-check
|
20
tests/fixtures/layout-requirement-status.yaml
vendored
Normal file
20
tests/fixtures/layout-requirement-status.yaml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
status: NEW
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: patchset-created
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
check:
|
||||
- project-check
|
37
tests/fixtures/layout-requirement-username.yaml
vendored
Normal file
37
tests/fixtures/layout-requirement-username.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
pipelines:
|
||||
- name: pipeline
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- username: jenkins
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: trigger
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- username: jenkins
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project1
|
||||
pipeline:
|
||||
- project1-pipeline
|
||||
- name: org/project2
|
||||
trigger:
|
||||
- project2-trigger
|
39
tests/fixtures/layout-requirement-vote.yaml
vendored
Normal file
39
tests/fixtures/layout-requirement-vote.yaml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
pipelines:
|
||||
- name: pipeline
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- username: jenkins
|
||||
verified: 1
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: trigger
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- username: jenkins
|
||||
verified: 1
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project1
|
||||
pipeline:
|
||||
- project1-pipeline
|
||||
- name: org/project2
|
||||
trigger:
|
||||
- project2-trigger
|
39
tests/fixtures/layout-requirement-vote1.yaml
vendored
Normal file
39
tests/fixtures/layout-requirement-vote1.yaml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
pipelines:
|
||||
- name: pipeline
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- username: jenkins
|
||||
verified: 1
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: trigger
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- username: jenkins
|
||||
verified: 1
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project1
|
||||
pipeline:
|
||||
- project1-pipeline
|
||||
- name: org/project2
|
||||
trigger:
|
||||
- project2-trigger
|
39
tests/fixtures/layout-requirement-vote2.yaml
vendored
Normal file
39
tests/fixtures/layout-requirement-vote2.yaml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
pipelines:
|
||||
- name: pipeline
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
approval:
|
||||
- username: jenkins
|
||||
verified: [1, 2]
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: trigger
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- username: jenkins
|
||||
verified: [1, 2]
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project1
|
||||
pipeline:
|
||||
- project1-pipeline
|
||||
- name: org/project2
|
||||
trigger:
|
||||
- project2-trigger
|
323
tests/test_requirements.py
Normal file
323
tests/test_requirements.py
Normal file
@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
from tests.base import ZuulTestCase
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s %(name)-32s '
|
||||
'%(levelname)-8s %(message)s')
|
||||
|
||||
|
||||
class TestRequirements(ZuulTestCase):
|
||||
"""Test pipeline and trigger requirements"""
|
||||
|
||||
def test_pipeline_require_approval_newer_than(self):
|
||||
"Test pipeline requirement: approval newer than"
|
||||
return self._test_require_approval_newer_than('org/project1',
|
||||
'project1-pipeline')
|
||||
|
||||
def test_trigger_require_approval_newer_than(self):
|
||||
"Test trigger requirement: approval newer than"
|
||||
return self._test_require_approval_newer_than('org/project2',
|
||||
'project2-trigger')
|
||||
|
||||
def _test_require_approval_newer_than(self, project, job):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-newer-than.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange(project, 'master', 'A')
|
||||
# A comment event that we will keep submitting to trigger
|
||||
comment = A.addApproval('CRVW', 2, username='nobody')
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
# No +1 from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add a too-old +1, should not be enqueued
|
||||
A.addApproval('VRFY', 1, granted_on=time.time() - 72 * 60 * 60)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add a recent +1
|
||||
self.fake_gerrit.addEvent(A.addApproval('VRFY', 1))
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, job)
|
||||
|
||||
def test_pipeline_require_approval_older_than(self):
|
||||
"Test pipeline requirement: approval older than"
|
||||
return self._test_require_approval_older_than('org/project1',
|
||||
'project1-pipeline')
|
||||
|
||||
def test_trigger_require_approval_older_than(self):
|
||||
"Test trigger requirement: approval older than"
|
||||
return self._test_require_approval_older_than('org/project2',
|
||||
'project2-trigger')
|
||||
|
||||
def _test_require_approval_older_than(self, project, job):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-older-than.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange(project, 'master', 'A')
|
||||
# A comment event that we will keep submitting to trigger
|
||||
comment = A.addApproval('CRVW', 2, username='nobody')
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
# No +1 from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add a recent +1 which should not be enqueued
|
||||
A.addApproval('VRFY', 1)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add an old +1 which should be enqueued
|
||||
A.addApproval('VRFY', 1, granted_on=time.time() - 72 * 60 * 60)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, job)
|
||||
|
||||
def test_pipeline_require_approval_username(self):
|
||||
"Test pipeline requirement: approval username"
|
||||
return self._test_require_approval_username('org/project1',
|
||||
'project1-pipeline')
|
||||
|
||||
def test_trigger_require_approval_username(self):
|
||||
"Test trigger requirement: approval username"
|
||||
return self._test_require_approval_username('org/project2',
|
||||
'project2-trigger')
|
||||
|
||||
def _test_require_approval_username(self, project, job):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-username.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange(project, 'master', 'A')
|
||||
# A comment event that we will keep submitting to trigger
|
||||
comment = A.addApproval('CRVW', 2, username='nobody')
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
# No approval from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add an approval from Jenkins
|
||||
A.addApproval('VRFY', 1)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, job)
|
||||
|
||||
def test_pipeline_require_approval_email(self):
|
||||
"Test pipeline requirement: approval email"
|
||||
return self._test_require_approval_email('org/project1',
|
||||
'project1-pipeline')
|
||||
|
||||
def test_trigger_require_approval_email(self):
|
||||
"Test trigger requirement: approval email"
|
||||
return self._test_require_approval_email('org/project2',
|
||||
'project2-trigger')
|
||||
|
||||
def _test_require_approval_email(self, project, job):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-email.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange(project, 'master', 'A')
|
||||
# A comment event that we will keep submitting to trigger
|
||||
comment = A.addApproval('CRVW', 2, username='nobody')
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
# No approval from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add an approval from Jenkins
|
||||
A.addApproval('VRFY', 1)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, job)
|
||||
|
||||
def test_pipeline_require_approval_vote1(self):
|
||||
"Test pipeline requirement: approval vote with one value"
|
||||
return self._test_require_approval_vote1('org/project1',
|
||||
'project1-pipeline')
|
||||
|
||||
def test_trigger_require_approval_vote1(self):
|
||||
"Test trigger requirement: approval vote with one value"
|
||||
return self._test_require_approval_vote1('org/project2',
|
||||
'project2-trigger')
|
||||
|
||||
def _test_require_approval_vote1(self, project, job):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-vote1.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange(project, 'master', 'A')
|
||||
# A comment event that we will keep submitting to trigger
|
||||
comment = A.addApproval('CRVW', 2, username='nobody')
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
# No approval from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# A -1 from jenkins should not cause it to be enqueued
|
||||
A.addApproval('VRFY', -1)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# A +1 should allow it to be enqueued
|
||||
A.addApproval('VRFY', 1)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, job)
|
||||
|
||||
def test_pipeline_require_approval_vote2(self):
|
||||
"Test pipeline requirement: approval vote with two values"
|
||||
return self._test_require_approval_vote2('org/project1',
|
||||
'project1-pipeline')
|
||||
|
||||
def test_trigger_require_approval_vote2(self):
|
||||
"Test trigger requirement: approval vote with two values"
|
||||
return self._test_require_approval_vote2('org/project2',
|
||||
'project2-trigger')
|
||||
|
||||
def _test_require_approval_vote2(self, project, job):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-vote2.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange(project, 'master', 'A')
|
||||
# A comment event that we will keep submitting to trigger
|
||||
comment = A.addApproval('CRVW', 2, username='nobody')
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
# No approval from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# A -1 from jenkins should not cause it to be enqueued
|
||||
A.addApproval('VRFY', -1)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# A -2 from jenkins should not cause it to be enqueued
|
||||
A.addApproval('VRFY', -2)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# A +1 should allow it to be enqueued
|
||||
A.addApproval('VRFY', 1)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, job)
|
||||
|
||||
# A +2 should allow it to be enqueued
|
||||
B = self.fake_gerrit.addFakeChange(project, 'master', 'B')
|
||||
# A comment event that we will keep submitting to trigger
|
||||
comment = B.addApproval('CRVW', 2, username='nobody')
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
|
||||
B.addApproval('VRFY', 2)
|
||||
self.fake_gerrit.addEvent(comment)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 2)
|
||||
self.assertEqual(self.history[1].name, job)
|
||||
|
||||
def test_pipeline_require_current_patchset(self):
|
||||
"Test pipeline requirement: current-patchset"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-'
|
||||
'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')
|
||||
self.fake_gerrit.addEvent(A.addApproval('CRVW', 1))
|
||||
self.waitUntilSettled()
|
||||
A.addPatchset()
|
||||
self.fake_gerrit.addEvent(A.addApproval('CRVW', 1))
|
||||
self.waitUntilSettled()
|
||||
|
||||
self.assertEqual(len(self.history), 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), 2)
|
||||
|
||||
# Make sure the same event on a new PS will trigger
|
||||
self.fake_gerrit.addEvent(A.getChangeCommentEvent(2))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 3)
|
||||
|
||||
def test_pipeline_require_open(self):
|
||||
"Test pipeline requirement: open"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-open.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
|
||||
status='MERGED')
|
||||
self.fake_gerrit.addEvent(A.addApproval('CRVW', 2))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
||||
self.fake_gerrit.addEvent(B.addApproval('CRVW', 2))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
|
||||
def test_pipeline_require_status(self):
|
||||
"Test pipeline requirement: status"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-requirement-status.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
|
||||
status='MERGED')
|
||||
self.fake_gerrit.addEvent(A.addApproval('CRVW', 2))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
||||
self.fake_gerrit.addEvent(B.addApproval('CRVW', 2))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
@ -664,31 +664,6 @@ class TestScheduler(ZuulTestCase):
|
||||
self.assertTrue(trigger.canMerge(a, mgr.getSubmitAllowNeeds()))
|
||||
trigger.maintainCache([])
|
||||
|
||||
def test_pipeline_requirements_closed_change(self):
|
||||
"Test that pipeline requirements for closed changes are effective"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-pipeline-requirements.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
|
||||
status='MERGED')
|
||||
self.fake_gerrit.addEvent(A.addApproval('CRVW', 2))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
self.assertEqual(len(self.builds), 0)
|
||||
|
||||
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B',
|
||||
status='MERGED')
|
||||
B.addApproval('CRVW', 2)
|
||||
B.addApproval('VRFY', 1)
|
||||
self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 0)
|
||||
self.assertEqual(len(self.builds), 0)
|
||||
|
||||
for pipeline in self.sched.layout.pipelines.values():
|
||||
pipeline.trigger.maintainCache([])
|
||||
|
||||
def test_build_configuration(self):
|
||||
"Test that zuul merges the right commits for testing"
|
||||
|
||||
@ -1651,135 +1626,6 @@ class TestScheduler(ZuulTestCase):
|
||||
self.assertEqual(D.data['status'], 'MERGED')
|
||||
self.assertEqual(D.reported, 2)
|
||||
|
||||
def test_pipeline_requirements_approval_check_and_gate(self):
|
||||
"Test pipeline requirements triggers both check and gate"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-pipeline-requirements.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
self._test_required_approval_check_and_gate()
|
||||
|
||||
def test_required_approval_check_and_gate(self):
|
||||
"Test required-approval triggers both check and gate"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-require-approval.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
self._test_required_approval_check_and_gate()
|
||||
|
||||
def _test_required_approval_check_and_gate(self):
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
||||
A.addApproval('CRVW', 2)
|
||||
# Add a too-old +1
|
||||
A.addApproval('VRFY', 1, granted_on=time.time() - 72 * 60 * 60)
|
||||
|
||||
aprv = A.addApproval('APRV', 1)
|
||||
self.fake_gerrit.addEvent(aprv)
|
||||
self.waitUntilSettled()
|
||||
# Should have run a check job
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, 'project-check')
|
||||
|
||||
# Report the result of that check job (overrides previous vrfy)
|
||||
# Skynet alert: this should trigger a gate job now that
|
||||
# all reqs are met
|
||||
self.fake_gerrit.addEvent(A.addApproval('VRFY', 1))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 2)
|
||||
self.assertEqual(self.history[1].name, 'project-gate')
|
||||
|
||||
def test_pipeline_requirements_approval_newer(self):
|
||||
"Test pipeline requirements newer trigger parameter"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-pipeline-requirements.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
self._test_required_approval_newer()
|
||||
|
||||
def test_required_approval_newer(self):
|
||||
"Test required-approval newer trigger parameter"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-require-approval.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
self._test_required_approval_newer()
|
||||
|
||||
def _test_required_approval_newer(self):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-require-approval.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
||||
A.addApproval('CRVW', 2)
|
||||
aprv = A.addApproval('APRV', 1)
|
||||
self.fake_gerrit.addEvent(aprv)
|
||||
self.waitUntilSettled()
|
||||
# No +1 from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add a too-old +1, should trigger check but not gate
|
||||
A.addApproval('VRFY', 1, granted_on=time.time() - 72 * 60 * 60)
|
||||
self.fake_gerrit.addEvent(aprv)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, 'project-check')
|
||||
|
||||
# Add a recent +1
|
||||
self.fake_gerrit.addEvent(A.addApproval('VRFY', 1))
|
||||
self.fake_gerrit.addEvent(aprv)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 2)
|
||||
self.assertEqual(self.history[1].name, 'project-gate')
|
||||
|
||||
def test_pipeline_requirements_approval_older(self):
|
||||
"Test pipeline requirements older trigger parameter"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-pipeline-requirements.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
self._test_required_approval_older()
|
||||
|
||||
def test_required_approval_older(self):
|
||||
"Test required-approval older trigger parameter"
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-require-approval.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
self._test_required_approval_older()
|
||||
|
||||
def _test_required_approval_older(self):
|
||||
self.config.set('zuul', 'layout_config',
|
||||
'tests/fixtures/layout-require-approval.yaml')
|
||||
self.sched.reconfigure(self.config)
|
||||
self.registerJobs()
|
||||
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
||||
crvw = A.addApproval('CRVW', 2)
|
||||
self.fake_gerrit.addEvent(crvw)
|
||||
self.waitUntilSettled()
|
||||
# No +1 from Jenkins so should not be enqueued
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Add an old +1 and trigger check with a comment
|
||||
A.addApproval('VRFY', 1, granted_on=time.time() - 72 * 60 * 60)
|
||||
self.fake_gerrit.addEvent(crvw)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, 'project-check')
|
||||
|
||||
# Add a recent +1 and make sure nothing changes
|
||||
A.addApproval('VRFY', 1)
|
||||
self.fake_gerrit.addEvent(crvw)
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
|
||||
# The last thing we did was query 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([])
|
||||
|
||||
def test_rerun_on_error(self):
|
||||
"Test that if a worker fails to run a job, it is run again"
|
||||
self.worker.hold_jobs_in_build = True
|
||||
@ -2921,35 +2767,3 @@ For CI problems and help debugging, contact ci@example.org"""
|
||||
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([])
|
||||
|
168
zuul/model.py
168
zuul/model.py
@ -979,10 +979,66 @@ class TriggerEvent(object):
|
||||
return change
|
||||
|
||||
|
||||
class EventFilter(object):
|
||||
class BaseFilter(object):
|
||||
def __init__(self, required_approvals=[]):
|
||||
self.required_approvals = required_approvals
|
||||
|
||||
for a in self.required_approvals:
|
||||
for k, v in a.items():
|
||||
if k == 'username':
|
||||
pass
|
||||
elif k == 'email-filter':
|
||||
a[k] = re.compile(v)
|
||||
elif k == 'newer-than':
|
||||
a[k] = time_to_seconds(v)
|
||||
elif k == 'older-than':
|
||||
a[k] = time_to_seconds(v)
|
||||
else:
|
||||
if not isinstance(v, list):
|
||||
a[k] = [v]
|
||||
|
||||
def matchesRequiredApprovals(self, change):
|
||||
now = time.time()
|
||||
for rapproval in self.required_approvals:
|
||||
matches_approval = False
|
||||
for approval in change.approvals:
|
||||
if 'description' not in approval:
|
||||
continue
|
||||
found_approval = True
|
||||
by = approval.get('by', {})
|
||||
for k, v in rapproval.items():
|
||||
if k == 'username':
|
||||
if (by.get('username', '') != v):
|
||||
found_approval = False
|
||||
elif k == 'email-filter':
|
||||
if (not v.search(by.get('email', ''))):
|
||||
found_approval = False
|
||||
elif k == 'newer-than':
|
||||
t = now - v
|
||||
if (approval['grantedOn'] < t):
|
||||
found_approval = False
|
||||
elif k == 'older-than':
|
||||
t = now - v
|
||||
if (approval['grantedOn'] >= t):
|
||||
found_approval = False
|
||||
else:
|
||||
if (normalizeCategory(approval['description']) != k or
|
||||
int(approval['value']) not in v):
|
||||
found_approval = False
|
||||
if found_approval:
|
||||
matches_approval = True
|
||||
break
|
||||
if not matches_approval:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class EventFilter(BaseFilter):
|
||||
def __init__(self, types=[], branches=[], refs=[], event_approvals={},
|
||||
comment_filters=[], email_filters=[], username_filters=[],
|
||||
timespecs=[], require_approvals=[]):
|
||||
timespecs=[], required_approvals=[]):
|
||||
super(EventFilter, self).__init__(
|
||||
required_approvals=required_approvals)
|
||||
self._types = types
|
||||
self._branches = branches
|
||||
self._refs = refs
|
||||
@ -996,17 +1052,8 @@ class EventFilter(object):
|
||||
self.email_filters = [re.compile(x) for x in email_filters]
|
||||
self.username_filters = [re.compile(x) for x in username_filters]
|
||||
self.event_approvals = event_approvals
|
||||
self.require_approvals = require_approvals
|
||||
self.timespecs = timespecs
|
||||
|
||||
for a in self.require_approvals:
|
||||
if 'older-than' in a:
|
||||
a['older-than'] = time_to_seconds(a['older-than'])
|
||||
if 'newer-than' in a:
|
||||
a['newer-than'] = time_to_seconds(a['newer-than'])
|
||||
if 'email-filter' in a:
|
||||
a['email-filter'] = re.compile(a['email-filter'])
|
||||
|
||||
def __repr__(self):
|
||||
ret = '<EventFilter'
|
||||
|
||||
@ -1019,9 +1066,9 @@ class EventFilter(object):
|
||||
if self.event_approvals:
|
||||
ret += ' event_approvals: %s' % ', '.join(
|
||||
['%s:%s' % a for a in self.event_approvals.items()])
|
||||
if self.require_approvals:
|
||||
ret += ' require_approvals: %s' % ', '.join(
|
||||
['%s' % a for a in self.require_approvals])
|
||||
if self.required_approvals:
|
||||
ret += ' required_approvals: %s' % ', '.join(
|
||||
['%s' % a for a in self.required_approvals])
|
||||
if self._comment_filters:
|
||||
ret += ' comment_filters: %s' % ', '.join(self._comment_filters)
|
||||
if self._email_filters:
|
||||
@ -1101,42 +1148,13 @@ class EventFilter(object):
|
||||
if not matches_approval:
|
||||
return False
|
||||
|
||||
if self.require_approvals and not change.approvals:
|
||||
if self.required_approvals and not change.approvals:
|
||||
# A change with no approvals can not match
|
||||
return False
|
||||
|
||||
now = time.time()
|
||||
for rapproval in self.require_approvals:
|
||||
matches_approval = False
|
||||
for approval in change.approvals:
|
||||
if 'description' not in approval:
|
||||
continue
|
||||
found_approval = True
|
||||
by = approval.get('by', {})
|
||||
for k, v in rapproval.items():
|
||||
if k == 'username':
|
||||
if (by.get('username', '') != v):
|
||||
found_approval = False
|
||||
elif k == 'email-filter':
|
||||
if (not v.search(by.get('email', ''))):
|
||||
found_approval = False
|
||||
elif k == 'newer-than':
|
||||
t = now - v
|
||||
if (approval['grantedOn'] < t):
|
||||
found_approval = False
|
||||
elif k == 'older-than':
|
||||
t = now - v
|
||||
if (approval['grantedOn'] >= t):
|
||||
found_approval = False
|
||||
else:
|
||||
if (normalizeCategory(approval['description']) != k or
|
||||
int(approval['value']) != v):
|
||||
found_approval = False
|
||||
if found_approval:
|
||||
matches_approval = True
|
||||
break
|
||||
if not matches_approval:
|
||||
return False
|
||||
# required approvals are ANDed
|
||||
if not self.matchesRequiredApprovals(change):
|
||||
return False
|
||||
|
||||
# timespecs are ORed
|
||||
matches_timespec = False
|
||||
@ -1149,21 +1167,14 @@ class EventFilter(object):
|
||||
return True
|
||||
|
||||
|
||||
class ChangeishFilter(object):
|
||||
class ChangeishFilter(BaseFilter):
|
||||
def __init__(self, open=None, current_patchset=None,
|
||||
statuses=[], approvals=[]):
|
||||
statuses=[], required_approvals=[]):
|
||||
super(ChangeishFilter, self).__init__(
|
||||
required_approvals=required_approvals)
|
||||
self.open = open
|
||||
self.current_patchset = current_patchset
|
||||
self.statuses = statuses
|
||||
self.approvals = approvals
|
||||
|
||||
for a in self.approvals:
|
||||
if 'older-than' in a:
|
||||
a['older-than'] = time_to_seconds(a['older-than'])
|
||||
if 'newer-than' in a:
|
||||
a['newer-than'] = time_to_seconds(a['newer-than'])
|
||||
if 'email-filter' in a:
|
||||
a['email-filter'] = re.compile(a['email-filter'])
|
||||
|
||||
def __repr__(self):
|
||||
ret = '<ChangeishFilter'
|
||||
@ -1174,8 +1185,8 @@ class ChangeishFilter(object):
|
||||
ret += ' current-patchset: %s' % self.current_patchset
|
||||
if self.statuses:
|
||||
ret += ' statuses: %s' % ', '.join(self.statuses)
|
||||
if self.approvals:
|
||||
ret += ' approvals: %s' % str(self.approvals)
|
||||
if self.required_approvals:
|
||||
ret += ' required_approvals: %s' % str(self.required_approvals)
|
||||
ret += '>'
|
||||
|
||||
return ret
|
||||
@ -1193,42 +1204,13 @@ class ChangeishFilter(object):
|
||||
if change.status not in self.statuses:
|
||||
return False
|
||||
|
||||
if self.approvals and not change.approvals:
|
||||
if self.required_approvals and not change.approvals:
|
||||
# A change with no approvals can not match
|
||||
return False
|
||||
|
||||
now = time.time()
|
||||
for rapproval in self.approvals:
|
||||
matches_approval = False
|
||||
for approval in change.approvals:
|
||||
if 'description' not in approval:
|
||||
continue
|
||||
found_approval = True
|
||||
by = approval.get('by', {})
|
||||
for k, v in rapproval.items():
|
||||
if k == 'username':
|
||||
if (by.get('username', '') != v):
|
||||
found_approval = False
|
||||
elif k == 'email-filter':
|
||||
if (not v.search(by.get('email', ''))):
|
||||
found_approval = False
|
||||
elif k == 'newer-than':
|
||||
t = now - v
|
||||
if (approval['grantedOn'] < t):
|
||||
found_approval = False
|
||||
elif k == 'older-than':
|
||||
t = now - v
|
||||
if (approval['grantedOn'] >= t):
|
||||
found_approval = False
|
||||
else:
|
||||
if (normalizeCategory(approval['description']) != k or
|
||||
int(approval['value']) != v):
|
||||
found_approval = False
|
||||
if found_approval:
|
||||
matches_approval = True
|
||||
break
|
||||
if not matches_approval:
|
||||
return False
|
||||
# required approvals are ANDed
|
||||
if not self.matchesRequiredApprovals(change):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
@ -280,7 +280,7 @@ class Scheduler(threading.Thread):
|
||||
open=require.get('open'),
|
||||
current_patchset=require.get('current-patchset'),
|
||||
statuses=toList(require.get('status')),
|
||||
approvals=toList(require.get('approval')))
|
||||
required_approvals=toList(require.get('approval')))
|
||||
manager.changeish_filters.append(f)
|
||||
|
||||
# TODO: move this into triggers (may require pluggable
|
||||
@ -302,7 +302,7 @@ class Scheduler(threading.Thread):
|
||||
toList(trigger.get('email_filter')),
|
||||
username_filters=
|
||||
toList(trigger.get('username_filter')),
|
||||
require_approvals=
|
||||
required_approvals=
|
||||
toList(trigger.get('require-approval')))
|
||||
manager.event_filters.append(f)
|
||||
elif 'timer' in conf_pipeline['trigger']:
|
||||
|
Loading…
x
Reference in New Issue
Block a user