Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 29 additions & 20 deletions src/github_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re

import boto3
from github import Github
from github import Github, GithubException

import config
import lambdalogging
Expand Down Expand Up @@ -33,10 +33,6 @@
class GithubProxy:
"""Encapsulate interactions with Github."""

def __init__(self):
"""Initialize proxy."""
pass

def publish_pr_comment(self, build):
"""Publish PR comment with link to build logs."""
pr_comment = PR_COMMENT_TEMPLATE.format(
Expand All @@ -46,22 +42,35 @@ def publish_pr_comment(self, build):
logs_url=build.get_logs_url(),
)

# initialize client before logging to ensure GitHub attributes are populated
gh_client = self._get_client()
LOG.debug('Publishing PR Comment: repo=%s/%s, pr_id=%s, comment=%s',
self._github_owner, self._github_repo, build.get_pr_id(), pr_comment)

repo = gh_client.get_user(self._github_owner).get_repo(self._github_repo)
repo.get_pull(build.get_pr_id()).create_issue_comment(pr_comment)

def _get_client(self):
if not hasattr(self, '_client'):
self._init_client()
return self._client

def _init_client(self):
# initialize client to obtain user info if first run
_ = self._github_client
attempt = 1
while attempt <= 2:
try:
LOG.debug('Publishing PR Comment: repo=%s/%s, pr_id=%s, comment=%s',
self._github_owner, self._github_repo, build.get_pr_id(), pr_comment)

repo = self._github_client.get_user(self._github_owner).get_repo(self._github_repo)
repo.get_pull(build.get_pr_id()).create_issue_comment(pr_comment)
except GithubException:
self._refresh_github_client()
attempt += 1
else:
return

raise PermissionError("Unable to authenticate to GitHub with available credentials!")

def _refresh_github_client(self):
self._init_github_info()
self._client = Github(self._github_token)
self.__github_client = Github(self._github_token)
return self.__github_client

@property
def _github_client(self):
try:
return self.__github_client
except AttributeError:
return self._refresh_github_client()

def _init_github_info(self):
response = CODEBUILD.batch_get_projects(
Expand Down
31 changes: 29 additions & 2 deletions test/unit/test_github_proxy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import pytest
from unittest.mock import MagicMock
from unittest.mock import MagicMock, Mock

from github import BadCredentialsException

import github_proxy
import test_constants
Expand Down Expand Up @@ -61,11 +63,27 @@ def mock_github(mocker):
return github_proxy.Github.return_value


def test_publish_pr_comment(mocker, mock_config, mock_codebuild, mock_secretsmanager, mock_github):
@pytest.fixture
def mock_renewing_github(mocker):
mocker.patch.object(github_proxy, 'Github')

mock_failure = Mock()
mock_failure.get_user.side_effect = BadCredentialsException(status='foo', data='bar')

github_proxy.Github.side_effect = (mock_failure, Mock())
return github_proxy.Github


def _mock_build():
build = MagicMock(status=BUILD_STATUS)
build.get_logs_url.return_value = LOGS_URL
build.get_pr_id.return_value = PR_ID
build.commit_id = COMMIT_ID
return build


def test_publish_pr_comment(mocker, mock_config, mock_codebuild, mock_secretsmanager, mock_github):
build = _mock_build()

proxy = github_proxy.GithubProxy()
proxy.publish_pr_comment(build)
Expand All @@ -91,6 +109,15 @@ def test_publish_pr_comment(mocker, mock_config, mock_codebuild, mock_secretsman
mock_pr.create_issue_comment.assert_called_once_with(expected_comment)


def test_public_pr_comment_renew_credentials(mock_config, mock_codebuild, mock_secretsmanager, mock_renewing_github):
build = _mock_build()

proxy = github_proxy.GithubProxy()
proxy.publish_pr_comment(build)

assert mock_renewing_github.call_count == 2


def test_init_github_info_auth_with_secrets_manager_arn(mocker, mock_config, mock_codebuild, mock_secretsmanager):
secret_arn = 'arn:secret'
mock_config.configure_mock(GITHUB_OAUTH_TOKEN_SECRET_ARN=secret_arn)
Expand Down