Merge "Merge pull requests from github reporter" into feature/zuulv3
This commit is contained in:
@@ -50,6 +50,11 @@ reporter. It has the following options:
|
||||
to ``true``.
|
||||
``comment: false``
|
||||
|
||||
**merge**
|
||||
Boolean value (``true`` or ``false``) that determines if the reporter should
|
||||
merge the pull reqeust. Defaults to ``false``.
|
||||
``merge=true``
|
||||
|
||||
SMTP
|
||||
----
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pbr>=1.1.0
|
||||
|
||||
Github3.py
|
||||
Github3.py==1.0.0a2
|
||||
PyYAML>=3.1.0
|
||||
Paste
|
||||
WebOb>=1.2.3
|
||||
|
||||
@@ -70,6 +70,7 @@ import zuul.merger.merger
|
||||
import zuul.merger.server
|
||||
import zuul.nodepool
|
||||
import zuul.zk
|
||||
from zuul.exceptions import MergeFailure
|
||||
|
||||
FIXTURE_DIR = os.path.join(os.path.dirname(__file__),
|
||||
'fixtures')
|
||||
@@ -559,6 +560,7 @@ class FakeGithubPullRequest(object):
|
||||
self.statuses = {}
|
||||
self.updated_at = None
|
||||
self.head_sha = None
|
||||
self.is_merged = False
|
||||
self._createPRRef()
|
||||
self._addCommitToRepo()
|
||||
self._updateTimeStamp()
|
||||
@@ -693,6 +695,8 @@ class FakeGithubConnection(githubconnection.GithubConnection):
|
||||
self.pr_number = 0
|
||||
self.pull_requests = []
|
||||
self.upstream_root = upstream_root
|
||||
self.merge_failure = False
|
||||
self.merge_not_allowed_count = 0
|
||||
|
||||
def openFakePullRequest(self, project, branch):
|
||||
self.pr_number += 1
|
||||
@@ -762,6 +766,16 @@ class FakeGithubConnection(githubconnection.GithubConnection):
|
||||
pull_request = self.pull_requests[pr_number - 1]
|
||||
pull_request.addComment(message)
|
||||
|
||||
def mergePull(self, project, pr_number, sha=None):
|
||||
pull_request = self.pull_requests[pr_number - 1]
|
||||
if self.merge_failure:
|
||||
raise Exception('Pull request was not merged')
|
||||
if self.merge_not_allowed_count > 0:
|
||||
self.merge_not_allowed_count -= 1
|
||||
raise MergeFailure('Merge was not successful due to mergeability'
|
||||
' conflict')
|
||||
pull_request.is_merged = True
|
||||
|
||||
def setCommitStatus(self, project, sha, state,
|
||||
url='', description='', context=''):
|
||||
owner, proj = project.split('/')
|
||||
|
||||
19
tests/fixtures/layouts/merging-github.yaml
vendored
Normal file
19
tests/fixtures/layouts/merging-github.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
- pipeline:
|
||||
name: merge
|
||||
description: Pipeline for merging the pull request
|
||||
manager: independent
|
||||
trigger:
|
||||
github:
|
||||
- event: pull_request
|
||||
action: comment
|
||||
comment: 'merge me'
|
||||
success:
|
||||
github:
|
||||
merge: true
|
||||
comment: false
|
||||
|
||||
- project:
|
||||
name: org/project
|
||||
merge:
|
||||
jobs:
|
||||
- noop
|
||||
@@ -165,3 +165,35 @@ class TestGithubDriver(ZuulTestCase):
|
||||
self.waitUntilSettled()
|
||||
self.assertNotIn('reporting', pr.statuses)
|
||||
self.assertEqual(2, len(pr.comments))
|
||||
|
||||
@simple_layout('layouts/merging-github.yaml', driver='github')
|
||||
def test_report_pull_merge(self):
|
||||
# pipeline merges the pull request on success
|
||||
A = self.fake_github.openFakePullRequest('org/project', 'master')
|
||||
self.fake_github.emitEvent(A.getCommentAddedEvent('merge me'))
|
||||
self.waitUntilSettled()
|
||||
self.assertTrue(A.is_merged)
|
||||
|
||||
# pipeline merges the pull request on success after failure
|
||||
self.fake_github.merge_failure = True
|
||||
B = self.fake_github.openFakePullRequest('org/project', 'master')
|
||||
self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
|
||||
self.waitUntilSettled()
|
||||
self.assertFalse(B.is_merged)
|
||||
self.fake_github.merge_failure = False
|
||||
|
||||
# pipeline merges the pull request on second run of merge
|
||||
# first merge failed on 405 Method Not Allowed error
|
||||
self.fake_github.merge_not_allowed_count = 1
|
||||
C = self.fake_github.openFakePullRequest('org/project', 'master')
|
||||
self.fake_github.emitEvent(C.getCommentAddedEvent('merge me'))
|
||||
self.waitUntilSettled()
|
||||
self.assertTrue(C.is_merged)
|
||||
|
||||
# pipeline does not merge the pull request
|
||||
# merge failed on 405 Method Not Allowed error - twice
|
||||
self.fake_github.merge_not_allowed_count = 2
|
||||
D = self.fake_github.openFakePullRequest('org/project', 'master')
|
||||
self.fake_github.emitEvent(D.getCommentAddedEvent('merge me'))
|
||||
self.waitUntilSettled()
|
||||
self.assertFalse(D.is_merged)
|
||||
|
||||
@@ -21,9 +21,11 @@ import webob
|
||||
import webob.dec
|
||||
import voluptuous as v
|
||||
import github3
|
||||
from github3.exceptions import MethodNotAllowed
|
||||
|
||||
from zuul.connection import BaseConnection
|
||||
from zuul.model import PullRequest, Ref, TriggerEvent
|
||||
from zuul.exceptions import MergeFailure
|
||||
|
||||
|
||||
class GithubWebhookListener():
|
||||
@@ -283,6 +285,17 @@ class GithubConnection(BaseConnection):
|
||||
pull_request = repository.issue(pr_number)
|
||||
pull_request.create_comment(message)
|
||||
|
||||
def mergePull(self, project, pr_number, sha=None):
|
||||
owner, proj = project.split('/')
|
||||
pull_request = self.github.pull_request(owner, proj, pr_number)
|
||||
try:
|
||||
result = pull_request.merge(sha=sha)
|
||||
except MethodNotAllowed as e:
|
||||
raise MergeFailure('Merge was not successful due to mergeability'
|
||||
' conflict, original error is %s' % e)
|
||||
if not result:
|
||||
raise Exception('Pull request was not merged')
|
||||
|
||||
def setCommitStatus(self, project, sha, state, url='', description='',
|
||||
context=''):
|
||||
owner, proj = project.split('/')
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
|
||||
import logging
|
||||
import voluptuous as v
|
||||
import time
|
||||
|
||||
from zuul.reporter import BaseReporter
|
||||
from zuul.exceptions import MergeFailure
|
||||
|
||||
|
||||
class GithubReporter(BaseReporter):
|
||||
@@ -28,6 +30,7 @@ class GithubReporter(BaseReporter):
|
||||
super(GithubReporter, self).__init__(driver, connection, config)
|
||||
self._commit_status = self.config.get('status', None)
|
||||
self._create_comment = self.config.get('comment', True)
|
||||
self._merge = self.config.get('merge', False)
|
||||
|
||||
def report(self, source, pipeline, item):
|
||||
"""Comment on PR and set commit status."""
|
||||
@@ -37,6 +40,9 @@ class GithubReporter(BaseReporter):
|
||||
hasattr(item.change, 'patchset') and
|
||||
item.change.patchset is not None):
|
||||
self.setPullStatus(pipeline, item)
|
||||
if (self._merge and
|
||||
hasattr(item.change, 'number')):
|
||||
self.mergePull(item)
|
||||
|
||||
def addPullComment(self, pipeline, item):
|
||||
message = self._formatItemReport(pipeline, item)
|
||||
@@ -68,10 +74,25 @@ class GithubReporter(BaseReporter):
|
||||
self.connection.setCommitStatus(
|
||||
project, sha, state, url, description, context)
|
||||
|
||||
def mergePull(self, item):
|
||||
project = item.change.project.name
|
||||
pr_number = item.change.number
|
||||
sha = item.change.patchset
|
||||
self.log.debug('Reporting change %s, params %s, merging via API' %
|
||||
(item.change, self.config))
|
||||
try:
|
||||
self.connection.mergePull(project, pr_number, sha)
|
||||
except MergeFailure:
|
||||
time.sleep(2)
|
||||
self.log.debug('Trying to merge change %s again...' % item.change)
|
||||
self.connection.mergePull(project, pr_number, sha)
|
||||
item.change.is_merged = True
|
||||
|
||||
|
||||
def getSchema():
|
||||
github_reporter = v.Schema({
|
||||
'status': v.Any('pending', 'success', 'failure'),
|
||||
'comment': bool
|
||||
'comment': bool,
|
||||
'merge': bool
|
||||
})
|
||||
return github_reporter
|
||||
|
||||
Reference in New Issue
Block a user