Select correct merge method for Github

Starting with Github Enterprise 3.8[0] and github.com from September
2022 on[1], the merge strategy changed from using merge-recursive to
merge-ort[0].

The merge-ort strategy is available in the Git client since version
2.33.0 and became the default in 2.34.0[2].

If not configured otherwise, we've so far used the default merge
strategy of the Git client (which varies depending on the client
version). With this change, we are now explicitly choosing the default
merge strategy based on the Github version. This way, we can reduce
errors resulting from the use of different merge strategies in Zuul and
Github.

Since the newly added merge strategies must be understood by the mergers
we also need to bump the model API version.

[0] https://docs.github.com/en/enterprise-server@3.8/admin/release-notes
[1] https://github.blog/changelog/2022-09-12-merge-commits-now-created-using-the-merge-ort-strategy/
[2] https://git-scm.com/docs/merge-strategies#Documentation/merge-strategies.txt-recursive

Change-Id: I354a76fa8985426312344818320980c67171d774
This commit is contained in:
Simon Westphahl 2023-03-07 11:28:52 +01:00
parent bd11c4ff79
commit 810191b60e
No known key found for this signature in database
11 changed files with 80 additions and 7 deletions

View File

@ -143,3 +143,9 @@ Version 17
:Prior Zuul version: 9.1.0
:Description: Adds ZuulRegex and adjusts SourceContext serialialization.
Affects schedulers and web.
Version 18
----------
:Prior Zuul version: 9.2.0
:Description: Adds new merge modes 'recursive' and 'ort' for the Github
driver.

View File

@ -0,0 +1,9 @@
---
upgrade:
- |
Zuul's GitHub driver has updated the default merge mode to better align
with GitHub's default behavior. This change requires Git 2.33.0 or newer
unless you explicitly override Zuul's default merge mode for a project.
Additionally, if you aren't overriding the merge mode then the merge
algorithm chosen may change depending on your Git and GitHub versions.
This is expected to provide more deterministic results.

View File

@ -19,3 +19,9 @@
gate:
jobs:
- noop
- project:
name: org/project1
gate:
jobs:
- noop

View File

@ -1260,7 +1260,7 @@ class TestMergeMode(ZuulTestCase):
@simple_layout('layouts/merge-mode-default.yaml', driver='github')
def test_default_merge_mode_github(self):
self._test_default_merge_mode(model.MERGER_MERGE,
self._test_default_merge_mode(model.MERGER_MERGE_ORT,
'github.com')

View File

@ -15,6 +15,7 @@
import json
from zuul import change_matcher
from zuul import model
from zuul.lib.re2util import ZuulRegex
from zuul.zk.components import ComponentRegistry
@ -480,6 +481,30 @@ class TestGithubModelUpgrade(ZuulTestCase):
"rebase not supported",
str(loading_errors[0].error))
@model_version(17)
@simple_layout('layouts/github-merge-mode.yaml', driver='github')
def test_default_merge_mode(self):
layout = self.scheds.first.sched.abide.tenants.get('tenant-one').layout
md = layout.getProjectMetadata('github.com/org/project1')
self.assertEqual(model.MERGER_MERGE, md.merge_mode)
# Upgrade our component
self.model_test_component_info.model_api = 18
component_registry = ComponentRegistry(self.zk_client)
for _ in iterate_timeout(30, "model api to update"):
if component_registry.model_api == 18:
break
# Perform a full reconfiguration which should cause us to
# re-fetch the merge modes.
self.scheds.first.fullReconfigure()
self.waitUntilSettled()
layout = self.scheds.first.sched.abide.tenants.get('tenant-one').layout
md = layout.getProjectMetadata('github.com/org/project1')
self.assertEqual(model.MERGER_MERGE_ORT, md.merge_mode)
class TestDefaultBranchUpgrade(ZuulTestCase):
config_file = "zuul-gerrit-github.conf"

View File

