summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2022-10-27 02:25:40 +0000
committerGerrit Code Review <review@openstack.org>2022-10-27 02:25:40 +0000
commit45c04e4c69849ced101c5be53ec325664f59cfca (patch)
tree46f9591f50782d82b8913fa844cbee1becaf4e8f
parent760630415936694289b9edf6526ecb16a0014fb6 (diff)
parent26b9b0e2fb167e27f1ba74f0d3a6542f37c69d55 (diff)
downloadzuul-45c04e4c69849ced101c5be53ec325664f59cfca.tar.gz
Merge "Add rebase-merge merge mode"
-rw-r--r--doc/source/config/project.rst9
-rw-r--r--releasenotes/notes/rebase-merge-6b96aa541cd53492.yaml5
-rw-r--r--tests/fixtures/layouts/gate-github-rebase.yaml38
-rw-r--r--tests/unit/test_github_driver.py52
-rw-r--r--zuul/configloader.py3
-rw-r--r--zuul/driver/github/githubreporter.py13
-rw-r--r--zuul/merger/merger.py24
-rw-r--r--zuul/model.py4
8 files changed, 132 insertions, 16 deletions
diff --git a/doc/source/config/project.rst b/doc/source/config/project.rst
index af3faa943..04d61b5d3 100644
--- a/doc/source/config/project.rst
+++ b/doc/source/config/project.rst
@@ -138,13 +138,20 @@ pipeline.
.. value:: cherry-pick
Cherry-picks each change onto the branch rather than
- performing any merges. This is not supported by Github and GitLab.
+ performing any merges. This is not supported by GitHub and GitLab.
.. value:: squash-merge
Squash merges each change onto the branch. This maps to the
merge mode ``squash`` in GitHub and GitLab.
+ .. value:: rebase
+
+ Rebases the changes onto the branch. This is only supported
+ by GitHub and maps to the ``rebase`` merge mode (but
+ does not alter committer information in the way that GitHub
+ does in the repos that Zuul prepares for jobs).
+
.. attr:: vars
:default: None
diff --git a/releasenotes/notes/rebase-merge-6b96aa541cd53492.yaml b/releasenotes/notes/rebase-merge-6b96aa541cd53492.yaml
new file mode 100644
index 000000000..02f87c389
--- /dev/null
+++ b/releasenotes/notes/rebase-merge-6b96aa541cd53492.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ The :value:`project.merge-mode.rebase` merge-mode is now supported
+ for GitHub.
diff --git a/tests/fixtures/layouts/gate-github-rebase.yaml b/tests/fixtures/layouts/gate-github-rebase.yaml
new file mode 100644
index 000000000..80408decd
--- /dev/null
+++ b/tests/fixtures/layouts/gate-github-rebase.yaml
@@ -0,0 +1,38 @@
+- pipeline:
+ name: gate
+ manager: dependent
+ trigger:
+ github:
+ - event: pull_request
+ action:
+ - opened
+ - changed
+ - reopened
+ branch: ^master$
+ success:
+ github:
+ status: success
+ merge: true
+ failure:
+ github: {}
+
+- job:
+ name: base
+ parent: null
+ run: playbooks/base.yaml
+
+- job:
+ name: project-test1
+ run: playbooks/project-test1.yaml
+
+- job:
+ name: project-test2
+ run: playbooks/project-test2.yaml
+
+- project:
+ name: org/project
+ merge-mode: rebase
+ gate:
+ jobs:
+ - project-test1
+ - project-test2
diff --git a/tests/unit/test_github_driver.py b/tests/unit/test_github_driver.py
index 49dae0ccb..90d69a7bc 100644
--- a/tests/unit/test_github_driver.py
+++ b/tests/unit/test_github_driver.py
@@ -1356,11 +1356,59 @@ class TestGithubDriver(ZuulTestCase):
self.assertEquals(A.comments[1],
'Merge mode cherry-pick not supported by Github')
+ @simple_layout('layouts/gate-github-rebase.yaml', driver='github')
+ def test_merge_method_rebase(self):
+ """
+ Tests that the merge mode gets forwarded to the reporter and the
+ PR was rebased.
+ """
+ self.executor_server.keep_jobdir = True
+ self.executor_server.hold_jobs_in_build = True
+ github = self.fake_github.getGithubClient()
+ repo = github.repo_from_project('org/project')
+ repo._set_branch_protection(
+ 'master', contexts=['tenant-one/check', 'tenant-one/gate'])
+
+ A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
+ repo.create_status(A.head_sha, 'success', 'example.com', 'description',
+ 'tenant-one/check')
+
+ # Create a second commit on master to verify rebase behavior
+ self.create_commit('org/project', message="Test rebase commit")
+
+ self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
+ self.waitUntilSettled()
+
+ build = self.builds[-1]
+ path = os.path.join(build.jobdir.src_root, 'github.com/org/project')
+ repo = git.Repo(path)
+ repo_messages = [c.message.strip() for c in repo.iter_commits()]
+ repo_messages.reverse()
+ expected = [
+ 'initial commit',
+ 'initial commit', # simple_layout adds a second "initial commit"
+ 'Test rebase commit',
+ 'A-1',
+ ]
+ self.assertEqual(expected, repo_messages)
+
+ self.executor_server.hold_jobs_in_build = False
+ self.executor_server.release()
+ self.waitUntilSettled()
+
+ # the change should have entered the gate
+ self.assertEqual(2, len(self.history))
+
+ # now check if the merge was done via rebase
+ merges = [report for report in self.fake_github.github_data.reports
+ if report[2] == 'merge']
+ assert (len(merges) == 1 and merges[0][3] == 'rebase')
+
@simple_layout('layouts/gate-github-squash-merge.yaml', driver='github')
def test_merge_method_squash_merge(self):
"""
Tests that the merge mode gets forwarded to the reporter and the
- merge fails because cherry-pick is not supported by github.
+ PR was squashed.
"""
github = self.fake_github.getGithubClient()
repo = github.repo_from_project('org/project')
@@ -1381,7 +1429,7 @@ class TestGithubDriver(ZuulTestCase):
# the change should have entered the gate
self.assertEqual(2, len(self.history))
- # now check if the merge was done via rebase
+ # now check if the merge was done via squash
merges = [report for report in self.fake_github.github_data.reports
if report[2] == 'merge']
assert (len(merges) == 1 and merges[0][3] == 'squash')
diff --git a/zuul/configloader.py b/zuul/configloader.py
index 63633a54c..03953eb31 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -1113,7 +1113,8 @@ class ProjectParser(object):
'vars': ansible_vars_dict,
'templates': [str],
'merge-mode': vs.Any('merge', 'merge-resolve',
- 'cherry-pick', 'squash-merge'),
+ 'cherry-pick', 'squash-merge',
+ 'rebase'),
'default-branch': str,
'queue': str,
str: pipeline_contents,
diff --git a/zuul/driver/github/githubreporter.py b/zuul/driver/github/githubreporter.py
index 8c5e8944c..e5c5ebfbd 100644
--- a/zuul/driver/github/githubreporter.py
+++ b/zuul/driver/github/githubreporter.py
@@ -17,9 +17,8 @@ import logging
import voluptuous as v
import time
+from zuul import model
from zuul.lib.logutil import get_annotated_logger
-from zuul.model import MERGER_MERGE_RESOLVE, MERGER_MERGE, MERGER_MAP, \
- MERGER_SQUASH_MERGE
from zuul.reporter import BaseReporter
from zuul.exceptions import MergeFailure
from zuul.driver.util import scalar_or_list
@@ -34,9 +33,10 @@ class GithubReporter(BaseReporter):
# Merge modes supported by github
merge_modes = {
- MERGER_MERGE: 'merge',
- MERGER_MERGE_RESOLVE: 'merge',
- MERGER_SQUASH_MERGE: 'squash',
+ model.MERGER_MERGE: 'merge',
+ model.MERGER_MERGE_RESOLVE: 'merge',
+ model.MERGER_SQUASH_MERGE: 'squash',
+ model.MERGER_REBASE: 'rebase',
}
def __init__(self, driver, connection, pipeline, config=None):
@@ -189,7 +189,8 @@ class GithubReporter(BaseReporter):
merge_mode = item.current_build_set.getMergeMode()
if merge_mode not in self.merge_modes:
- mode = [x[0] for x in MERGER_MAP.items() if x[1] == merge_mode][0]
+ mode = [x[0] for x in model.MERGER_MAP.items()
+ if x[1] == merge_mode][0]
self.log.warning('Merge mode %s not supported by Github', mode)
raise MergeFailure('Merge mode %s not supported by Github' % mode)
diff --git a/zuul/merger/merger.py b/zuul/merger/merger.py
index 34b495fcc..09968eac6 100644
--- a/zuul/merger/merger.py
+++ b/zuul/merger/merger.py
@@ -582,7 +582,7 @@ class Repo(object):
repo.git.merge(*args)
return repo.head.commit
- def squash_merge(self, item, zuul_event_id=None):
+ def squashMerge(self, item, zuul_event_id=None):
log = get_annotated_logger(self.log, zuul_event_id)
repo = self.createRepoObject(zuul_event_id)
args = ['--squash', 'FETCH_HEAD']
@@ -594,6 +594,17 @@ class Repo(object):
'Merge change %s,%s' % (item['number'], item['patchset']))
return repo.head.commit
+ def rebaseMerge(self, item, base, zuul_event_id=None):
+ log = get_annotated_logger(self.log, zuul_event_id)
+ repo = self.createRepoObject(zuul_event_id)
+ args = [base]
+ ref = item['ref']
+ self.fetch(ref, zuul_event_id=zuul_event_id)
+ log.debug("Rebasing %s with args %s", ref, args)
+ repo.git.checkout('FETCH_HEAD')
+ repo.git.rebase(*args)
+ return repo.head.commit
+
def fetch(self, ref, zuul_event_id=None):
repo = self.createRepoObject(zuul_event_id)
# NOTE: The following is currently not applicable, but if we
@@ -1029,14 +1040,14 @@ class Merger(object):
for message in messages:
ref_log.debug(message)
- def _mergeChange(self, item, ref, zuul_event_id):
+ def _mergeChange(self, item, base, zuul_event_id):
log = get_annotated_logger(self.log, zuul_event_id)
repo = self.getRepo(item['connection'], item['project'],
zuul_event_id=zuul_event_id)
try:
- repo.checkout(ref, zuul_event_id=zuul_event_id)
+ repo.checkout(base, zuul_event_id=zuul_event_id)
except Exception:
- log.exception("Unable to checkout %s", ref)
+ log.exception("Unable to checkout %s", base)
return None, None
try:
@@ -1050,8 +1061,11 @@ class Merger(object):
commit = repo.cherryPick(item['ref'],
zuul_event_id=zuul_event_id)
elif mode == zuul.model.MERGER_SQUASH_MERGE:
- commit = repo.squash_merge(
+ commit = repo.squashMerge(
item, zuul_event_id=zuul_event_id)
+ elif mode == zuul.model.MERGER_REBASE:
+ commit = repo.rebaseMerge(
+ item, base, zuul_event_id=zuul_event_id)
else:
raise Exception("Unsupported merge mode: %s" % mode)
except git.GitCommandError:
diff --git a/zuul/model.py b/zuul/model.py
index 7269dee24..100785885 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -55,13 +55,15 @@ 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_SQUASH_MERGE = 4 # "git merge --squash"
+MERGER_REBASE = 5 # "git rebase"
MERGER_MAP = {
'merge': MERGER_MERGE,
'merge-resolve': MERGER_MERGE_RESOLVE,
'cherry-pick': MERGER_CHERRY_PICK,
'squash-merge': MERGER_SQUASH_MERGE,
+ 'rebase': MERGER_REBASE,
}
PRECEDENCE_NORMAL = 0