Skip to content

Commit bb80ef6

Browse files
authored
ref(ecosystem): refactor link_all_repos to bulk update repositories (#95494)
1 parent 6460092 commit bb80ef6

File tree

10 files changed

+183
-78
lines changed

10 files changed

+183
-78
lines changed

src/sentry/integrations/bitbucket/repository.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
from typing import Any
2+
13
from sentry.integrations.types import IntegrationProviderSlug
24
from sentry.locks import locks
35
from sentry.models.apitoken import generate_token
46
from sentry.models.options.organization_option import OrganizationOption
57
from sentry.organizations.services.organization.model import RpcOrganization
68
from sentry.plugins.providers import IntegrationRepositoryProvider
9+
from sentry.plugins.providers.integration_repository import RepositoryConfig
710
from sentry.shared_integrations.exceptions import ApiError
811
from sentry.utils.email import parse_email, parse_user_name
912
from sentry.utils.http import absolute_uri
@@ -43,7 +46,9 @@ def get_webhook_secret(self, organization):
4346
)
4447
return secret
4548

46-
def build_repository_config(self, organization: RpcOrganization, data):
49+
def build_repository_config(
50+
self, organization: RpcOrganization, data: dict[str, Any]
51+
) -> RepositoryConfig:
4752
installation = self.get_installation(data.get("installation"), organization.id)
4853
client = installation.get_client()
4954
try:

src/sentry/integrations/bitbucket_server/repository.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
from datetime import datetime, timezone
2+
from typing import Any
23

34
from django.core.cache import cache
45
from django.urls import reverse
56

67
from sentry.integrations.types import IntegrationProviderSlug
78
from sentry.organizations.services.organization.model import RpcOrganization
8-
from sentry.plugins.providers.integration_repository import IntegrationRepositoryProvider
9+
from sentry.plugins.providers.integration_repository import (
10+
IntegrationRepositoryProvider,
11+
RepositoryConfig,
12+
)
913
from sentry.shared_integrations.exceptions import ApiError
1014
from sentry.utils.hashlib import md5_text
1115
from sentry.utils.http import absolute_uri
@@ -30,7 +34,9 @@ def get_repository_data(self, organization, config):
3034
config["repo"] = repo["name"]
3135
return config
3236

33-
def build_repository_config(self, organization: RpcOrganization, data):
37+
def build_repository_config(
38+
self, organization: RpcOrganization, data: dict[str, Any]
39+
) -> RepositoryConfig:
3440
installation = self.get_installation(data.get("installation"), organization.id)
3541
client = installation.get_client()
3642

src/sentry/integrations/github/repository.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from sentry.models.repository import Repository
1313
from sentry.organizations.services.organization.model import RpcOrganization
1414
from sentry.plugins.providers import IntegrationRepositoryProvider
15+
from sentry.plugins.providers.integration_repository import RepositoryConfig
1516
from sentry.shared_integrations.exceptions import ApiError, IntegrationError
1617

