diff --git a/tests/fakegithub.py b/tests/fakegithub.py index 91245b3ce2..3a47ef7368 100644 --- a/tests/fakegithub.py +++ b/tests/fakegithub.py @@ -17,6 +17,7 @@ import urllib from collections import defaultdict import datetime + import github3.exceptions import re import time @@ -87,11 +88,12 @@ class FakeApp: class FakeCheckRun(object): - def __init__(self, name, details_url, output, status, conclusion, + def __init__(self, id, name, details_url, output, status, conclusion, completed_at, external_id, actions, app): if actions is None: actions = [] + self.id = id self.name = name self.details_url = details_url self.output = output @@ -110,6 +112,7 @@ class FakeCheckRun(object): def as_dict(self): return { + 'id': self.id, "name": self.name, "status": self.status, "output": self.output, @@ -166,9 +169,10 @@ class FakeCommit(object): # the last status provided for a commit. self._statuses.insert(0, status) - def set_check_run(self, name, details_url, output, status, conclusion, + def set_check_run(self, id, name, details_url, output, status, conclusion, completed_at, external_id, actions, app): check_run = FakeCheckRun( + id, name, details_url, output, @@ -182,6 +186,7 @@ class FakeCommit(object): # Always insert a check_run to the front of the list to represent the # last check_run provided for a commit. self._check_runs.insert(0, check_run) + return check_run def get_url(self, path, params=None): if path == 'statuses': @@ -219,6 +224,7 @@ class FakeRepository(object): self._commits = {} self.data = data self.name = name + self.check_run_counter = 0 # Simple dictionary to store permission values per feature (e.g. # checks, Repository contents, Pull requests, Commit statuses, ...). @@ -303,7 +309,9 @@ class FakeRepository(object): if commit is None: commit = FakeCommit(head_sha) self._commits[head_sha] = commit + self.check_run_counter += 1 commit.set_check_run( + str(self.check_run_counter), name, details_url, output, @@ -568,9 +576,11 @@ class FakeResponse(object): def raise_for_status(self): if 400 <= self.status_code < 600: - raise HTTPError( - '{} {}'.format(self.status_code, self.status_message), - response=self) + if isinstance(self.data, str): + text = '{} {}'.format(self.status_code, self.data) + else: + text = '{} {}'.format(self.status_code, self.status_message) + raise HTTPError(text, response=self) class FakeGithubSession(object): @@ -637,6 +647,39 @@ class FakeGithubSession(object): } return FakeResponse(data, 201) + # Handle check run creation + match = re.match(r'.*/repos/(.*)/check-runs$', url) + if match: + org, reponame = match.groups()[0].split('/', 1) + repo = self.client._data.repos.get((org, reponame)) + + if repo._permissions.get("checks") is False: + # To create a proper github3 exception, we need to mock a + # response object + return FakeResponse( + "Resource not accessible by integration", 403) + + head_sha = json.get('head_sha') + commit = repo._commits.get(head_sha, None) + if commit is None: + commit = FakeCommit(head_sha) + repo._commits[head_sha] = commit + repo.check_run_counter += 1 + check_run = commit.set_check_run( + str(repo.check_run_counter), + json['name'], + json['details_url'], + json['output'], + json.get('status'), + json.get('conclusion'), + json.get('completed_at'), + json['external_id'], + json['actions'], + json.get('app', 'zuul'), + ) + + return FakeResponse(check_run.as_dict(), 201) + return FakeResponse(None, 404) def put(self, url, data=None, headers=None, params=None, json=None): diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py index 50433ef518..1ef5f9a8da 100644 --- a/zuul/driver/github/githubconnection.py +++ b/zuul/driver/github/githubconnection.py @@ -1993,8 +1993,8 @@ class GithubConnection(BaseConnection): zuul_event_id=None): log = get_annotated_logger(self.log, zuul_event_id) github = self.getGithubClient(project, zuul_event_id=zuul_event_id) + self._append_accept_header(github, PREVIEW_CHECKS_ACCEPT) owner, proj = project.split("/") - repository = github.repository(owner, proj) # Always provide an empty list of actions by default actions = [] @@ -2045,6 +2045,8 @@ class GithubConnection(BaseConnection): # change (e.g. the check_run creation failed), there should already # be a check_run available. If not we will create one. check_runs = [] + + repository = github.repository(owner, proj) try: check_runs = [ c for c in repository.commit(sha).check_runs() @@ -2132,7 +2134,7 @@ class GithubConnection(BaseConnection): # Report the start of a check run try: - check_run = repository.create_check_run( + data = dict( name=context, head_sha=sha, status=status, @@ -2141,8 +2143,13 @@ class GithubConnection(BaseConnection): external_id=external_id, actions=actions, ) - except github3.exceptions.GitHubException as exc: - # TODO (felix): Should we retry the check run creation? + + url = github.session.build_url('repos', project, + 'check-runs') + resp = github.session.post(url, json=data) + resp.raise_for_status() + + except requests.exceptions.HTTPError as exc: log.error( "Failed to create check run %s for %s#%s on sha %s: %s", context, project, pr_number, sha, str(exc),