-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
feat(code-review): Handle issue comment incl eyes emoji #105527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
92be11c
40560ae
3683d7c
0b0ea04
b78e5bd
4f52e81
740b846
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| """ | ||
| Handler for GitHub issue_comment webhook events. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import enum | ||
| import logging | ||
| from collections.abc import Mapping | ||
| from typing import Any | ||
|
|
||
| from sentry import options | ||
| from sentry.integrations.github.client import GitHubReaction | ||
| from sentry.integrations.services.integration import RpcIntegration | ||
| from sentry.models.organization import Organization | ||
| from sentry.models.repository import Repository | ||
| from sentry.utils import metrics | ||
|
|
||
| from ..permissions import has_code_review_enabled | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class ErrorStatus(enum.StrEnum): | ||
| MISSING_INTEGRATION = "missing_integration" | ||
| REACTION_FAILED = "reaction_failed" | ||
|
|
||
|
|
||
| class Log(enum.StrEnum): | ||
| MISSING_INTEGRATION = "github.webhook.issue_comment.missing-integration" | ||
| REACTION_FAILED = "github.webhook.issue_comment.reaction-failed" | ||
|
|
||
|
|
||
| class Metrics(enum.StrEnum): | ||
| ERROR = "seer.code_review.webhook.issue_comment.error" | ||
| OUTCOME = "seer.code_review.webhook.issue_comment.outcome" | ||
|
|
||
|
|
||
| SENTRY_REVIEW_COMMAND = "@sentry review" | ||
|
|
||
|
|
||
| def is_pr_review_command(comment_body: str | None) -> bool: | ||
| if comment_body is None: | ||
| return False | ||
| return SENTRY_REVIEW_COMMAND in comment_body.lower() | ||
|
|
||
|
|
||
| def _add_eyes_reaction_to_comment( | ||
| integration: RpcIntegration | None, | ||
| organization: Organization, | ||
| repo: Repository, | ||
| comment_id: str, | ||
| ) -> None: | ||
| extra = {"organization_id": organization.id, "repo": repo.name, "comment_id": comment_id} | ||
|
|
||
| if integration is None: | ||
| metrics.incr( | ||
| Metrics.ERROR.value, | ||
| tags={"error_status": ErrorStatus.MISSING_INTEGRATION.value}, | ||
| ) | ||
| logger.warning( | ||
| Log.MISSING_INTEGRATION.value, | ||
| extra=extra, | ||
| ) | ||
| return | ||
|
|
||
| try: | ||
| client = integration.get_installation(organization_id=organization.id).get_client() | ||
| client.create_comment_reaction(repo.name, comment_id, GitHubReaction.EYES) | ||
| metrics.incr( | ||
| Metrics.OUTCOME.value, | ||
| tags={"status": "reaction_added"}, | ||
| ) | ||
| except Exception: | ||
| metrics.incr( | ||
| Metrics.ERROR.value, | ||
| tags={"error_status": ErrorStatus.REACTION_FAILED.value}, | ||
| ) | ||
| logger.exception( | ||
| Log.REACTION_FAILED.value, | ||
| extra=extra, | ||
| ) | ||
|
|
||
|
|
||
| def handle_issue_comment_event( | ||
| *, | ||
| event_type: str, | ||
| event: Mapping[str, Any], | ||
| organization: Organization, | ||
| repo: Repository, | ||
| integration: RpcIntegration | None = None, | ||
| **kwargs: Any, | ||
| ) -> None: | ||
| """ | ||
| Handle issue_comment webhook events for PR review commands. | ||
| """ | ||
| comment = event.get("comment", {}) | ||
| comment_id = comment.get("id") | ||
| comment_body = comment.get("body") | ||
|
|
||
| if not has_code_review_enabled(organization): | ||
| return | ||
|
|
||
| if not is_pr_review_command(comment_body or ""): | ||
| return | ||
|
|
||
| if comment_id: | ||
| if not options.get("github.webhook.issue-comment"): | ||
| _add_eyes_reaction_to_comment(integration, organization, repo, str(comment_id)) | ||
|
|
||
| # Import here to avoid circular dependency with handlers | ||
| from .handlers import _schedule_task | ||
|
|
||
| _schedule_task(event_type, event, organization, repo) | ||
|
Comment on lines
+107
to
+114
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. Bug: The 🔍 Detailed AnalysisIn 💡 Suggested FixMove the 🤖 Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews. |
||
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.
You can either let the exception raise and handle it in the caller or you need to return the outcome to the caller and exit.
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.
On this one, I was purposely having the helper
_add_eyes_reaction_to_commentlog and swallow the exception so every caller of the helper wouldn't need to wrap in a try/catch. I would like us to still forward to seer even if for some reason the 👀 posting failed. For example if we hit a rate limit here, but still want the PR review itself to proceed. What do you think?