1718
WEBHOOK_EVENTS = ["push", "pull_request"]
@@ -51,8 +52,8 @@ def get_repository_data(
5152
return config
5253

5354
def build_repository_config(
54-
self, organization: RpcOrganization, data: Mapping[str, Any]
55-
) -> Mapping[str, Any]:
55+
self, organization: RpcOrganization, data: dict[str, Any]
56+
) -> RepositoryConfig:
5657
return {
5758
"name": data["identifier"],
5859
"external_id": data["external_id"],

src/sentry/integrations/github/tasks/link_all_repos.py

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from typing import Any
23

34
from sentry.constants import ObjectStatus
45
from sentry.integrations.services.integration import integration_service
@@ -18,7 +19,6 @@
1819
from sentry.taskworker.config import TaskworkerConfig
1920
from sentry.taskworker.namespaces import integrations_control_tasks
2021
from sentry.taskworker.retry import Retry
21-
from sentry.utils import metrics
2222

2323
logger = logging.getLogger(__name__)
2424

@@ -58,27 +58,11 @@ def link_all_repos(
5858
integration_id=integration_id, status=ObjectStatus.ACTIVE
5959
)
6060
if not integration:
61-
# TODO: Remove this logger in favor of context manager
62-
logger.error(
63-
"%s.link_all_repos.integration_missing",
64-
integration_key,
65-
extra={"organization_id": organization_id},
66-
)
67-
metrics.incr("github.link_all_repos.error", tags={"type": "missing_integration"})
6861
lifecycle.record_failure(str(LinkAllReposHaltReason.MISSING_INTEGRATION))
6962
return
7063

7164
rpc_org = organization_service.get(id=organization_id)
7265
if rpc_org is None:
73-
logger.error(
74-
"%s.link_all_repos.organization_missing",
75-
integration_key,
76-
extra={"organization_id": organization_id},
77-
)
78-
metrics.incr(
79-
f"{integration_key}.link_all_repos.error",
80-
tags={"type": "missing_organization"},
81-
)
8266
lifecycle.record_failure(str(LinkAllReposHaltReason.MISSING_ORGANIZATION))
8367
return
8468

@@ -93,26 +77,31 @@ def link_all_repos(
9377
lifecycle.record_halt(str(LinkAllReposHaltReason.RATE_LIMITED))
9478
return
9579

96-
metrics.incr(f"{integration_key}.link_all_repos.api_error")
9780
raise
9881

9982
integration_repo_provider = get_integration_repository_provider(integration)
10083

101-
# If we successfully create any repositories, we'll set this to True
102-
success = False
103-
84+
repo_configs: list[dict[str, Any]] = []
85+
missing_repos = []
10486
for repo in repositories:
10587
try:
106-
config = get_repo_config(repo, integration_id)
107-
integration_repo_provider.create_repository(
108-
repo_config=config, organization=rpc_org
109-
)
110-
success = True
88+
repo_configs.append(get_repo_config(repo, integration_id))
11189
except KeyError:
112-
continue
113-
except RepoExistsError:
114-
metrics.incr("sentry.integration_repo_provider.repo_exists")
90+
missing_repos.append(repo)
11591
continue
11692

117-
if not success:
118-
lifecycle.record_halt(str(LinkAllReposHaltReason.REPOSITORY_NOT_CREATED))
93+
try:
94+
integration_repo_provider.create_repositories(
95+
configs=repo_configs, organization=rpc_org
96+
)
97+
except RepoExistsError as e:
98+
lifecycle.record_halt(
99+
str(LinkAllReposHaltReason.REPOSITORY_NOT_CREATED),
100+
{"missing_repos": e.repos, "integration_id": integration_id},
101+
)
102+
103+
if missing_repos:
104+
lifecycle.record_halt(
105+
str(LinkAllReposHaltReason.REPOSITORY_NOT_CREATED),
106+
{"missing_repos": missing_repos, "integration_id": integration_id},
107+
)

src/sentry/integrations/github_enterprise/repository.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
from typing import Any
2+
13
from sentry.integrations.github.repository import GitHubRepositoryProvider
24
from sentry.integrations.services.integration import integration_service
35
from sentry.integrations.types import IntegrationProviderSlug
46
from sentry.organizations.services.organization.model import RpcOrganization
7+
from sentry.plugins.providers.integration_repository import RepositoryConfig
58
from sentry.shared_integrations.exceptions import ApiError, IntegrationError
69

710
WEBHOOK_EVENTS = ["push", "pull_request"]
@@ -25,7 +28,9 @@ def _validate_repo(self, client, installation, repo):
2528

2629
return repo_data
2730

28-
def build_repository_config(self, organization: RpcOrganization, data):
31+
def build_repository_config(
32+
self, organization: RpcOrganization, data: dict[str, Any]
33+
) -> RepositoryConfig:
2934
integration = integration_service.get_integration(
3035
integration_id=data["integration_id"], provider=self.repo_provider
3136
)

src/sentry/integrations/gitlab/repository.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
from typing import Any
2+
13
from sentry.integrations.types import IntegrationProviderSlug
24
from sentry.organizations.services.organization.model import RpcOrganization
35
from sentry.plugins.providers import IntegrationRepositoryProvider
6+
from sentry.plugins.providers.integration_repository import RepositoryConfig
47
from sentry.shared_integrations.exceptions import ApiError
58

69

@@ -31,7 +34,9 @@ def get_repository_data(self, organization, config):
3134
)
3235
return config
3336

34-
def build_repository_config(self, organization: RpcOrganization, data):
37+
def build_repository_config(
38+
self, organization: RpcOrganization, data: dict[str, Any]
39+
) -> RepositoryConfig:
3540

3641
installation = self.get_installation(data.get("installation"), organization.id)
3742
client = installation.get_client()

src/sentry/integrations/services/repository/impl.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ def update_repositories(self, *, organization_id: int, updates: list[RpcReposito
106106
fields_to_update = set(list(update_mapping.values())[0].keys())
107107

108108
with transaction.atomic(router.db_for_write(Repository)):
109-
110109
repositories = Repository.objects.filter(
111110
organization_id=organization_id, id__in=update_mapping.keys()
112111
)

src/sentry/integrations/vsts/repository.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from sentry.models.repository import Repository
1010
from sentry.organizations.services.organization.model import RpcOrganization
1111
from sentry.plugins.providers import IntegrationRepositoryProvider
12+
from sentry.plugins.providers.integration_repository import RepositoryConfig
1213

1314
MAX_COMMIT_DATA_REQUESTS = 90
1415

@@ -46,8 +47,8 @@ def get_repository_data(
4647
return config
4748

4849
def build_repository_config(
49-
self, organization: RpcOrganization, data: Mapping[str, str]
50-
) -> Mapping[str, Any]:
50+
self, organization: RpcOrganization, data: dict[str, Any]
51+
) -> RepositoryConfig:
5152
return {
5253
"name": data["name"],
5354
"external_id": data["external_id"],

0 commit comments

Comments
 (0)