Skip to content

Commit dfd35ef

Browse files
committed
Merge branch 'master' into tkdodo/ref/builds-endpoint-to-apiOptions
2 parents 2a096ee + cf6b081 commit dfd35ef

File tree

19 files changed

+365
-49
lines changed

19 files changed

+365
-49
lines changed

src/sentry/audit_log/events.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,3 +384,38 @@ def render(self, audit_log_entry: AuditLogEntry) -> str:
384384
return "updated repository settings for {repository_count} repositories".format(
385385
repository_count=data.get("repository_count", 0),
386386
)
387+
388+
389+
def _render_repo_event(action: str, audit_log_entry: AuditLogEntry) -> str:
390+
data = audit_log_entry.data
391+
actor = audit_log_entry.actor_label or "unknown"
392+
repo_name = data.get("repo_name", "unknown")
393+
source = data.get("source", "")
394+
msg = f"{actor} {action} repository {repo_name}"
395+
if source:
396+
msg += f" (via {source})"
397+
return msg
398+
399+
400+
class RepoAddedAuditLogEvent(AuditLogEvent):
401+
def __init__(self) -> None:
402+
super().__init__(event_id=1161, name="REPO_ADDED", api_name="repo.added")
403+
404+
def render(self, audit_log_entry: AuditLogEntry) -> str:
405+
return _render_repo_event("added", audit_log_entry)
406+
407+
408+
class RepoDisabledAuditLogEvent(AuditLogEvent):
409+
def __init__(self) -> None:
410+
super().__init__(event_id=1162, name="REPO_DISABLED", api_name="repo.disabled")
411+
412+
def render(self, audit_log_entry: AuditLogEntry) -> str:
413+
return _render_repo_event("disabled", audit_log_entry)
414+
415+
416+
class RepoEnabledAuditLogEvent(AuditLogEvent):
417+
def __init__(self) -> None:
418+
super().__init__(event_id=1163, name="REPO_ENABLED", api_name="repo.enabled")
419+
420+
def render(self, audit_log_entry: AuditLogEntry) -> str:
421+
return _render_repo_event("enabled", audit_log_entry)

src/sentry/audit_log/register.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,3 +695,6 @@
695695
template="updated autofix automation settings for {project_count} projects",
696696
)
697697
)
698+
default_manager.add(events.RepoAddedAuditLogEvent())
699+
default_manager.add(events.RepoDisabledAuditLogEvent())
700+
default_manager.add(events.RepoEnabledAuditLogEvent())

src/sentry/integrations/bitbucket/webhook.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from sentry.integrations.source_code_management.webhook import SCMWebhook
2525
from sentry.integrations.types import IntegrationProviderSlug
2626
from sentry.integrations.utils.metrics import IntegrationWebhookEvent, IntegrationWebhookEventType
27+
from sentry.integrations.utils.webhook_viewer_context import webhook_viewer_context
2728
from sentry.models.commit import Commit
2829
from sentry.models.commitauthor import CommitAuthor
2930
from sentry.models.organization import Organization
@@ -258,11 +259,14 @@ def post(self, request: HttpRequest, organization_id: int) -> HttpResponse:
258259

259260
event_handler = handler()
260261

261-
with IntegrationWebhookEvent(
262-
interaction_type=event_handler.event_type,
263-
domain=IntegrationDomain.SOURCE_CODE_MANAGEMENT,
264-
provider_key=event_handler.provider,
265-
).capture():
262+
with (
263+
webhook_viewer_context(organization.id),
264+
IntegrationWebhookEvent(
265+
interaction_type=event_handler.event_type,
266+
domain=IntegrationDomain.SOURCE_CODE_MANAGEMENT,
267+
provider_key=event_handler.provider,
268+
).capture(),
269+
):
266270
event_handler(event, repo=repo, organization=organization)
267271

268272
return HttpResponse(status=204)

