Merge "Improve error reporting when pr merge fails"
This commit is contained in:
commit
89e42edeb3
|
@ -87,7 +87,6 @@ import zuul.nodepool
|
||||||
import zuul.rpcclient
|
import zuul.rpcclient
|
||||||
import zuul.zk
|
import zuul.zk
|
||||||
import zuul.configloader
|
import zuul.configloader
|
||||||
from zuul.exceptions import MergeFailure
|
|
||||||
from zuul.lib.config import get_default
|
from zuul.lib.config import get_default
|
||||||
from zuul.lib.logutil import get_annotated_logger
|
from zuul.lib.logutil import get_annotated_logger
|
||||||
|
|
||||||
|
@ -2252,19 +2251,6 @@ class FakeGithubConnection(githubconnection.GithubConnection):
|
||||||
pull_request = self.pull_requests[int(pr_number)]
|
pull_request = self.pull_requests[int(pr_number)]
|
||||||
pull_request.addComment(message)
|
pull_request.addComment(message)
|
||||||
|
|
||||||
def mergePull(self, project, pr_number, commit_message='', sha=None,
|
|
||||||
method='merge', zuul_event_id=None):
|
|
||||||
# record that this got reported
|
|
||||||
self.reports.append((project, pr_number, 'merge', method))
|
|
||||||
pull_request = self.pull_requests[int(pr_number)]
|
|
||||||
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.setMerged(commit_message)
|
|
||||||
|
|
||||||
def setCommitStatus(self, project, sha, state, url='', description='',
|
def setCommitStatus(self, project, sha, state, url='', description='',
|
||||||
context='default', user='zuul', zuul_event_id=None):
|
context='default', user='zuul', zuul_event_id=None):
|
||||||
# record that this got reported and call original method
|
# record that this got reported and call original method
|
||||||
|
|
|
@ -28,6 +28,16 @@ from tests.fake_graphql import FakeGithubQuery
|
||||||
FAKE_BASE_URL = 'https://example.com/api/v3/'
|
FAKE_BASE_URL = 'https://example.com/api/v3/'
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorResponse:
|
||||||
|
status_code = 0
|
||||||
|
message = ''
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
return {
|
||||||
|
'message': self.message
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class FakeUser(object):
|
class FakeUser(object):
|
||||||
def __init__(self, login):
|
def __init__(self, login):
|
||||||
self.login = login
|
self.login = login
|
||||||
|
@ -276,16 +286,7 @@ class FakeRepository(object):
|
||||||
if self.fail_not_found > 0:
|
if self.fail_not_found > 0:
|
||||||
self.fail_not_found -= 1
|
self.fail_not_found -= 1
|
||||||
|
|
||||||
class Response:
|
resp = ErrorResponse()
|
||||||
status_code = 0
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
def json(self):
|
|
||||||
return {
|
|
||||||
'message': self.message
|
|
||||||
}
|
|
||||||
|
|
||||||
resp = Response()
|
|
||||||
resp.status_code = 404
|
resp.status_code = 404
|
||||||
resp.message = 'Not Found'
|
resp.message = 'Not Found'
|
||||||
|
|
||||||
|
@ -472,6 +473,23 @@ class FakePull(object):
|
||||||
# with the head_sha as the only commit
|
# with the head_sha as the only commit
|
||||||
return [self.head]
|
return [self.head]
|
||||||
|
|
||||||
|
def merge(self, commit_message=None, sha=None, merge_method=None):
|
||||||
|
conn = self._fake_pull_request.github
|
||||||
|
pr = self._fake_pull_request
|
||||||
|
|
||||||
|
# record that this got reported
|
||||||
|
conn.reports.append((pr.project, pr.number, 'merge', merge_method))
|
||||||
|
if conn.merge_failure:
|
||||||
|
raise Exception('Unknown merge failure')
|
||||||
|
if conn.merge_not_allowed_count > 0:
|
||||||
|
conn.merge_not_allowed_count -= 1
|
||||||
|
resp = ErrorResponse()
|
||||||
|
resp.status_code = 403
|
||||||
|
resp.message = 'Merge not allowed'
|
||||||
|
raise github3.exceptions.MethodNotAllowed(resp)
|
||||||
|
pr.setMerged(commit_message)
|
||||||
|
return True
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
pr = self._fake_pull_request
|
pr = self._fake_pull_request
|
||||||
connection = pr.github
|
connection = pr.github
|
||||||
|
|
|
@ -620,6 +620,7 @@ class TestGithubDriver(ZuulTestCase):
|
||||||
self.assertTrue(A.is_merged)
|
self.assertTrue(A.is_merged)
|
||||||
self.assertThat(A.merge_message,
|
self.assertThat(A.merge_message,
|
||||||
MatchesRegex(r'.*PR title.*Reviewed-by.*', re.DOTALL))
|
MatchesRegex(r'.*PR title.*Reviewed-by.*', re.DOTALL))
|
||||||
|
self.assertEqual(len(A.comments), 0)
|
||||||
|
|
||||||
# pipeline merges the pull request on success after failure
|
# pipeline merges the pull request on success after failure
|
||||||
self.fake_github.merge_failure = True
|
self.fake_github.merge_failure = True
|
||||||
|
@ -627,6 +628,9 @@ class TestGithubDriver(ZuulTestCase):
|
||||||
self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
|
self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
|
||||||
self.waitUntilSettled()
|
self.waitUntilSettled()
|
||||||
self.assertFalse(B.is_merged)
|
self.assertFalse(B.is_merged)
|
||||||
|
self.assertEqual(len(B.comments), 1)
|
||||||
|
self.assertEqual(B.comments[0],
|
||||||
|
'Pull request merge failed: Unknown merge failure')
|
||||||
self.fake_github.merge_failure = False
|
self.fake_github.merge_failure = False
|
||||||
|
|
||||||
# pipeline merges the pull request on second run of merge
|
# pipeline merges the pull request on second run of merge
|
||||||
|
@ -645,7 +649,10 @@ class TestGithubDriver(ZuulTestCase):
|
||||||
self.waitUntilSettled()
|
self.waitUntilSettled()
|
||||||
self.assertFalse(D.is_merged)
|
self.assertFalse(D.is_merged)
|
||||||
self.assertEqual(len(D.comments), 1)
|
self.assertEqual(len(D.comments), 1)
|
||||||
self.assertEqual(D.comments[0], 'Merge failed')
|
# Validate that the merge failure comment contains the message github
|
||||||
|
# returned
|
||||||
|
self.assertEqual(D.comments[0],
|
||||||
|
'Pull request merge failed: 403 Merge not allowed')
|
||||||
|
|
||||||
@simple_layout('layouts/dependent-github.yaml', driver='github')
|
@simple_layout('layouts/dependent-github.yaml', driver='github')
|
||||||
def test_draft_pr(self):
|
def test_draft_pr(self):
|
||||||
|
@ -1173,9 +1180,11 @@ class TestGithubDriver(ZuulTestCase):
|
||||||
# the change should have entered the gate
|
# the change should have entered the gate
|
||||||
self.assertEqual(2, len(self.history))
|
self.assertEqual(2, len(self.history))
|
||||||
|
|
||||||
|
# Merge should have failed because cherry-pick is not supported
|
||||||
self.assertEqual(2, len(A.comments))
|
self.assertEqual(2, len(A.comments))
|
||||||
self.assertFalse(A.is_merged)
|
self.assertFalse(A.is_merged)
|
||||||
self.assertIn('Merge Failed', A.comments[1])
|
self.assertEquals(A.comments[1],
|
||||||
|
'Merge mode cherry-pick not supported by Github')
|
||||||
|
|
||||||
@simple_layout('layouts/gate-github-squash-merge.yaml', driver='github')
|
@simple_layout('layouts/gate-github-squash-merge.yaml', driver='github')
|
||||||
def test_merge_method_squash_merge(self):
|
def test_merge_method_squash_merge(self):
|
||||||
|
|
|
@ -1736,13 +1736,12 @@ class GithubConnection(BaseConnection):
|
||||||
try:
|
try:
|
||||||
result = pull_request.merge(commit_message=commit_message, sha=sha,
|
result = pull_request.merge(commit_message=commit_message, sha=sha,
|
||||||
merge_method=method)
|
merge_method=method)
|
||||||
except github3.exceptions.MethodNotAllowed as e:
|
except Exception as e:
|
||||||
raise MergeFailure('Merge was not successful due to mergeability'
|
raise MergeFailure('Pull request merge failed: %s' % e)
|
||||||
' conflict, original error is %s' % e)
|
|
||||||
|
|
||||||
log.debug("Merged PR %s/%s#%s", owner, proj, pr_number)
|
|
||||||
if not result:
|
if not result:
|
||||||
raise Exception('Pull request was not merged')
|
raise MergeFailure('Pull request was not merged')
|
||||||
|
log.debug("Merged PR %s/%s#%s", owner, proj, pr_number)
|
||||||
|
|
||||||
def _getCommit(self, repository, sha, retries=5):
|
def _getCommit(self, repository, sha, retries=5):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -95,10 +95,10 @@ class GithubReporter(BaseReporter):
|
||||||
if self._create_comment or errors_received:
|
if self._create_comment or errors_received:
|
||||||
self.addPullComment(item)
|
self.addPullComment(item)
|
||||||
if (self._merge):
|
if (self._merge):
|
||||||
self.mergePull(item)
|
try:
|
||||||
if not item.change.is_merged:
|
self.mergePull(item)
|
||||||
msg = self._formatItemReportMergeFailure(item)
|
except Exception as e:
|
||||||
self.addPullComment(item, msg)
|
self.addPullComment(item, str(e))
|
||||||
|
|
||||||
def _formatItemReportJobs(self, item):
|
def _formatItemReportJobs(self, item):
|
||||||
# Return the list of jobs portion of the report
|
# Return the list of jobs portion of the report
|
||||||
|
@ -161,8 +161,7 @@ class GithubReporter(BaseReporter):
|
||||||
if merge_mode not in self.merge_modes:
|
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 MERGER_MAP.items() if x[1] == merge_mode][0]
|
||||||
self.log.warning('Merge mode %s not supported by Github', mode)
|
self.log.warning('Merge mode %s not supported by Github', mode)
|
||||||
# TODO(tobiash): report error to user
|
raise MergeFailure('Merge mode %s not supported by Github' % mode)
|
||||||
return
|
|
||||||
|
|
||||||
merge_mode = self.merge_modes[merge_mode]
|
merge_mode = self.merge_modes[merge_mode]
|
||||||
project = item.change.project.name
|
project = item.change.project.name
|
||||||
|
@ -179,13 +178,15 @@ class GithubReporter(BaseReporter):
|
||||||
zuul_event_id=item.event)
|
zuul_event_id=item.event)
|
||||||
item.change.is_merged = True
|
item.change.is_merged = True
|
||||||
return
|
return
|
||||||
except MergeFailure:
|
except MergeFailure as e:
|
||||||
log.exception('Merge attempt of change %s %s/2 failed.',
|
log.exception('Merge attempt of change %s %s/2 failed.',
|
||||||
item.change, i, exc_info=True)
|
item.change, i, exc_info=True)
|
||||||
|
error_message = str(e)
|
||||||
if i == 1:
|
if i == 1:
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
log.warning('Merge of change %s failed after 2 attempts, giving up',
|
log.warning('Merge of change %s failed after 2 attempts, giving up',
|
||||||
item.change)
|
item.change)
|
||||||
|
raise MergeFailure(error_message)
|
||||||
|
|
||||||
def addReview(self, item):
|
def addReview(self, item):
|
||||||
log = get_annotated_logger(self.log, item.event)
|
log = get_annotated_logger(self.log, item.event)
|
||||||
|
|
Loading…
Reference in New Issue