commit eed5820fb3269099d1ef5bd0d50a96edee52bd47 Author: Simon Westphahl Date: Mon Sep 21 09:54:58 2020 +0200 Optimize GitHub requests on PR merge Using github3py for merging a pull request there is always one extra request to get the pull request before the merge can be done due to the github3py data model. Since merging is done synchronously in in the scheduler main loop, it makes sense to save the extra API request by building and executing the merge call directly. Change-Id: I48dbb5fc1b7e7dec4cb09c779b9661d82935d9df diff --git a/tests/fakegithub.py b/tests/fakegithub.py index 89b3ec7..6ab5dab 100644 --- a/tests/fakegithub.py +++ b/tests/fakegithub.py @@ -506,24 +506,6 @@ class FakePull(object): # with the head_sha as the only commit 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.github_data.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): pr = self._fake_pull_request connection = pr.github @@ -654,6 +636,31 @@ class FakeGithubSession(object): return FakeResponse(None, 404) + def put(self, url, data=None, headers=None, params=None, json=None): + # Handle pull request merge + match = re.match(r'.+/repos/(.+)/pulls/(\d+)/merge$', url) + if match: + project, pr_number = match.groups() + project = urllib.parse.unquote(project) + pr = self.client._data.pull_requests[int(pr_number)] + conn = pr.github + + # record that this got reported + self.client._data.reports.append( + (pr.project, pr.number, 'merge', json["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(json["commit_message"]) + return FakeResponse({"merged": True}, 200) + + return FakeResponse(None, 404) + def get_repo(self, request, params=None): org, project, request = request.split('/', 2) project_name = '{}/{}'.format(org, project) diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py index 822659b..594fe5a 100644 --- a/zuul/driver/github/githubconnection.py +++ b/zuul/driver/github/githubconnection.py @@ -1856,17 +1856,31 @@ class GithubConnection(BaseConnection): method='merge', zuul_event_id=None): log = get_annotated_logger(self.log, zuul_event_id) github = self.getGithubClient(project, zuul_event_id=zuul_event_id) - owner, proj = project.split('/') - pull_request = github.pull_request(owner, proj, pr_number) + url = github.session.build_url("repos", project, "pulls", + str(pr_number), "merge") + + payload = { + "merge_method": method, + } + if sha: + payload["sha"] = sha + if commit_message: + payload["commit_message"] = commit_message + try: - result = pull_request.merge(commit_message=commit_message, sha=sha, - merge_method=method) + resp = github.session.put(url, json=payload) + resp.raise_for_status() + data = resp.json() + if not data: + result = False + else: + result = data["merged"] except Exception as e: raise MergeFailure('Pull request merge failed: %s' % e) if not result: raise MergeFailure('Pull request was not merged') - log.debug("Merged PR %s/%s#%s", owner, proj, pr_number) + log.debug("Merged PR %s#%s", project, pr_number) def _getCommit(self, repository, sha, retries=5): try: