Fix multiple prs found when commit is not head
When receiving a status or checks event we need to find the corresponding pull request to that event. Github stores the status on the head commit of a pr and not on the pr itself which leads to problems if multiple prs exist with the same head but different target branches. Therefore zuul errors out when more than one pr is found for that event. However the github search also returns all prs that contain the sha we search for but have a different head which leads to the same error as if we had two conflicting prs. This can be solved by delaying the error and filtering the pr bodies for the head sha first. Change-Id: Iadafd3cf68a742941e9189e84fca594bc3394084
This commit is contained in:
parent
a0de74bef8
commit
0093aabd4e
|
@ -1909,7 +1909,8 @@ class FakeGithubPullRequest(object):
|
||||||
|
|
||||||
def __init__(self, github, number, project, branch,
|
def __init__(self, github, number, project, branch,
|
||||||
subject, upstream_root, files=[], number_of_commits=1,
|
subject, upstream_root, files=[], number_of_commits=1,
|
||||||
writers=[], body=None, body_text=None, draft=False):
|
writers=[], body=None, body_text=None, draft=False,
|
||||||
|
base_sha=None):
|
||||||
"""Creates a new PR with several commits.
|
"""Creates a new PR with several commits.
|
||||||
Sends an event about opened PR."""
|
Sends an event about opened PR."""
|
||||||
self.github = github
|
self.github = github
|
||||||
|
@ -1936,7 +1937,7 @@ class FakeGithubPullRequest(object):
|
||||||
self.merge_message = None
|
self.merge_message = None
|
||||||
self.state = 'open'
|
self.state = 'open'
|
||||||
self.url = 'https://%s/%s/pull/%s' % (github.server, project, number)
|
self.url = 'https://%s/%s/pull/%s' % (github.server, project, number)
|
||||||
self._createPRRef()
|
self._createPRRef(base_sha=base_sha)
|
||||||
self._addCommitToRepo(files=files)
|
self._addCommitToRepo(files=files)
|
||||||
self._updateTimeStamp()
|
self._updateTimeStamp()
|
||||||
|
|
||||||
|
@ -2103,10 +2104,11 @@ class FakeGithubPullRequest(object):
|
||||||
repo_path = os.path.join(self.upstream_root, self.project)
|
repo_path = os.path.join(self.upstream_root, self.project)
|
||||||
return git.Repo(repo_path)
|
return git.Repo(repo_path)
|
||||||
|
|
||||||
def _createPRRef(self):
|
def _createPRRef(self, base_sha=None):
|
||||||
|
base_sha = base_sha or 'refs/tags/init'
|
||||||
repo = self._getRepo()
|
repo = self._getRepo()
|
||||||
GithubChangeReference.create(
|
GithubChangeReference.create(
|
||||||
repo, self.getPRReference(), 'refs/tags/init')
|
repo, self.getPRReference(), base_sha)
|
||||||
|
|
||||||
def _addCommitToRepo(self, files=[], reset=False):
|
def _addCommitToRepo(self, files=[], reset=False):
|
||||||
repo = self._getRepo()
|
repo = self._getRepo()
|
||||||
|
@ -2354,11 +2356,13 @@ class FakeGithubConnection(githubconnection.GithubConnection):
|
||||||
self.zuul_web_port = port
|
self.zuul_web_port = port
|
||||||
|
|
||||||
def openFakePullRequest(self, project, branch, subject, files=[],
|
def openFakePullRequest(self, project, branch, subject, files=[],
|
||||||
body=None, body_text=None, draft=False):
|
body=None, body_text=None, draft=False,
|
||||||
|
base_sha=None):
|
||||||
self.pr_number += 1
|
self.pr_number += 1
|
||||||
pull_request = FakeGithubPullRequest(
|
pull_request = FakeGithubPullRequest(
|
||||||
self, self.pr_number, project, branch, subject, self.upstream_root,
|
self, self.pr_number, project, branch, subject, self.upstream_root,
|
||||||
files=files, body=body, body_text=body_text, draft=draft)
|
files=files, body=body, body_text=body_text, draft=draft,
|
||||||
|
base_sha=base_sha)
|
||||||
self.pull_requests[self.pr_number] = pull_request
|
self.pull_requests[self.pr_number] = pull_request
|
||||||
return pull_request
|
return pull_request
|
||||||
|
|
||||||
|
|
|
@ -687,9 +687,24 @@ class FakeGithubClient(object):
|
||||||
return re.match(r'[a-z0-9]{40}', s)
|
return re.match(r'[a-z0-9]{40}', s)
|
||||||
|
|
||||||
if query_is_sha(query):
|
if query_is_sha(query):
|
||||||
return (FakeIssueSearchResult(FakeIssue(pr))
|
# Github returns all PR's that contain the sha in their history
|
||||||
for pr in self._data.pull_requests.values()
|
result = []
|
||||||
if pr.head_sha == query)
|
for pr in self._data.pull_requests.values():
|
||||||
|
# Quick check if head sha matches
|
||||||
|
if pr.head_sha == query:
|
||||||
|
result.append(FakeIssueSearchResult(FakeIssue(pr)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If head sha doesn't match it still could be in the pr history
|
||||||
|
repo = pr._getRepo()
|
||||||
|
commits = repo.iter_commits(
|
||||||
|
'%s...%s' % (pr.branch, pr.head_sha))
|
||||||
|
for commit in commits:
|
||||||
|
if commit.hexsha == query:
|
||||||
|
result.append(FakeIssueSearchResult(FakeIssue(pr)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
# Non-SHA queries are of the form:
|
# Non-SHA queries are of the form:
|
||||||
#
|
#
|
||||||
|
|
|
@ -92,6 +92,11 @@ class TestGithubRequirements(ZuulTestCase):
|
||||||
project = 'org/project2'
|
project = 'org/project2'
|
||||||
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
|
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
|
||||||
|
|
||||||
|
# Create second PR which contains the head of A in its history. Zuul
|
||||||
|
# should not get disturbed by the existence of this one.
|
||||||
|
self.fake_github.openFakePullRequest(
|
||||||
|
project, 'master', 'A', base_sha=A.head_sha)
|
||||||
|
|
||||||
# An error status should not cause it to be enqueued
|
# An error status should not cause it to be enqueued
|
||||||
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
|
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
|
||||||
context='tenant-one/check')
|
context='tenant-one/check')
|
||||||
|
|
|
@ -1660,16 +1660,25 @@ class GithubConnection(BaseConnection):
|
||||||
issues = list(github.search_issues(sha))
|
issues = list(github.search_issues(sha))
|
||||||
|
|
||||||
log.debug('Got PR on project %s for sha %s', project_name, sha)
|
log.debug('Got PR on project %s for sha %s', project_name, sha)
|
||||||
if len(issues) > 1:
|
|
||||||
raise Exception('Multiple pulls found with head sha %s' % sha)
|
|
||||||
|
|
||||||
if len(issues) == 0:
|
if len(issues) == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
pr_body = self._getChange(
|
# Github returns all issues that contain the sha, not only the ones
|
||||||
project, issues.pop().issue.number, sha, event=event).pr
|
# with that sha as head_sha so we need to get and update all those
|
||||||
self._sha_pr_cache.update(project_name, pr_body)
|
# changes and then filter for the head sha before we can error out
|
||||||
return pr_body
|
# with multiple pulls found.
|
||||||
|
found_pr_body = None
|
||||||
|
for item in issues:
|
||||||
|
pr_body = self._getChange(
|
||||||
|
project, item.issue.number, sha, event=event).pr
|
||||||
|
self._sha_pr_cache.update(project_name, pr_body)
|
||||||
|
if pr_body['head']['sha'] == sha:
|
||||||
|
if found_pr_body:
|
||||||
|
raise Exception(
|
||||||
|
'Multiple pulls found with head sha %s' % sha)
|
||||||
|
found_pr_body = pr_body
|
||||||
|
|
||||||
|
return found_pr_body
|
||||||
|
|
||||||
def getPullReviews(self, pr_obj, project, number, event):
|
def getPullReviews(self, pr_obj, project, number, event):
|
||||||
log = get_annotated_logger(self.log, event)
|
log = get_annotated_logger(self.log, event)
|
||||||
|
|
Loading…
Reference in New Issue