This repository was archived by the owner on Jun 13, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
[WIP] fix: Uptake additional GH Repository webhook actions #1153
Open
ajay-sentry
wants to merge
1
commit into
main
Choose a base branch
from
Ajay/2938-repo-webhooks
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,10 +103,10 @@ | |
def unhandled_webhook_event(self, request, *args, **kwargs): | ||
return Response(data=WebhookHandlerErrorMessages.UNSUPPORTED_EVENT) | ||
|
||
def _get_repo(self, request): | ||
def _get_repo(self): | ||
""" | ||
Attempts to fetch the repo first via the index on o(wnerid, service_id), | ||
then naively on service, service_id if that fails. | ||
Attempts to fetch the repo first via the index on (ownerid, service_id), | ||
then naively on (service, service_id) if that fails. | ||
""" | ||
repo_data = self.request.data.get("repository", {}) | ||
repo_service_id = repo_data.get("id") | ||
|
@@ -119,67 +119,74 @@ | |
) | ||
except Owner.DoesNotExist: | ||
log.info( | ||
f"Error fetching owner with service_id {owner_service_id}, " | ||
f"using repository service id to get repo", | ||
f"Error fetching owner with service_id {owner_service_id}, using repository service id to get repo", | ||
extra=dict(repo_service_id=repo_service_id, repo_slug=repo_slug), | ||
) | ||
try: | ||
log.info( | ||
"Unable to find repository owner, fetching repo with service, service_id", | ||
extra=dict(repo_service_id=repo_service_id, repo_slug=repo_slug), | ||
) | ||
return Repository.objects.get( | ||
author__service=self.service_name, service_id=repo_service_id | ||
) | ||
except Repository.DoesNotExist: | ||
log.info( | ||
"Received event for non-existent repository", | ||
"Received event for non-existent owner and repository", | ||
extra=dict(repo_service_id=repo_service_id, repo_slug=repo_slug), | ||
) | ||
self._inc_err("repo_not_found") | ||
self._inc_err("owner_and_repo_not_found") | ||
raise NotFound("Repository does not exist") | ||
else: | ||
try: | ||
log.debug( | ||
"Found repository owner, fetching repo with ownerid, service_id", | ||
extra=dict(repo_service_id=repo_service_id, repo_slug=repo_slug), | ||
) | ||
return Repository.objects.get( | ||
author__ownerid=owner.ownerid, service_id=repo_service_id | ||
) | ||
except Repository.DoesNotExist: | ||
log.info( | ||
"Received event for non-existent repository", | ||
extra=dict(repo_service_id=repo_service_id, repo_slug=repo_slug), | ||
) | ||
default_ghapp_installation = owner.github_app_installations.filter( | ||
name=GITHUB_APP_INSTALLATION_DEFAULT_NAME | ||
).first() | ||
if default_ghapp_installation or owner.integration_id: | ||
log.info( | ||
"Repository no found but owner is using integration, creating repository" | ||
"Repository not found but owner is using integration, creating repository" | ||
) | ||
return Repository.objects.get_or_create_from_git_repo( | ||
repo_data, owner | ||
)[0] | ||
log.info( | ||
"Received event for non-existent repository", | ||
extra=dict(repo_service_id=repo_service_id, repo_slug=repo_slug), | ||
) | ||
self._inc_err("repo_not_found") | ||
raise NotFound("Repository does not exist") | ||
|
||
def ping(self, request, *args, **kwargs): | ||
return Response(data="pong") | ||
|
||
def repository(self, request, *args, **kwargs): | ||
action, repo = self.request.data.get("action"), self._get_repo(request) | ||
action = self.request.data.get("action") | ||
|
||
# Repo created event, separate from other actions because we know | ||
# that the repo will not exist in our DB yet | ||
if action == "created": | ||
repo_data = self.request.data.get("repository", {}) | ||
repo_service_id = repo_data.get("id") | ||
owner_service_id = repo_data.get("owner", {}).get("id") | ||
owner, _ = Owner.objects.get_or_create( | ||
service=self.service_name, service_id=owner_service_id | ||
) | ||
repo, _ = Repository.objects.get_or_create( | ||
author__ownerid=owner.ownerid, service_id=repo_service_id | ||
) | ||
return Response() | ||
|
||
repo = self._get_repo() | ||
if action == "publicized": | ||
repo.private, repo.activated = False, False | ||
repo.save() | ||
repo.save(update_fields=["private", "activated"]) | ||
log.info( | ||
"Repository publicized", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
elif action == "privatized": | ||
repo.private = True | ||
repo.save() | ||
repo.save(update_fields=["private"]) | ||
log.info( | ||
"Repository privatized", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
|
@@ -195,33 +202,78 @@ | |
"Repository soft-deleted", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
elif action == "edited": | ||
default_branch = ( | ||
self.request.data.get("changes", {}) | ||
.get("default_branch", {}) | ||
.get("from") | ||
) | ||
if default_branch and default_branch != repo.branch: | ||
repo.branch = default_branch | ||
repo.save(update_fields=["branch"]) | ||
log.info( | ||
"Repository default branch updated", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
elif action == "renamed": | ||
repo.name = self.request.data.get("repository", {}).get("name") | ||
repo.save(update_fields=["name"]) | ||
log.info( | ||
"Repository renamed", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
elif action == "transferred": | ||
repo.author = Owner.objects.get( | ||
service=self.service_name, | ||
service_id=self.request.data.get("repository", {}) | ||
.get("owner", {}) | ||
.get("id"), | ||
) | ||
Comment on lines
+226
to
+231
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably get_or_create here too |
||
repo.save(update_fields=["author"]) | ||
log.info( | ||
"Repository transferred", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
elif action == "archived": | ||
repo.activated = False | ||
repo.save(update_fields=["activated"]) | ||
log.info( | ||
"Repository archived", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
elif action == "unarchived": | ||
repo.activated = True | ||
repo.save(update_fields=["activated"]) | ||
log.info( | ||
"Repository unarchived", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
Comment on lines
+237
to
+250
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this actually what "activated" means in our system? i figured it had plan implications or something There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so, at least it seemed to make sense according to https://www.notion.so/sentry/Database-Field-Terminology-2c501a0844bc410aa35f1ecfd3c3eb61 |
||
else: | ||
log.warning( | ||
f"Unknown repository action: {action}", extra=dict(repoid=repo.repoid) | ||
"Unknown repository action", | ||
extra=dict(action=action, repoid=repo.repoid), | ||
) | ||
return Response() | ||
|
||
def delete(self, request, *args, **kwargs): | ||
ref_type = request.data.get("ref_type", "") | ||
repo = self._get_repo(request) | ||
repo = self._get_repo() | ||
if ref_type != "branch": | ||
log.info( | ||
f"Unsupported ref type: {ref_type}, exiting", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
return Response("Unsupported ref type") | ||
branch_name = self.request.data.get("ref")[11:] | ||
Branch.objects.filter( | ||
repository=self._get_repo(request), name=branch_name | ||
).delete() | ||
Branch.objects.filter(repository=repo, name=branch_name).delete() | ||
log.info( | ||
f"Branch '{branch_name}' deleted", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
) | ||
return Response() | ||
|
||
def public(self, request, *args, **kwargs): | ||
repo = self._get_repo(request) | ||
repo = self._get_repo() | ||
repo.private, repo.activated = False, False | ||
repo.save() | ||
log.info( | ||
|
@@ -232,7 +284,7 @@ | |
|
||
def push(self, request, *args, **kwargs): | ||
ref_type = "branch" if request.data.get("ref", "")[5:10] == "heads" else "tag" | ||
repo = self._get_repo(request) | ||
repo = self._get_repo() | ||
if ref_type != "branch": | ||
log.debug( | ||
"Ref is tag, not branch, ignoring push event", | ||
|
@@ -320,7 +372,7 @@ | |
return Response() | ||
|
||
def status(self, request, *args, **kwargs): | ||
repo = self._get_repo(request) | ||
repo = self._get_repo() | ||
commitid = request.data.get("sha") | ||
|
||
if not repo.active: | ||
|
@@ -365,7 +417,7 @@ | |
return Response() | ||
|
||
def pull_request(self, request, *args, **kwargs): | ||
repo = self._get_repo(request) | ||
repo = self._get_repo() | ||
|
||
if not repo.active: | ||
log.info( | ||
|
@@ -703,7 +755,7 @@ | |
def member(self, request, *args, **kwargs): | ||
action = request.data["action"] | ||
if action == "removed": | ||
repo = self._get_repo(request) | ||
repo = self._get_repo() | ||
log.info( | ||
"Request to remove read permissions for user", | ||
extra=dict(repoid=repo.repoid, github_webhook_event=self.event), | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the webhook give us its public/private status? if we can't know whether the repo is meant to be private, i don't think we can create it here
for public repos this would make them show up without having to trigger a sync, but for private repos you still have to sync to populate
owner.permission