Skip to content

Commit

Permalink
feat: use GithubAppInstallation in api
Browse files Browse the repository at this point in the history
Uses the gh app installation WITHOUT compromising
the old usage (of `owner.integration_id`).
Deprecated paths are marked so.

These changes don't backfill old data, but start using
the new model with webhooks.

the sample data from tests (the keys) are taken from
[github docs](https://docs.github.com/en/webhooks/webhook-events-and-payloads#installation)
and some amount of empirical data.

depends on: #346
  • Loading branch information
giovanni-guidini committed Jan 19, 2024
1 parent 635ff36 commit 01dfbb9
Show file tree
Hide file tree
Showing 6 changed files with 790 additions and 195 deletions.
40 changes: 36 additions & 4 deletions upload/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
from datetime import timedelta
from json import dumps
from typing import Optional

import jwt
from asgiref.sync import async_to_sync
Expand All @@ -17,7 +18,13 @@
from shared.reports.enums import UploadType
from shared.torngit.exceptions import TorngitClientError, TorngitObjectNotFoundError

from codecov_auth.models import Owner
from codecov_auth.models import (
GITHUB_APP_INSTALLATION_DEFAULT_NAME,
SERVICE_GITHUB,
SERVICE_GITHUB_ENTERPRISE,
GithubAppInstallation,
Owner,
)
from core.models import Commit, CommitNotification, Pull, Repository
from plan.constants import USER_PLAN_REPRESENTATIONS
from reports.models import CommitReport, ReportSession
Expand Down Expand Up @@ -315,20 +322,45 @@ def determine_upload_pr_to_use(upload_params):
return upload_params.get("pr")


def ghapp_installation_id_to_use(repository: Repository) -> Optional[str]:
if (
repository.service != SERVICE_GITHUB
and repository.service != SERVICE_GITHUB_ENTERPRISE
):
return None

gh_app_default_installation: GithubAppInstallation = (
repository.author.github_app_installations.filter(
name=GITHUB_APP_INSTALLATION_DEFAULT_NAME
).first()
)
if (
gh_app_default_installation
and gh_app_default_installation.is_repo_covered_by_integration(repository)
):
return gh_app_default_installation.installation_id
elif repository.using_integration and repository.author.integration_id:
# THIS FLOW IS DEPRECATED
# it will (hopefully) be removed after the ghapp installation work is complete
# and the data is backfilles appropriately
return repository.author.integration_id


def try_to_get_best_possible_bot_token(repository):
if repository.using_integration and repository.author.integration_id:
ghapp_installation_id = ghapp_installation_id_to_use(repository)
if ghapp_installation_id is not None:
try:
github_token = get_github_integration_token(
repository.author.service,
integration_id=repository.author.integration_id,
installation_id=ghapp_installation_id,
)
return dict(key=github_token)
except InvalidInstallationError:
log.warning(
"Invalid installation error",
extra=dict(
service=repository.author.service,
integration_id=repository.author.integration_id,
integration_id=ghapp_installation_id,
),
)
# now we'll fallback to trying an OAuth token
Expand Down
30 changes: 28 additions & 2 deletions upload/tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,44 @@
import jwt
import pytest
from django.conf import settings
from django.test import TransactionTestCase
from rest_framework.exceptions import Throttled, ValidationError
from shared.config import get_config
from shared.github import InvalidInstallationError

from codecov_auth.models import GithubAppInstallation, Service
from core.tests.factories import CommitFactory, OwnerFactory, RepositoryFactory
from plan.constants import PlanName
from reports.tests.factories import CommitReportFactory, UploadFactory
from upload.helpers import (
check_commit_upload_constraints,
determine_repo_for_upload,
ghapp_installation_id_to_use,
try_to_get_best_possible_bot_token,
validate_activated_repo,
validate_upload,
)


class TestGithubAppInstallationUsage(TransactionTestCase):
def test_not_github_provider(self):
repo = RepositoryFactory(author__service=Service.GITLAB.value)
assert ghapp_installation_id_to_use(repo) is None

def test_github_app_installation_flow(self):
owner = OwnerFactory(service=Service.GITHUB.value, integration_id=None)
covered_repo = RepositoryFactory(author=owner)
not_covered_repo = RepositoryFactory(author=owner)
ghapp_installation = GithubAppInstallation(
owner=owner,
repository_service_ids=[covered_repo.service_id],
installation_id=200,
)
ghapp_installation.save()
assert ghapp_installation_id_to_use(covered_repo) == 200
assert ghapp_installation_id_to_use(not_covered_repo) is None


def test_try_to_get_best_possible_bot_token_no_repobot_no_ownerbot(db):
owner = OwnerFactory.create(unencrypted_oauth_token="super")
owner.save()
Expand Down Expand Up @@ -72,7 +94,9 @@ def test_try_to_get_best_possible_bot_token_using_integration(
assert try_to_get_best_possible_bot_token(repository) == {
"key": "test-token",
}
get_github_integration_token.assert_called_once_with("github", integration_id=12345)
get_github_integration_token.assert_called_once_with(
"github", installation_id=12345
)


@patch("upload.helpers.get_github_integration_token")
Expand All @@ -92,7 +116,9 @@ def test_try_to_get_best_possible_bot_token_using_invalid_integration(
"key": "bornana",
"secret": None,
}
get_github_integration_token.assert_called_once_with("github", integration_id=12345)
get_github_integration_token.assert_called_once_with(
"github", installation_id=12345
)


def test_try_to_get_best_possible_nothing_and_is_private(db):
Expand Down
4 changes: 2 additions & 2 deletions utils/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@


@cache.cache_function(ttl=480)
def get_github_integration_token(service, integration_id=None):
return _get_github_integration_token(service, integration_id=integration_id)
def get_github_integration_token(service, installation_id=None):
return _get_github_integration_token(service, integration_id=installation_id)
Loading

0 comments on commit 01dfbb9

Please sign in to comment.