Support squash merge in Github

Many projects running on Github want to use the squash merge strategy
of github to keep their history clean. Now that the github reporter
forwards the merge-mode that can be configured on a project it is easy
to also support squash merge.

Change-Id: I1f4131002dd380f2860557a9aebcc0bcf4dc2af8
This commit is contained in:
Tobias Henkel 2019-05-23 21:39:41 +02:00
parent 3f485be486
commit d221171e62
No known key found for this signature in database
GPG Key ID: 03750DEC158E5FA2
8 changed files with 98 additions and 2 deletions

View File

@ -1369,6 +1369,11 @@ pipeline.
Cherry-picks each change onto the branch rather than
performing any merges. This is not supported by Github.
.. value:: squash-merge
Squash merges each change onto the branch. This maps to the
merge mode ``squash`` in Github.
.. attr:: vars
:default: None

View File

@ -0,0 +1,4 @@
---
features:
- |
The merge-mode ``squash-merge`` is now supported for Github.

View File

@ -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: squash-merge
gate:
jobs:
- project-test1
- project-test2

View File

@ -1126,6 +1126,36 @@ class TestGithubDriver(ZuulTestCase):
self.assertFalse(A.is_merged)
self.assertIn('Merge Failed', A.comments[1])
@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.
"""
github = self.fake_github.getGithubClient()
github._data.required_contexts[('org/project', 'master')] = [
'tenant-one/check',
'tenant-one/gate']
A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
self.waitUntilSettled()
repo = github.repo_from_project('org/project')
repo.create_status(A.head_sha, 'success', 'example.com', 'description',
'tenant-one/check')
self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
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.reports
if report[2] == 'merge']
assert(len(merges) == 1 and merges[0][3] == 'squash')
class TestGithubUnprotectedBranches(ZuulTestCase):
config_file = 'zuul-github-driver.conf'

View File

@ -1020,7 +1020,7 @@ class ProjectParser(object):
'vars': ansible_vars_dict,
'templates': [str],
'merge-mode': vs.Any('merge', 'merge-resolve',
'cherry-pick'),
'cherry-pick', 'squash-merge'),
'default-branch': str,
str: pipeline_contents,
'_source_context': model.SourceContext,

View File

@ -17,7 +17,8 @@ import voluptuous as v
import time
from zuul.lib.logutil import get_annotated_logger
from zuul.model import MERGER_MERGE_RESOLVE, MERGER_MERGE, MERGER_MAP
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,6 +35,7 @@ class GithubReporter(BaseReporter):
merge_modes = {
MERGER_MERGE: 'merge',
MERGER_MERGE_RESOLVE: 'merge',
MERGER_SQUASH_MERGE: 'squash',
}
def __init__(self, driver, connection, pipeline, config=None):

View File

@ -420,6 +420,18 @@ class Repo(object):
repo.git.merge(*args)
return repo.head.commit
def squash_merge(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']
ref = item['ref']
self.fetch(ref, zuul_event_id=zuul_event_id)
log.debug("Squash-Merging %s with args %s", ref, args)
repo.git.merge(*args)
repo.index.commit(
'Merge change %s,%s' % (item['number'], item['patchset']))
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
@ -676,6 +688,9 @@ class Merger(object):
elif mode == zuul.model.MERGER_CHERRY_PICK:
commit = repo.cherryPick(item['ref'],
zuul_event_id=zuul_event_id)
elif mode == zuul.model.MERGER_SQUASH_MERGE:
commit = repo.squash_merge(
item, zuul_event_id=zuul_event_id)
else:
raise Exception("Unsupported merge mode: %s" % mode)
except git.GitCommandError:

View File

@ -35,11 +35,13 @@ from zuul.lib.logutil import get_annotated_logger
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_MAP = {
'merge': MERGER_MERGE,
'merge-resolve': MERGER_MERGE_RESOLVE,
'cherry-pick': MERGER_CHERRY_PICK,
'squash-merge': MERGER_SQUASH_MERGE,
}
PRECEDENCE_NORMAL = 0