Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions src/sentry/models/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

from django.contrib.postgres.fields.array import ArrayField
from django.db import models
from django.db.models.signals import pre_delete
from django.db.models.signals import post_save, pre_delete
from django.utils import timezone

from sentry.backup.dependencies import NormalizedModelName, get_model_name
from sentry.backup.sanitize import SanitizableField, Sanitizer
from sentry.backup.scopes import RelocationScope
from sentry.constants import ObjectStatus
from sentry.constants import DEFAULT_CODE_REVIEW_TRIGGERS, ObjectStatus
from sentry.db.models import (
BoundedBigIntegerField,
BoundedPositiveIntegerField,
Expand All @@ -24,6 +24,8 @@
rename_on_pending_deletion,
reset_pending_deletion_field_names,
)
from sentry.models.options.organization_option import OrganizationOption
from sentry.models.repositorysettings import RepositorySettings
from sentry.organizations.services.organization.service import organization_service
from sentry.signals import pending_delete
from sentry.users.services.user import RpcUser
Expand Down Expand Up @@ -186,3 +188,40 @@ def handle_exception(e):
sender=Repository,
weak=False,
)


def handle_auto_enable_code_review(instance: Repository) -> None:
"""
When a new repository is created, auto enable code review if applicable.
"""

SUPPORTED_PROVIDERS = {"integrations:github"}

if instance.provider not in SUPPORTED_PROVIDERS:
return

if OrganizationOption.objects.get_value(
organization=instance.organization_id,
key="sentry:auto_enable_code_review",
default=False,
):
triggers = OrganizationOption.objects.get_value(
organization=instance.organization_id,
key="sentry:default_code_review_triggers",
default=DEFAULT_CODE_REVIEW_TRIGGERS,
)
if not isinstance(triggers, list):
triggers = DEFAULT_CODE_REVIEW_TRIGGERS

RepositorySettings.objects.get_or_create(
repository_id=instance.id,
defaults={"enabled_code_review": True, "code_review_triggers": triggers},
)


def on_repository_created(sender, instance, created, **kwargs):
if created:
handle_auto_enable_code_review(instance)


post_save.connect(on_repository_created, sender=Repository, weak=False)
Copy link
Member

Choose a reason for hiding this comment

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

After talking through this a bit more with @kcons, I think we should move this to override the Repository model's save method, ensuring both of these models are created within a transaction. It's a bit safer, and it's a pattern we follow for our outboxes, which have similar concerns.

Copy link
Member Author

Choose a reason for hiding this comment

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

That makes sense to me. Making the changes now!

122 changes: 122 additions & 0 deletions tests/sentry/models/test_repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from django.core import mail

from sentry.constants import DEFAULT_CODE_REVIEW_TRIGGERS
from sentry.models.options.organization_option import OrganizationOption
from sentry.models.repository import Repository
from sentry.models.repositorysettings import RepositorySettings
from sentry.plugins.providers.dummy import DummyRepositoryProvider
from sentry.testutils.cases import TestCase
from sentry.testutils.helpers.features import with_feature
Expand Down Expand Up @@ -64,3 +67,122 @@ def test_generate_delete_fail_email(self) -> None:
assert msg.context["repo"] == self.repo
assert msg.context["error_message"] == "Test error message"
assert msg.context["provider_name"] == "Example"


class RepositoryCodeReviewSettingsTest(TestCase):
"""Tests for the post_save signal that creates RepositorySettings on repository creation."""

def test_no_settings_created_when_auto_enable_disabled(self):
org = self.create_organization()

repo = Repository.objects.create(
organization_id=org.id,
name="test-repo",
provider="integrations:github",
)

assert not RepositorySettings.objects.filter(repository=repo).exists()

def test_settings_created_when_auto_enable_enabled(self):
org = self.create_organization()

OrganizationOption.objects.set_value(
organization=org,
key="sentry:auto_enable_code_review",
value=True,
)

repo = Repository.objects.create(
organization_id=org.id,
name="test-repo",
provider="integrations:github",
)

settings = RepositorySettings.objects.get(repository=repo)
assert settings.enabled_code_review is True
assert settings.code_review_triggers == DEFAULT_CODE_REVIEW_TRIGGERS

def test_settings_created_with_triggers(self):
org = self.create_organization()

OrganizationOption.objects.set_value(
organization=org,
key="sentry:auto_enable_code_review",
value=True,
)
OrganizationOption.objects.set_value(
organization=org,
key="sentry:default_code_review_triggers",
value=["on_new_commit", "on_ready_for_review"],
)

repo = Repository.objects.create(
organization_id=org.id,
name="test-repo",
provider="integrations:github",
)

settings = RepositorySettings.objects.get(repository=repo)
assert settings.enabled_code_review is True
assert settings.code_review_triggers == ["on_new_commit", "on_ready_for_review"]

def test_no_settings_for_unsupported_provider(self):
org = self.create_organization()

OrganizationOption.objects.set_value(
organization=org,
key="sentry:auto_enable_code_review",
value=True,
)

repo = Repository.objects.create(
organization_id=org.id,
name="test-repo",
provider="unsupported_provider",
)

assert not RepositorySettings.objects.filter(repository=repo).exists()

def test_invalid_triggers_type_defaults_to_empty_list(self):
org = self.create_organization()

OrganizationOption.objects.set_value(
organization=org,
key="sentry:auto_enable_code_review",
value=True,
)
# Set invalid triggers type (string instead of list)
OrganizationOption.objects.set_value(
organization=org,
key="sentry:default_code_review_triggers",
value="invalid_string",
)

repo = Repository.objects.create(
organization_id=org.id,
name="test-repo",
provider="integrations:github",
)

settings = RepositorySettings.objects.get(repository=repo)
assert settings.code_review_triggers == DEFAULT_CODE_REVIEW_TRIGGERS

def test_settings_not_duplicated_on_update(self):
org = self.create_organization()

OrganizationOption.objects.set_value(
organization=org,
key="sentry:auto_enable_code_review",
value=True,
)

repo = Repository.objects.create(
organization_id=org.id,
name="test-repo",
provider="integrations:github",
)

repo.name = "updated-repo"
repo.save()

assert RepositorySettings.objects.filter(repository=repo).count() == 1
Loading