src/sentry/integrations/bitbucket_server/webhook.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from sentry.integrations.source_code_management.webhook import SCMWebhook
2222
from sentry.integrations.types import IntegrationProviderSlug
2323
from sentry.integrations.utils.metrics import IntegrationWebhookEvent, IntegrationWebhookEventType
24+
from sentry.integrations.utils.webhook_viewer_context import webhook_viewer_context
2425
from sentry.models.commit import Commit
2526
from sentry.models.commitauthor import CommitAuthor
2627
from sentry.models.organization import Organization
@@ -210,6 +211,7 @@ def post(self, request: HttpRequest, organization_id, integration_id) -> HttpRes
210211

211212
event_handler = handler()
212213

213-
event_handler(event, organization=organization, integration_id=integration_id)
214+
with webhook_viewer_context(organization.id):
215+
event_handler(event, organization=organization, integration_id=integration_id)
214216

215217
return HttpResponse(status=204)

src/sentry/integrations/cursor/webhooks/handler.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from sentry.api.base import Endpoint, cell_silo_endpoint
2222
from sentry.integrations.cursor.integration import CursorAgentIntegration
2323
from sentry.integrations.services.integration import integration_service
24+
from sentry.integrations.utils.webhook_viewer_context import webhook_viewer_context
2425
from sentry.seer.autofix.utils import (
2526
CodingAgentResult,
2627
CodingAgentStatus,
@@ -58,7 +59,8 @@ def post(self, request: Request, organization_id: int) -> Response:
5859
logger.warning("cursor_webhook.invalid_signature")
5960
raise PermissionDenied("Invalid signature")
6061

61-
self._process_webhook(payload)
62+
with webhook_viewer_context(organization_id):
63+
self._process_webhook(payload)
6264
logger.info("cursor_webhook.success", extra={"event_type": event_type})
6365
return self.respond(status=204)
6466

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
)
1414
from sentry.organizations.services.organization import organization_service
1515
from sentry.plugins.providers.integration_repository import (
16-
RepoExistsError,
1716
RepositoryInputConfig,
1817
get_integration_repository_provider,
1918
)
@@ -40,7 +39,7 @@ def get_repo_config(repo: Mapping[str, Any], integration_id: int) -> RepositoryI
4039
processing_deadline_duration=60,
4140
silo_mode=SiloMode.CONTROL,
4241
)
43-
@retry(exclude=(RepoExistsError, KeyError))
42+
@retry(exclude=(KeyError,))
4443
def link_all_repos(
4544
integration_key: str,
4645
integration_id: int,
@@ -88,14 +87,15 @@ def link_all_repos(
8887
missing_repos.append(repo)
8988
continue
9089

91-
try:
90+
_created_repos, _reactivated_repos, existing_repos = (
9291
integration_repo_provider.create_repositories(
9392
configs=repo_configs, organization=rpc_org
9493
)
95-
except RepoExistsError as e:
94+
)
95+
if existing_repos:
9696
lifecycle.record_halt(
9797
str(LinkAllReposHaltReason.REPOSITORY_NOT_CREATED),
98-
{"missing_repos": e.repos, "integration_id": integration_id},
98+
{"missing_repos": existing_repos, "integration_id": integration_id},
9999
)
100100

101101
if missing_repos:

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

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@
2121
SCMIntegrationInteractionEvent,
2222
SCMIntegrationInteractionType,
2323
)
24+
from sentry.integrations.source_code_management.repo_audit import log_repo_change
2425
from sentry.organizations.services.organization import organization_service
25-
from sentry.plugins.providers.integration_repository import (
26-
RepoExistsError,
27-
get_integration_repository_provider,
28-
)
26+
from sentry.plugins.providers.integration_repository import get_integration_repository_provider
2927
from sentry.shared_integrations.exceptions import ApiError
3028
from sentry.silo.base import SiloMode
3129
from sentry.tasks.base import instrumented_task, retry
@@ -171,6 +169,8 @@ def sync_repos_for_org(organization_integration_id: int) -> None:
171169
if dry_run:
172170
return
173171

172+
repo_by_external_id = {r.external_id: r for r in active_repos + disabled_repos}
173+
174174
if new_ids:
175175
integration_repo_provider = get_integration_repository_provider(integration)
176176
repo_configs = [
@@ -179,12 +179,27 @@ def sync_repos_for_org(organization_integration_id: int) -> None:
179179
if str(repo["id"]) in new_ids
180180
]
181181
if repo_configs:
182-
try:
183-
integration_repo_provider.create_repositories(
184-
configs=repo_configs, organization=rpc_org
182+
created_repos, reactivated_repos, _ = integration_repo_provider.create_repositories(
183+
configs=repo_configs, organization=rpc_org
184+
)
185+
186+
for repo in created_repos:
187+
log_repo_change(
188+
event_name="REPO_ADDED",
189+
organization_id=organization_id,
190+
repo=repo,
191+
source="repository sync",
192+
provider=integration.provider,
193+
)
194+
195+
for repo in reactivated_repos:
196+
log_repo_change(
197+
event_name="REPO_ENABLED",
198+
organization_id=organization_id,
199+
repo=repo,
200+
source="repository sync",
201+
provider=integration.provider,
185202
)
186-
except RepoExistsError:
187-
pass
188203

189204
if removed_ids:
190205
repository_service.disable_repositories_by_external_ids(
@@ -194,13 +209,31 @@ def sync_repos_for_org(organization_integration_id: int) -> None:
194209
external_ids=list(removed_ids),
195210
)
196211

212+
for eid in removed_ids:
213+
removed_repo = repo_by_external_id.get(eid)
214+
if removed_repo:
215+
log_repo_change(
216+
event_name="REPO_DISABLED",
217+
organization_id=organization_id,
218+
repo=removed_repo,
219+
source="automatic SCM syncing",
220+
provider=integration.provider,
221+
)
222+
197223
if restored_ids:
198224
for repo in disabled_repos:
199225
if repo.external_id in restored_ids:
200226
repo.status = ObjectStatus.ACTIVE
201227
repository_service.update_repository(
202228
organization_id=organization_id, update=repo
203229
)
230+
log_repo_change(
231+
event_name="REPO_ENABLED",
232+
organization_id=organization_id,
233+
repo=repo,
234+
source="automatic SCM syncing",
235+
provider=integration.provider,
236+
)
204237

205238

206239
@instrumented_task(

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

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
SCMIntegrationInteractionEvent,
1414
SCMIntegrationInteractionType,
1515
)
16+
from sentry.integrations.source_code_management.repo_audit import log_repo_change
1617
from sentry.organizations.services.organization import organization_service
1718
from sentry.organizations.services.organization.model import RpcOrganization
1819
from sentry.plugins.providers.integration_repository import (
19-
RepoExistsError,
2020
RepositoryInputConfig,
2121
get_integration_repository_provider,
2222
)
@@ -36,7 +36,7 @@
3636
processing_deadline_duration=120,
3737
silo_mode=SiloMode.CONTROL,
3838
)
39-
@retry(exclude=(RepoExistsError, KeyError))
39+
@retry(exclude=(KeyError,))
4040
def sync_repos_on_install_change(
4141
integration_id: int,
4242
action: str,
@@ -119,18 +119,59 @@ def _sync_repos_for_org(
119119
continue
120120

121121
if repo_configs:
122-
try:
122+
created_repos, reactivated_repos, _missing_repos = (
123123
integration_repo_provider.create_repositories(
124124
configs=repo_configs, organization=rpc_org
125125
)
126-
except RepoExistsError:
127-
pass
126+
)
127+
128+
for created_repo in created_repos:
129+
log_repo_change(
130+
event_name="REPO_ADDED",
131+
organization_id=rpc_org.id,
132+
repo=created_repo,
133+
source="GitHub webhook",
134+
provider=integration.provider,
135+
)
136+
137+
for reactivated_repo in reactivated_repos:
138+
log_repo_change(
139+
event_name="REPO_ENABLED",
140+
organization_id=rpc_org.id,
141+
repo=reactivated_repo,
142+
source="GitHub webhook",
143+
provider=integration.provider,
144+
)
128145

129146
if repos_removed:
147+
# Look up repos before disabling to get their IDs and names
130148
external_ids = [str(repo["id"]) for repo in repos_removed]
149+
existing_repos = repository_service.get_repositories(
150+
organization_id=rpc_org.id,
151+
integration_id=integration.id,
152+
providers=[provider],
153+
)
154+
repo_by_eid = {
155+
r.external_id: r
156+
for r in existing_repos
157+
if r.external_id and r.status == ObjectStatus.ACTIVE
158+
}
159+
131160
repository_service.disable_repositories_by_external_ids(
132161
organization_id=rpc_org.id,
133162
integration_id=integration.id,
134163
provider=provider,
135164
external_ids=external_ids,
136165
)
166+
167+
for repo in repos_removed:
168+
eid = str(repo["id"])
169+
sentry_repo = repo_by_eid.get(eid)
170+
if sentry_repo:
171+
log_repo_change(
172+
event_name="REPO_DISABLED",
173+
organization_id=rpc_org.id,
174+
repo=sentry_repo,
175+
source="GitHub webhook",
176+
provider=integration.provider,
177+
)

src/sentry/integrations/github/webhook.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from sentry.integrations.utils.metrics import IntegrationWebhookEvent, IntegrationWebhookEventType
4545
from sentry.integrations.utils.scope import clear_tags_and_context
4646
from sentry.integrations.utils.sync import sync_group_assignee_inbound_by_external_actor
47+
from sentry.integrations.utils.webhook_viewer_context import webhook_viewer_context
4748
from sentry.models.commit import Commit
4849
from sentry.models.commitauthor import CommitAuthor
4950
from sentry.models.commitfilechange import CommitFileChange, post_bulk_create
@@ -263,14 +264,15 @@ def __call__(self, event: Mapping[str, Any], **kwargs: Any) -> None:
263264

264265
for repo in repos.exclude(status=ObjectStatus.HIDDEN):
265266
self.update_repo_data(repo, event)
266-
self._handle(
267-
github_event=github_event,
268-
integration=integration,
269-
event=event,
270-
organization=orgs[repo.organization_id],
271-
repo=repo,
272-
github_delivery_id=github_delivery_id,
273-
)
267+
with webhook_viewer_context(repo.organization_id):
268+
self._handle(
269+
github_event=github_event,
270+
integration=integration,
271+
event=event,
272+
organization=orgs[repo.organization_id],
273+
repo=repo,
274+
github_delivery_id=github_delivery_id,
275+
)
274276

275277
def update_repo_data(self, repo: Repository, event: Mapping[str, Any]) -> None:
276278
"""

src/sentry/integrations/gitlab/webhooks.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from sentry.integrations.utils.metrics import IntegrationWebhookEvent, IntegrationWebhookEventType
2929
from sentry.integrations.utils.scope import clear_tags_and_context
3030
from sentry.integrations.utils.sync import sync_group_assignee_inbound_by_external_actor
31+
from sentry.integrations.utils.webhook_viewer_context import webhook_viewer_context
3132
from sentry.models.commit import Commit
3233
from sentry.models.commitauthor import CommitAuthor
3334
from sentry.models.pullrequest import PullRequest
@@ -516,11 +517,14 @@ def post(self, request: HttpRequest) -> HttpResponse:
516517
organization = org_context.organization
517518
event_handler = handler()
518519

519-
with IntegrationWebhookEvent(
520-
interaction_type=event_handler.event_type,
521-
domain=IntegrationDomain.SOURCE_CODE_MANAGEMENT,
522-
provider_key=event_handler.provider,
523-
).capture():
520+
with (
521+
webhook_viewer_context(install.organization_id),
522+
IntegrationWebhookEvent(
523+
interaction_type=event_handler.event_type,
524+
domain=IntegrationDomain.SOURCE_CODE_MANAGEMENT,
525+
provider_key=event_handler.provider,
526+
).capture(),
527+
):
524528
event_handler(event, integration=integration, organization=organization)
525529

526530
return HttpResponse(status=204)

0 commit comments

Comments
 (0)