diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 1ad5e7d42e8552..604ba5cb0c73b5 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -31,7 +31,7 @@ releases: 0004_cleanup_failed_safe_deletes replays: 0006_add_bulk_delete_job -sentry: 1012_add_event_id_to_open_period +sentry: 1013_add_repositorysettings_table social_auth: 0003_social_auth_json_field diff --git a/src/sentry/migrations/1013_add_repositorysettings_table.py b/src/sentry/migrations/1013_add_repositorysettings_table.py new file mode 100644 index 00000000000000..918f25f7dd8356 --- /dev/null +++ b/src/sentry/migrations/1013_add_repositorysettings_table.py @@ -0,0 +1,61 @@ +# Generated by Django 5.2.8 on 2025-12-09 22:43 + +import django.contrib.postgres.fields +import django.db.models.deletion +from django.db import migrations, models + +import sentry.db.models.fields.bounded +import sentry.db.models.fields.foreignkey +from sentry.new_migrations.migrations import CheckedMigration + + +class Migration(CheckedMigration): + # This flag is used to mark that a migration shouldn't be automatically run in production. + # This should only be used for operations where it's safe to run the migration after your + # code has deployed. So this should not be used for most operations that alter the schema + # of a table. + # Here are some things that make sense to mark as post deployment: + # - Large data migrations. Typically we want these to be run manually so that they can be + # monitored and not block the deploy for a long period of time while they run. + # - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to + # run this outside deployments so that we don't block them. Note that while adding an index + # is a schema change, it's completely safe to run the operation after the code has deployed. + # Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment + + is_post_deployment = False + + dependencies = [ + ("sentry", "1012_add_event_id_to_open_period"), + ] + + operations = [ + migrations.CreateModel( + name="RepositorySettings", + fields=[ + ( + "id", + sentry.db.models.fields.bounded.BoundedBigAutoField( + primary_key=True, serialize=False + ), + ), + ("enabled_code_review", models.BooleanField(default=False)), + ( + "code_review_triggers", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=32), default=list, size=None + ), + ), + ( + "repository", + sentry.db.models.fields.foreignkey.FlexibleForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="sentry.repository", + unique=True, + ), + ), + ], + options={ + "db_table": "sentry_repositorysettings", + }, + ), + ] diff --git a/src/sentry/models/__init__.py b/src/sentry/models/__init__.py index c34615e484f620..9f1cfb384b7d9d 100644 --- a/src/sentry/models/__init__.py +++ b/src/sentry/models/__init__.py @@ -104,6 +104,7 @@ from .releaseprojectenvironment import * # NOQA from .releases.release_project import * # NOQA from .repository import * # NOQA +from .repositorysettings import * # NOQA from .rollbackorganization import * # NOQA from .rollbackuser import * # NOQA from .rule import * # NOQA diff --git a/src/sentry/models/repositorysettings.py b/src/sentry/models/repositorysettings.py new file mode 100644 index 00000000000000..4ae894d084966b --- /dev/null +++ b/src/sentry/models/repositorysettings.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from enum import StrEnum + +from django.contrib.postgres.fields.array import ArrayField +from django.db import models + +from sentry.backup.scopes import RelocationScope +from sentry.db.models import FlexibleForeignKey, Model, region_silo_model, sane_repr + + +class CodeReviewTrigger(StrEnum): + ON_COMMAND_PHRASE = "on_command_phrase" + ON_NEW_COMMIT = "on_new_commit" + ON_READY_FOR_REVIEW = "on_ready_for_review" + + @classmethod + def as_choices(cls) -> tuple[tuple[str, str], ...]: + return tuple((trigger.value, trigger.value) for trigger in cls) + + +@region_silo_model +class RepositorySettings(Model): + """ + Stores (organization) repository specific settings. + """ + + __relocation_scope__ = RelocationScope.Global + + repository = FlexibleForeignKey( + "sentry.Repository", on_delete=models.CASCADE, unique=True, db_index=True + ) + enabled_code_review = models.BooleanField(default=False) + code_review_triggers = ArrayField( + models.CharField(max_length=32, choices=CodeReviewTrigger.as_choices()), + default=list, + ) + + class Meta: + app_label = "sentry" + db_table = "sentry_repositorysettings" + + __repr__ = sane_repr("repository_id", "enabled_code_review") diff --git a/src/sentry/testutils/helpers/backups.py b/src/sentry/testutils/helpers/backups.py index 9e3993638a436b..5cafa604f89d35 100644 --- a/src/sentry/testutils/helpers/backups.py +++ b/src/sentry/testutils/helpers/backups.py @@ -99,6 +99,7 @@ from sentry.models.projecttemplate import ProjectTemplate from sentry.models.recentsearch import RecentSearch from sentry.models.relay import Relay, RelayUsage +from sentry.models.repositorysettings import CodeReviewTrigger, RepositorySettings from sentry.models.rule import NeglectedRule, RuleActivity, RuleActivityType from sentry.models.savedsearch import SavedSearch, Visibility from sentry.models.search_common import SearchType @@ -639,6 +640,15 @@ def create_exhaustive_organization( repo.external_id = "https://git.example.com:1234" repo.save() + RepositorySettings.objects.create( + repository=repo, + enabled_code_review=True, + code_review_triggers=[ + CodeReviewTrigger.ON_NEW_COMMIT, + CodeReviewTrigger.ON_READY_FOR_REVIEW, + ], + ) + # Group* group = self.create_group(project=project) group_search_view = GroupSearchView.objects.create(