From 3b5fc9166ec84ddabbe051c9d8be4a84d35e106e Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Thu, 30 Apr 2026 15:02:32 -0700 Subject: [PATCH 1/5] fix(ACI): Re-land GitHub ticket action validation with selective test fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-applies 36ce44664cb (reverted in a630f3f) which requires `repo` and `integration` to be passed when creating a GitHub ticketing action. The revert was triggered by a CI failure in test_rule.py that selective testing had missed: GithubEnterpriseActionHandler is loaded via Django startup side-effects (@registry.register decorator), so the static import scanner had no edge from the handler file to the test file. Fix: add explicit imports of the github and github_enterprise handler packages at the top of test_rule.py so the scanner can detect the dependency going forward. Separately fix: remove `integration` from `required` inside `additional_fields` in the data_schema — the translator stores it as Action.integration_id, not inside additional_fields. --- .../github/handlers/github_handler.py | 38 +++++++++++++++++++ .../handlers/github_enterprise_handler.py | 6 +-- tests/sentry/api/serializers/test_rule.py | 7 ++++ .../validators/actions/test_ticketing.py | 28 +++++++++++++- 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/sentry/integrations/github/handlers/github_handler.py b/src/sentry/integrations/github/handlers/github_handler.py index 41d865f1c1b8..479e6af80936 100644 --- a/src/sentry/integrations/github/handlers/github_handler.py +++ b/src/sentry/integrations/github/handlers/github_handler.py @@ -11,3 +11,41 @@ class GithubActionHandler(TicketingActionHandler): group = ActionHandler.Group.TICKET_CREATION provider_slug = IntegrationProviderSlug.GITHUB + + data_schema = { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "description": "Schema for GitHub ticket creation action data blob", + "properties": { + "dynamic_form_fields": { + "type": "array", + "description": "Dynamic form fields from customer configuration", + "items": {"type": "object"}, + "default": [], + }, + "additional_fields": { + "type": "object", + "description": "Additional fields that aren't part of standard fields", + "additionalProperties": True, + "properties": { + "repo": {"type": "string"}, + "labels": { + "type": ["array", "null"], + "items": {"type": "string"}, + "default": [], + }, + "assignee": { + "type": ["string", "null"], + }, + "integration": { + "type": [ + "string", + ], + }, + }, + "required": ["repo", "integration"], + }, + }, + "required": ["additional_fields"], + "additionalProperties": False, + } diff --git a/src/sentry/integrations/github_enterprise/handlers/github_enterprise_handler.py b/src/sentry/integrations/github_enterprise/handlers/github_enterprise_handler.py index 23c19aab8f9e..96dab93746c0 100644 --- a/src/sentry/integrations/github_enterprise/handlers/github_enterprise_handler.py +++ b/src/sentry/integrations/github_enterprise/handlers/github_enterprise_handler.py @@ -1,13 +1,11 @@ +from sentry.integrations.github.handlers.github_handler import GithubActionHandler from sentry.integrations.types import IntegrationProviderSlug -from sentry.notifications.notification_action.action_handler_registry.base import ( - TicketingActionHandler, -) from sentry.workflow_engine.models import Action from sentry.workflow_engine.registry import action_handler_registry from sentry.workflow_engine.types import ActionHandler @action_handler_registry.register(Action.Type.GITHUB_ENTERPRISE) -class GithubEnterpriseActionHandler(TicketingActionHandler): +class GithubEnterpriseActionHandler(GithubActionHandler): group = ActionHandler.Group.TICKET_CREATION provider_slug = IntegrationProviderSlug.GITHUB_ENTERPRISE diff --git a/tests/sentry/api/serializers/test_rule.py b/tests/sentry/api/serializers/test_rule.py index fcdf5ae5509f..91ff73b1fe07 100644 --- a/tests/sentry/api/serializers/test_rule.py +++ b/tests/sentry/api/serializers/test_rule.py @@ -1,3 +1,10 @@ +# Explicit imports so selective testing can detect when these handlers change. +# They register via @action_handler_registry.register at import time (startup +# side-effect), so without these the static scanner has no edge from handler +# files to this test file. +import sentry.integrations.github.handlers # noqa: F401 +import sentry.integrations.github_enterprise.handlers # noqa: F401 + import responses from django.db import DEFAULT_DB_ALIAS, connections from django.test.utils import CaptureQueriesContext diff --git a/tests/sentry/workflow_engine/endpoints/validators/actions/test_ticketing.py b/tests/sentry/workflow_engine/endpoints/validators/actions/test_ticketing.py index bb9adbd1441e..4672af698816 100644 --- a/tests/sentry/workflow_engine/endpoints/validators/actions/test_ticketing.py +++ b/tests/sentry/workflow_engine/endpoints/validators/actions/test_ticketing.py @@ -32,6 +32,14 @@ def test_validate(self) -> None: assert result is True +class TestGitHubActionValidator(BaseTicketingActionValidatorTest): + def setUp(self) -> None: + super().setUp() + self.valid_data["data"] = { + "additional_fields": {"repo": "owner/test-repo", "integration": "12345"} + } + + class TestJiraActionValidator(BaseTicketingActionValidatorTest): __test__ = True provider = Action.Type.JIRA @@ -47,11 +55,27 @@ class TestAzureDevOpsActionValidator(BaseTicketingActionValidatorTest): provider = Action.Type.AZURE_DEVOPS -class TestGithubActionValidator(BaseTicketingActionValidatorTest): +class TestGithubActionValidator(TestGitHubActionValidator): __test__ = True provider = Action.Type.GITHUB + def test_validate_missing_repo(self) -> None: + data = {**self.valid_data, "data": {}} + validator = BaseActionValidator( + data=data, + context={"organization": self.organization}, + ) + assert validator.is_valid() is False + -class TestGithubEnterpriseActionValidator(BaseTicketingActionValidatorTest): +class TestGithubEnterpriseActionValidator(TestGitHubActionValidator): __test__ = True provider = Action.Type.GITHUB_ENTERPRISE + + def test_validate_missing_repo(self) -> None: + data = {**self.valid_data, "data": {}} + validator = BaseActionValidator( + data=data, + context={"organization": self.organization}, + ) + assert validator.is_valid() is False From aa1b0b900f13f653effbfecec738d48e3f2572a1 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Thu, 30 Apr 2026 15:03:21 -0700 Subject: [PATCH 2/5] fix(ACI): remove integration from required in GitHub action data_schema The IssueAlertMigrator translator moves `integration` from rule action data into Action.integration_id (a dedicated model field), so it never appears in additional_fields. Requiring it in the schema caused a ValidationError on action.save() when migrating existing GitHub/GithubEnterprise rules. --- src/sentry/integrations/github/handlers/github_handler.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/sentry/integrations/github/handlers/github_handler.py b/src/sentry/integrations/github/handlers/github_handler.py index 479e6af80936..fd87ebca34ea 100644 --- a/src/sentry/integrations/github/handlers/github_handler.py +++ b/src/sentry/integrations/github/handlers/github_handler.py @@ -37,13 +37,8 @@ class GithubActionHandler(TicketingActionHandler): "assignee": { "type": ["string", "null"], }, - "integration": { - "type": [ - "string", - ], - }, }, - "required": ["repo", "integration"], + "required": ["repo"], }, }, "required": ["additional_fields"], From 1ad67b3cd3ca56c32d40f1656f0f24111a8e1b25 Mon Sep 17 00:00:00 2001 From: "getsantry[bot]" <66042841+getsantry[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 22:10:37 +0000 Subject: [PATCH 3/5] :hammer_and_wrench: apply pre-commit fixes --- tests/sentry/api/serializers/test_rule.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/sentry/api/serializers/test_rule.py b/tests/sentry/api/serializers/test_rule.py index 91ff73b1fe07..ad95dca00de3 100644 --- a/tests/sentry/api/serializers/test_rule.py +++ b/tests/sentry/api/serializers/test_rule.py @@ -2,15 +2,14 @@ # They register via @action_handler_registry.register at import time (startup # side-effect), so without these the static scanner has no edge from handler # files to this test file. -import sentry.integrations.github.handlers # noqa: F401 -import sentry.integrations.github_enterprise.handlers # noqa: F401 - import responses from django.db import DEFAULT_DB_ALIAS, connections from django.test.utils import CaptureQueriesContext from django.utils import timezone from rest_framework import serializers +import sentry.integrations.github.handlers # noqa: F401 +import sentry.integrations.github_enterprise.handlers # noqa: F401 from sentry.api.serializers import serialize from sentry.api.serializers.models.rule import RuleSerializer, WorkflowEngineRuleSerializer from sentry.integrations.models import OrganizationIntegration From 401b0ea4d533d51e3bf961dd2b1f8a86e109fe92 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Thu, 30 Apr 2026 15:12:56 -0700 Subject: [PATCH 4/5] ruff pls --- tests/sentry/api/serializers/test_rule.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/sentry/api/serializers/test_rule.py b/tests/sentry/api/serializers/test_rule.py index ad95dca00de3..4402c3e64416 100644 --- a/tests/sentry/api/serializers/test_rule.py +++ b/tests/sentry/api/serializers/test_rule.py @@ -1,13 +1,13 @@ -# Explicit imports so selective testing can detect when these handlers change. -# They register via @action_handler_registry.register at import time (startup -# side-effect), so without these the static scanner has no edge from handler -# files to this test file. import responses from django.db import DEFAULT_DB_ALIAS, connections from django.test.utils import CaptureQueriesContext from django.utils import timezone from rest_framework import serializers +# Explicit imports so selective testing can detect when these handlers change. +# They register via @action_handler_registry.register at import time (startup +# side-effect), so without these the static scanner has no edge from handler +# files to this test file. import sentry.integrations.github.handlers # noqa: F401 import sentry.integrations.github_enterprise.handlers # noqa: F401 from sentry.api.serializers import serialize From b239c2baef5bc6d27eb78cfff02c5e08e027aeda Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Thu, 30 Apr 2026 15:23:22 -0700 Subject: [PATCH 5/5] fix(ACI): fix tests broken by GitHub action repo requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - test_action_type_in_migration: add `repo` to GitHub/GithubEnterprise test cases — the schema now requires repo in additional_fields - test_rule.py: fix import order (ruff) --- .../migration_helpers/test_migrate_rule_action.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/sentry/workflow_engine/migration_helpers/test_migrate_rule_action.py b/tests/sentry/workflow_engine/migration_helpers/test_migrate_rule_action.py index 6bdff8ffba0a..13a382627ba7 100644 --- a/tests/sentry/workflow_engine/migration_helpers/test_migrate_rule_action.py +++ b/tests/sentry/workflow_engine/migration_helpers/test_migrate_rule_action.py @@ -834,6 +834,7 @@ def test_action_type_in_migration(self) -> None: { "integration": "1", "id": "sentry.integrations.github.notify_action.GitHubCreateTicketAction", + "repo": "owner/repo", "uuid": "test-uuid", }, Action.Type.GITHUB, @@ -843,6 +844,7 @@ def test_action_type_in_migration(self) -> None: { "integration": "1", "id": "sentry.integrations.github_enterprise.notify_action.GitHubEnterpriseCreateTicketAction", + "repo": "owner/repo", "uuid": "test-uuid", }, Action.Type.GITHUB_ENTERPRISE,