Skip to content

feat(github): Handle installation_repositories webhook #111864

Merged
wedamija merged 9 commits intomasterfrom
danf/github-installation-repositories
Apr 3, 2026
Merged

feat(github): Handle installation_repositories webhook #111864
wedamija merged 9 commits intomasterfrom
danf/github-installation-repositories

Conversation

@wedamija
Copy link
Copy Markdown
Member

Currently, we only sync the available repositories from Github on installing the integration. So over time, if new repositories are added to the github organization, or access to specific repositories is added or removed, we end up out of sync with which repositories we store in Sentry.

To fix this, we are going to start handling the installation_repositories webhook. This is fired whenever the repositories that a github app can access change. This allows us to keep all the repos in sync.

Note that when access to a repo is removed, we only ever disable the repo and never delete it. This allows us to keep the history of commits and so on so far.

@wedamija wedamija requested a review from a team March 30, 2026 23:55
@wedamija wedamija requested review from a team as code owners March 30, 2026 23:55
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Mar 30, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate RPC call to fetch organization per iteration
    • Eliminated duplicate organization_service.get() call by fetching the organization once in the main loop and passing it to _sync_repos_for_org instead of fetching it again inside the function.

Create PR

Or push these changes by commenting:

@cursor push 3a1e5c4352
Preview (3a1e5c4352)
diff --git a/src/sentry/integrations/github/tasks/sync_repos_on_install_change.py b/src/sentry/integrations/github/tasks/sync_repos_on_install_change.py
--- a/src/sentry/integrations/github/tasks/sync_repos_on_install_change.py
+++ b/src/sentry/integrations/github/tasks/sync_repos_on_install_change.py
@@ -13,6 +13,7 @@
     SCMIntegrationInteractionType,
 )
 from sentry.organizations.services.organization import organization_service
+from sentry.organizations.services.organization.model import RpcOrganization
 from sentry.plugins.providers.integration_repository import (
     RepoExistsError,
     RepositoryInputConfig,
@@ -78,10 +79,18 @@
 
     for oi in org_integrations:
         organization_id = oi.organization_id
+        rpc_org = organization_service.get(id=organization_id)
 
+        if rpc_org is None:
+            logger.info(
+                "sync_repos_on_install_change.missing_organization",
+                extra={"organization_id": organization_id},
+            )
+            continue
+
         if not features.has(
             "organizations:github-repo-auto-sync",
-            organization_service.get(id=organization_id),
+            rpc_org,
         ):
             continue
 
@@ -93,7 +102,7 @@
         ).capture():
             _sync_repos_for_org(
                 integration=integration,
-                organization_id=organization_id,
+                rpc_org=rpc_org,
                 provider=provider,
                 repos_added=repos_added,
                 repos_removed=repos_removed,
@@ -103,19 +112,11 @@
 def _sync_repos_for_org(
     *,
     integration: RpcIntegration,
-    organization_id: int,
+    rpc_org: RpcOrganization,
     provider: str,
     repos_added: list[GitHubRepo],
     repos_removed: list[GitHubRepo],
 ) -> None:
-    rpc_org = organization_service.get(id=organization_id)
-    if rpc_org is None:
-        logger.info(
-            "sync_repos_on_install_change.missing_organization",
-            extra={"organization_id": organization_id},
-        )
-        return
-
     if repos_added:
         integration_repo_provider = get_integration_repository_provider(integration)
         repo_configs: list[RepositoryInputConfig] = []
@@ -137,7 +138,7 @@
     if repos_removed:
         external_ids = [str(repo["id"]) for repo in repos_removed]
         repository_service.disable_repositories_by_external_ids(
-            organization_id=organization_id,
+            organization_id=rpc_org.id,
             integration_id=integration.id,
             provider=provider,
             external_ids=external_ids,

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

wedamija added 9 commits April 1, 2026 15:25
Currently, we only sync the available repositories from Github on installing the integration. So over time, if new repositories are added to the github organization, or access to specific repositories is added or removed, we end up out of sync with which repositories we store in Sentry.

To fix this, we are going to start handling the `installation_repositories` webhook. This is fired whenever the repositories that a github app can access change. This allows us to keep all the repos in sync.

Note that when access to a repo is removed, we only ever disable the repo and never delete it. This allows us to keep the history of commits and so on so far.
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment on lines +27 to +28
# GHE only routes installation events to control silo.
# installation_repositories is not yet supported for GHE.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it that it's not supported?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just didn't have any tests for this file before?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, although maybe we tested outside the impl instead? Not sure

@wedamija wedamija merged commit a8a8009 into master Apr 3, 2026
103 checks passed
@wedamija wedamija deleted the danf/github-installation-repositories branch April 3, 2026 16:23
@wedamija wedamija added the Trigger: Revert Add to a merged PR to revert it (skips CI) label Apr 3, 2026
@getsentry-bot
Copy link
Copy Markdown
Contributor

PR reverted: ca7fcd4

getsentry-bot added a commit that referenced this pull request Apr 3, 2026
…)"

This reverts commit a8a8009.

Co-authored-by: wedamija <6288560+wedamija@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Backend Test Failures

Failures on a8a8009 in this run:

tests/sentry/taskworker/test_config.py::test_all_instrumented_tasks_registeredlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/taskworker/test_config.py:120: in test_all_instrumented_tasks_registered
    raise AssertionError(
E   AssertionError: Found 1 module(s) with @instrumented_task that are NOT registered in TASKWORKER_IMPORTS.
E   These tasks will not be discovered by the taskworker in production!
E   
E   Missing modules:
E     - sentry.integrations.github.tasks.sync_repos_on_install_change
E   
E   Add these to TASKWORKER_IMPORTS in src/sentry/conf/server.py

wedamija added a commit that referenced this pull request Apr 3, 2026
We had to revert #111864 because the webhooks were being routed to cells. This is because we deploy cells first, then control, and so the routing wasn't in place. Adding this routing first, then we'll merge the webhook itself after this deploys.
wedamija added a commit that referenced this pull request Apr 3, 2026
…ook (#112226)

We had to revert #111864 because
the webhooks were being routed to cells. This is because we deploy cells
first, then control, and so the routing wasn't in place. Adding this
routing first, then we'll merge the webhook itself after this deploys.

<!-- Describe your PR here. -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Trigger: Revert Add to a merged PR to revert it (skips CI)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants