Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
47 changes: 46 additions & 1 deletion src/sentry/models/repository.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import logging
from typing import Any

from django.contrib.postgres.fields.array import ArrayField
Expand All @@ -10,7 +11,7 @@
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,11 +25,15 @@
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
from sentry.utils.email import MessageBuilder

_default_logger = logging.getLogger(__name__)


@region_silo_model
class Repository(Model):
Expand Down Expand Up @@ -147,6 +152,46 @@ def sanitize_relocation_json(
sanitizer.set_string(json, SanitizableField(model_name, "provider"))
json["fields"]["languages"] = "[]"

def save(self, *args: Any, **kwargs: Any) -> None:
is_new = self.pk is None
super().save(*args, **kwargs)
if is_new:
try:
self._handle_auto_enable_code_review()
except Exception:
_default_logger.exception(
"Failed to auto-enable code review on repository creation",
extra={"repository_id": self.id},
)
pass

def _handle_auto_enable_code_review(self) -> None:
"""
When a new repository is created, auto enable code review if applicable.
"""
SUPPORTED_PROVIDERS = {"integrations:github"}

if self.provider not in SUPPORTED_PROVIDERS:
return

if OrganizationOption.objects.get_value(
organization=self.organization_id,
key="sentry:auto_enable_code_review",
default=False,
):
triggers = OrganizationOption.objects.get_value(
organization=self.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=self.id,
defaults={"enabled_code_review": True, "code_review_triggers": triggers},
)


def on_delete(instance, actor: RpcUser | None = None, **kwargs):
"""
Expand Down
148 changes: 148 additions & 0 deletions tests/sentry/models/test_repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from unittest.mock import patch

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 +69,146 @@ 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 auto-enabling code review settings 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

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

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

with patch.object(
Repository,
"_handle_auto_enable_code_review",
side_effect=Exception("Test exception"),
):
repo = Repository.objects.create(
organization_id=org.id,
name="test-repo",
provider="integrations:github",
)

# Repository should still be saved
assert Repository.objects.filter(id=repo.id).exists()
assert repo.name == "test-repo"
Loading