diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 07574094..d655819a 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -26,6 +26,9 @@ jobs: cache: pip - name: Install dependencies run: pip install -r requirements.txt + - name: Boot compose test service + run: docker compose up --build --detach + working-directory: bert_e/tests/images - name: Install tox run: pip install tox - run: tox -e utests diff --git a/bert_e/bert_e.py b/bert_e/bert_e.py index bd43b8c3..f2a03b76 100644 --- a/bert_e/bert_e.py +++ b/bert_e/bert_e.py @@ -44,20 +44,20 @@ def __init__(self, settings): settings.robot_password, settings.robot_email, settings.github_app_id, - settings.github_private_key, settings.github_installation_id, + settings.github_private_key, ) - self.project_repo = self.client.get_repository( - owner=settings.repository_owner, - slug=settings.repository_slug - ) - settings['use_queue'] = not settings.disable_queues if settings.repository_host == 'bitbucket': self.settings.robot.account_id = self.client.get_user_id() if settings.repository_host == 'github': if settings['tasks']: LOG.warning("Disabling tasks on GitHub repo") settings['tasks'] = [] + self.project_repo = self.client.get_repository( + owner=settings.repository_owner, + slug=settings.repository_slug + ) + settings['use_queue'] = not settings.disable_queues self.git_repo = GitRepository( self.project_repo.git_url, diff --git a/bert_e/git_host/bitbucket/__init__.py b/bert_e/git_host/bitbucket/__init__.py index 24d2a275..6ac13fae 100644 --- a/bert_e/git_host/bitbucket/__init__.py +++ b/bert_e/git_host/bitbucket/__init__.py @@ -51,7 +51,7 @@ def build_filter_query(filters): @factory.api_client('bitbucket') class Client(base.BertESession, base.AbstractClient): - def __init__(self, bitbucket_login, bitbucket_password, bitbucket_mail): + def __init__(self, bitbucket_login, bitbucket_password, bitbucket_mail, *args, **kwargs): super().__init__() headers = { 'Accept': 'application/json', @@ -63,6 +63,8 @@ def __init__(self, bitbucket_login, bitbucket_password, bitbucket_mail): self.email = bitbucket_mail self.headers.update(headers) self.auth = HTTPBasicAuth(bitbucket_login, bitbucket_password) + self.args = args + self.kwargs = kwargs def get_repository(self, slug, owner=None): """Get the repository with the associated owner and slug.""" diff --git a/bert_e/git_host/github/__init__.py b/bert_e/git_host/github/__init__.py index 8ed3ee0f..97d8b030 100644 --- a/bert_e/git_host/github/__init__.py +++ b/bert_e/git_host/github/__init__.py @@ -41,8 +41,9 @@ class Error(base.Error): class Client(base.AbstractClient): def __init__(self, login: str, password: str, email: str, - app_id: int, installation_id: int, private_key: str, - org=None, base_url='https://api.github.com'): + app_id: int | None = None, installation_id: int | None = None, + private_key: str | None = None, org=None, base_url='https://api.github.com', + accept_header="application/vnd.github.v3+json"): rlog = logging.getLogger('requests.packages.urllib3.connectionpool') rlog.setLevel(logging.CRITICAL) @@ -57,6 +58,7 @@ def __init__(self, login: str, password: str, email: str, self.org = org self.base_url = base_url.rstrip('/') self.query_cache = defaultdict(LRUCache) + self.accept_header = accept_header self.session.headers.update(self.headers) @@ -84,8 +86,9 @@ def _get_installation_token(self, ttl_cache=None): url = f'{self.base_url}/app/installations/{self.installation_id}/access_tokens' headers = { 'Authorization': f'Bearer {self._get_jwt()}', - 'Accept': 'application/vnd.github.v3+json', + 'Accept': self.accept_header, } + print(headers) response = self.session.post(url, headers=headers) response.raise_for_status() return response.json()['token'] @@ -99,7 +102,7 @@ def is_app(self): @property def headers(self): headers = { - 'Accept': 'application/vnd.github.v3+json', + 'Accept': self.accept_header, 'User-Agent': 'Bert-E', 'Content-Type': 'application/json', 'From': self.email, diff --git a/bert_e/git_host/mock.py b/bert_e/git_host/mock.py index 4bc3c341..d1c229da 100644 --- a/bert_e/git_host/mock.py +++ b/bert_e/git_host/mock.py @@ -55,11 +55,13 @@ class Error404Response: @api_client('mock') class Client(base.AbstractClient): - def __init__(self, username, password, email): + def __init__(self, username, password, email, *args, **kwargs): self.login = username self.password = password self.auth = self self.email = email + self.args = args + self.kwargs = kwargs def create_repository(self, slug, owner=None, scm='git', is_private=True): diff --git a/bert_e/tests/images/compose.yaml b/bert_e/tests/images/compose.yaml new file mode 100644 index 00000000..ff87d7af --- /dev/null +++ b/bert_e/tests/images/compose.yaml @@ -0,0 +1,6 @@ +--- +services: + github-mock: + build: github-mock + ports: + - 4010:4010 diff --git a/bert_e/tests/images/github-mock/Dockerfile b/bert_e/tests/images/github-mock/Dockerfile new file mode 100644 index 00000000..150c9b56 --- /dev/null +++ b/bert_e/tests/images/github-mock/Dockerfile @@ -0,0 +1,29 @@ +FROM debian:bookworm-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + && rm -rf /var/lib/apt/lists/* + +ENV PRISM_VERSION=v5.3.1 + +RUN curl https://github.com/stoplightio/prism/releases/download/${PRISM_VERSION}/prism-cli-linux \ + -L \ + -o /usr/local/bin/prism \ + && chmod +x /usr/local/bin/prism + +WORKDIR /app + +RUN curl -O -L https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/ghec/ghec.2022-11-28.json + +# There's a misconfiguration in the openapi file, we are going to replace the following strings: +# - "server-statistics-actions.yaml" -> "#/components/schemas/server-statistics-actions" +# - "server-statistics-packages.yaml" -> "#/components/schemas/server-statistics-packages" + +RUN sed -i 's/server-statistics-actions.yaml/#\/components\/schemas\/server-statistics-actions/g' ghec.2022-11-28.json \ + && sed -i 's/server-statistics-packages.yaml/#\/components\/schemas\/server-statistics-packages/g' ghec.2022-11-28.json + +ENTRYPOINT [ "prism" ] + +CMD ["mock", "ghec.2022-11-28.json", "-h", "0.0.0.0"] \ No newline at end of file diff --git a/bert_e/tests/unit/test_github_app_auth.py b/bert_e/tests/unit/test_github_app_auth.py new file mode 100644 index 00000000..ede219fb --- /dev/null +++ b/bert_e/tests/unit/test_github_app_auth.py @@ -0,0 +1,34 @@ +from gettext import install +from bert_e.git_host.github import Client +from pytest import fixture +from cryptography.hazmat.primitives import serialization as crypto_serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.backends import default_backend as crypto_default_backend + +@fixture +def client_app(): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=crypto_default_backend() + ) + return Client( + login='login', + password='password', + email='email@org.com', + app_id=1, + installation_id=1, + private_key=private_key.private_bytes( + crypto_serialization.Encoding.PEM, + crypto_serialization.PrivateFormat.PKCS8, + crypto_serialization.NoEncryption() + ), + base_url="http://localhost:4010", + accept_header="application/json" + ) + +def test_github_auth_app(client_app): + repository = client_app.get_repository('octo-org', 'Hello-World') + pr = repository.get_pull_request(1) + assert pr.id == 1347 + assert client_app.headers['Authorization'].startswith('Bearer ') == True