@ -61,6 +61,7 @@ from zuul.zk.change_cache import (
ChangeKey,
ConcurrentUpdateError,
)
from zuul.zk.components import COMPONENT_REGISTRY
from zuul.zk.event_queues import ConnectionEventQueue
GITHUB_BASE_URL = 'https://api.github.com'
@ -70,6 +71,8 @@ PREVIEW_CHECKS_ACCEPT = 'application/vnd.github.antiope-preview+json'
ALL_MERGE_MODES = [
model.MERGER_MERGE,
model.MERGER_MERGE_RESOLVE,
model.MERGER_MERGE_RECURSIVE,
model.MERGER_MERGE_ORT,
model.MERGER_SQUASH_MERGE,
model.MERGER_REBASE,
]
@ -1879,6 +1882,9 @@ class GithubConnection(ZKChangeCacheMixin, ZKBranchCacheMixin, BaseConnection):
if resp.get('allow_merge_commit'):
merge_modes.append(model.MERGER_MERGE)
merge_modes.append(model.MERGER_MERGE_RESOLVE)
if COMPONENT_REGISTRY.model_api >= 18:
merge_modes.append(model.MERGER_MERGE_RECURSIVE)
merge_modes.append(model.MERGER_MERGE_ORT)
if resp.get('allow_squash_merge'):
merge_modes.append(model.MERGER_SQUASH_MERGE)
if resp.get('allow_rebase_merge'):

View File

@ -35,6 +35,8 @@ class GithubReporter(BaseReporter):
merge_modes = {
model.MERGER_MERGE: 'merge',
model.MERGER_MERGE_RESOLVE: 'merge',
model.MERGER_MERGE_RECURSIVE: 'merge',
model.MERGER_MERGE_ORT: 'merge',
model.MERGER_SQUASH_MERGE: 'squash',
model.MERGER_REBASE: 'rebase',
}

View File

@ -24,6 +24,7 @@ from zuul.model import Project
from zuul.driver.github.githubmodel import GithubRefFilter
from zuul.driver.util import scalar_or_list
from zuul.zk.change_cache import ChangeKey
from zuul.zk.components import COMPONENT_REGISTRY
class GithubSource(BaseSource):
@ -158,6 +159,14 @@ class GithubSource(BaseSource):
"""Get the open changes for a project."""
raise NotImplementedError()
def getProjectDefaultMergeMode(self, project):
if COMPONENT_REGISTRY.model_api < 18:
return 'merge'
github_version = self.connection._github_client_manager._github_version
if github_version and github_version < (3, 8):
return 'merge-recursive'
return 'merge-ort'
def updateChange(self, change, history=None):
"""Update information for a change."""
raise NotImplementedError()

View File

@ -1129,6 +1129,12 @@ class Merger(object):
elif mode == zuul.model.MERGER_MERGE_RESOLVE:
commit = repo.merge(item['ref'], 'resolve',
zuul_event_id=zuul_event_id)
elif mode == zuul.model.MERGER_MERGE_RECURSIVE:
commit = repo.merge(item['ref'], 'recursive',
zuul_event_id=zuul_event_id)
elif mode == zuul.model.MERGER_MERGE_ORT:
commit = repo.merge(item['ref'], 'ort',
zuul_event_id=zuul_event_id)
elif mode == zuul.model.MERGER_CHERRY_PICK:
commit = repo.cherryPick(item['ref'],
zuul_event_id=zuul_event_id)

View File

@ -54,15 +54,19 @@ from zuul.zk.blob_store import BlobStore
from zuul.zk.change_cache import ChangeKey
from zuul.zk.components import COMPONENT_REGISTRY
MERGER_MERGE = 1 # "git merge"
MERGER_MERGE_RESOLVE = 2 # "git merge -s resolve"
MERGER_CHERRY_PICK = 3 # "git cherry-pick"
MERGER_SQUASH_MERGE = 4 # "git merge --squash"
MERGER_REBASE = 5 # "git rebase"
MERGER_MERGE = 1 # "git merge"
MERGER_MERGE_RESOLVE = 2 # "git merge -s resolve"
MERGER_CHERRY_PICK = 3 # "git cherry-pick"
MERGER_SQUASH_MERGE = 4 # "git merge --squash"
MERGER_REBASE = 5 # "git rebase"
MERGER_MERGE_RECURSIVE = 6 # "git merge -s recursive"
MERGER_MERGE_ORT = 7 # "git merge -s ort"
MERGER_MAP = {
'merge': MERGER_MERGE,
'merge-resolve': MERGER_MERGE_RESOLVE,
'merge-recursive': MERGER_MERGE_RECURSIVE,
'merge-ort': MERGER_MERGE_ORT,
'cherry-pick': MERGER_CHERRY_PICK,
'squash-merge': MERGER_SQUASH_MERGE,
'rebase': MERGER_REBASE,

View File

@ -14,4 +14,4 @@
# When making ZK schema changes, increment this and add a record to
# doc/source/developer/model-changelog.rst
MODEL_API = 17
MODEL_API = 18