Re-enable merge-mode config option and add more tests
This re-enables the ability for projects to define their own merging behavior via the project merge-mode option. It re-enables and reworks the old v2.5 test_build_configuration test, which was written against the cherry-pick merge mode. Prior to this patch, all merges were hard-coded to use the merge-recovery mode, so two new tests are also added here to test those configurations as well. Change-Id: Ief9430b3a3c33b0c9d7ebf3fd0361166e2a5a1af Story: 2000785 Task: 3309
This commit is contained in:
parent
2e7f9d7057
commit
8bd5710a0e
|
@ -0,0 +1,62 @@
|
|||
- pipeline:
|
||||
name: check
|
||||
manager: independent
|
||||
source:
|
||||
gerrit
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
||||
|
||||
- pipeline:
|
||||
name: gate
|
||||
manager: dependent
|
||||
success-message: Build succeeded (gate).
|
||||
source:
|
||||
gerrit
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: comment-added
|
||||
approval:
|
||||
- approved: 1
|
||||
success:
|
||||
gerrit:
|
||||
verified: 2
|
||||
submit: true
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -2
|
||||
start:
|
||||
gerrit:
|
||||
verified: 0
|
||||
precedence: high
|
||||
|
||||
- job:
|
||||
name:
|
||||
project-test1
|
||||
|
||||
- project:
|
||||
name: org/project-merge
|
||||
merge-mode: merge
|
||||
gate:
|
||||
jobs:
|
||||
- project-test1
|
||||
|
||||
- project:
|
||||
name: org/project-merge-resolve
|
||||
merge-mode: merge-resolve
|
||||
gate:
|
||||
jobs:
|
||||
- project-test1
|
||||
|
||||
- project:
|
||||
name: org/project-cherry-pick
|
||||
merge-mode: cherry-pick
|
||||
gate:
|
||||
jobs:
|
||||
- project-test1
|
|
@ -0,0 +1 @@
|
|||
test
|
|
@ -0,0 +1 @@
|
|||
test
|
|
@ -0,0 +1 @@
|
|||
test
|
|
@ -0,0 +1,6 @@
|
|||
- tenant:
|
||||
name: tenant-one
|
||||
source:
|
||||
gerrit:
|
||||
config-repos:
|
||||
- common-config
|
|
@ -923,41 +923,6 @@ class TestScheduler(ZuulTestCase):
|
|||
a = source.getChange(event, refresh=True)
|
||||
self.assertTrue(source.canMerge(a, mgr.getSubmitAllowNeeds()))
|
||||
|
||||
@skip("Disabled for early v3 development")
|
||||
def test_build_configuration(self):
|
||||
"Test that zuul merges the right commits for testing"
|
||||
|
||||
self.gearman_server.hold_jobs_in_queue = True
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
||||
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
||||
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
||||
A.addApproval('code-review', 2)
|
||||
B.addApproval('code-review', 2)
|
||||
C.addApproval('code-review', 2)
|
||||
self.fake_gerrit.addEvent(A.addApproval('approved', 1))
|
||||
self.fake_gerrit.addEvent(B.addApproval('approved', 1))
|
||||
self.fake_gerrit.addEvent(C.addApproval('approved', 1))
|
||||
self.waitUntilSettled()
|
||||
|
||||
self.gearman_server.release('.*-merge')
|
||||
self.waitUntilSettled()
|
||||
self.gearman_server.release('.*-merge')
|
||||
self.waitUntilSettled()
|
||||
self.gearman_server.release('.*-merge')
|
||||
self.waitUntilSettled()
|
||||
queue = self.gearman_server.getQueue()
|
||||
ref = self.getParameter(queue[-1], 'ZUUL_REF')
|
||||
self.gearman_server.hold_jobs_in_queue = False
|
||||
self.gearman_server.release()
|
||||
self.waitUntilSettled()
|
||||
|
||||
path = os.path.join(self.git_root, "org/project")
|
||||
repo = git.Repo(path)
|
||||
repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
|
||||
repo_messages.reverse()
|
||||
correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
|
||||
self.assertEqual(repo_messages, correct_messages)
|
||||
|
||||
@skip("Disabled for early v3 development")
|
||||
def test_build_configuration_conflict(self):
|
||||
"Test that merge conflicts are handled"
|
||||
|
@ -4713,3 +4678,66 @@ class TestSchedulerSuccessURL(ZuulTestCase):
|
|||
self.assertIn(
|
||||
'- docs-draft-test2 https://server/job',
|
||||
body[3])
|
||||
|
||||
|
||||
class TestSchedulerMergeModes(ZuulTestCase):
|
||||
tenant_config_file = 'config/merge-modes/main.yaml'
|
||||
|
||||
def _test_project_merge_mode(self, mode):
|
||||
self.launch_server.keep_jobdir = False
|
||||
project = 'org/project-%s' % mode
|
||||
self.launch_server.hold_jobs_in_build = True
|
||||
A = self.fake_gerrit.addFakeChange(project, 'master', 'A')
|
||||
B = self.fake_gerrit.addFakeChange(project, 'master', 'B')
|
||||
C = self.fake_gerrit.addFakeChange(project, 'master', 'C')
|
||||
A.addApproval('code-review', 2)
|
||||
B.addApproval('code-review', 2)
|
||||
C.addApproval('code-review', 2)
|
||||
self.fake_gerrit.addEvent(A.addApproval('approved', 1))
|
||||
self.fake_gerrit.addEvent(B.addApproval('approved', 1))
|
||||
self.fake_gerrit.addEvent(C.addApproval('approved', 1))
|
||||
self.waitUntilSettled()
|
||||
|
||||
build = self.builds[-1]
|
||||
ref = self.getParameter(build, 'ZUUL_REF')
|
||||
|
||||
path = os.path.join(build.jobdir.git_root, project)
|
||||
repo = git.Repo(path)
|
||||
repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
|
||||
repo_messages.reverse()
|
||||
|
||||
self.launch_server.hold_jobs_in_build = False
|
||||
self.launch_server.release()
|
||||
self.waitUntilSettled()
|
||||
|
||||
return repo_messages
|
||||
|
||||
def _test_merge(self, mode):
|
||||
us_path = os.path.join(
|
||||
self.upstream_root, 'org/project-%s' % mode)
|
||||
expected_messages = [
|
||||
'initial commit',
|
||||
'add content from fixture',
|
||||
# the intermediate commits order is nondeterministic
|
||||
"Merge commit 'refs/changes/1/2/1' of %s into HEAD" % us_path,
|
||||
"Merge commit 'refs/changes/1/3/1' of %s into HEAD" % us_path,
|
||||
]
|
||||
result = self._test_project_merge_mode(mode)
|
||||
self.assertEqual(result[:2], expected_messages[:2])
|
||||
self.assertEqual(result[-2:], expected_messages[-2:])
|
||||
|
||||
def test_project_merge_mode_merge(self):
|
||||
self._test_merge('merge')
|
||||
|
||||
def test_project_merge_mode_merge_resolve(self):
|
||||
self._test_merge('merge-resolve')
|
||||
|
||||
def test_project_merge_mode_cherrypick(self):
|
||||
expected_messages = [
|
||||
'initial commit',
|
||||
'add content from fixture',
|
||||
'A-1',
|
||||
'B-1',
|
||||
'C-1']
|
||||
result = self._test_project_merge_mode('cherry-pick')
|
||||
self.assertEqual(result, expected_messages)
|
||||
|
|
|
@ -185,7 +185,12 @@ class ProjectTemplateParser(object):
|
|||
|
||||
@staticmethod
|
||||
def getSchema(layout):
|
||||
project_template = {vs.Required('name'): str}
|
||||
project_template = {
|
||||
vs.Required('name'): str,
|
||||
'merge-mode': vs.Any(
|
||||
'merge', 'merge-resolve',
|
||||
'cherry-pick')}
|
||||
|
||||
for p in layout.pipelines.values():
|
||||
project_template[p.name] = {'queue': str,
|
||||
'jobs': [vs.Any(str, dict)]}
|
||||
|
@ -240,7 +245,9 @@ class ProjectParser(object):
|
|||
@staticmethod
|
||||
def getSchema(layout):
|
||||
project = {vs.Required('name'): str,
|
||||
'templates': [str]}
|
||||
'templates': [str],
|
||||
'merge-mode': vs.Any('merge', 'merge-resolve',
|
||||
'cherry-pick')}
|
||||
for p in layout.pipelines.values():
|
||||
project[p.name] = {'queue': str,
|
||||
'jobs': [vs.Any(str, dict)]}
|
||||
|
@ -259,6 +266,8 @@ class ProjectParser(object):
|
|||
configs = [layout.project_templates[name] for name in conf_templates]
|
||||
configs.append(project_template)
|
||||
project = model.ProjectConfig(conf['name'])
|
||||
mode = conf.get('merge-mode', 'merge-resolve')
|
||||
project.merge_mode = model.MERGER_MAP[mode]
|
||||
for pipeline in layout.pipelines.values():
|
||||
project_pipeline = model.ProjectPipelineConfig()
|
||||
project_pipeline.job_tree = model.JobTree(None)
|
||||
|
|
|
@ -43,11 +43,13 @@ def make_merger_item(item):
|
|||
newrev = item.change.newrev
|
||||
branch = item.change.ref
|
||||
connection_name = item.pipeline.source.connection.connection_name
|
||||
return dict(project=item.change.project.name,
|
||||
project = item.change.project.name
|
||||
|
||||
return dict(project=project,
|
||||
url=item.pipeline.source.getGitUrl(
|
||||
item.change.project),
|
||||
connection_name=connection_name,
|
||||
merge_mode=item.change.project.merge_mode,
|
||||
merge_mode=item.current_build_set.getMergeMode(project),
|
||||
refspec=refspec,
|
||||
branch=branch,
|
||||
ref=item.current_build_set.ref,
|
||||
|
|
|
@ -422,11 +422,13 @@ class PipelineManager(object):
|
|||
oldrev = item.change.oldrev
|
||||
newrev = item.change.newrev
|
||||
connection_name = self.pipeline.source.connection.connection_name
|
||||
return dict(project=item.change.project.name,
|
||||
|
||||
project = item.change.project.name
|
||||
return dict(project=project,
|
||||
url=self.pipeline.source.getGitUrl(
|
||||
item.change.project),
|
||||
connection_name=connection_name,
|
||||
merge_mode=item.change.project.merge_mode,
|
||||
merge_mode=item.current_build_set.getMergeMode(project),
|
||||
refspec=item.change.refspec,
|
||||
branch=item.change.branch,
|
||||
ref=item.current_build_set.ref,
|
||||
|
|
|
@ -329,7 +329,6 @@ class Project(object):
|
|||
|
||||
def __init__(self, name, foreign=False):
|
||||
self.name = name
|
||||
self.merge_mode = MERGER_MERGE_RESOLVE
|
||||
# foreign projects are those referenced in dependencies
|
||||
# of layout projects, this should matter
|
||||
# when deciding whether to enqueue their changes
|
||||
|
@ -704,6 +703,11 @@ class BuildSet(object):
|
|||
def getTries(self, job_name):
|
||||
return self.tries.get(job_name)
|
||||
|
||||
def getMergeMode(self, job_name):
|
||||
if not self.layout or job_name not in self.layout.project_configs:
|
||||
return MERGER_MERGE_RESOLVE
|
||||
return self.layout.project_configs[job_name].merge_mode
|
||||
|
||||
|
||||
class QueueItem(object):
|
||||
"""Represents the position of a Change in a ChangeQueue.
|
||||
|
@ -1584,13 +1588,14 @@ class ProjectPipelineConfig(object):
|
|||
def __init__(self):
|
||||
self.job_tree = None
|
||||
self.queue_name = None
|
||||
# TODOv3(jeblair): add merge mode
|
||||
self.merge_mode = None
|
||||
|
||||
|
||||
class ProjectConfig(object):
|
||||
# Represents a project cofiguration
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.merge_mode = None
|
||||
self.pipelines = {}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue