Skip to content

Commit bf2f564

Browse files
authored
feat(preprod): add commitcomparison model (#97292)
Adds a new `CommitComparison` model to track Git data for Emerge customers migrating to Sentry. See: https://www.notion.so/sentry/Emerge-Git-User-Experience-2148b10e4b5d80829406c85e010bd083
1 parent 2407603 commit bf2f564

File tree

4 files changed

+164
-1
lines changed

4 files changed

+164
-1
lines changed

migrations_lockfile.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ preprod: 0013_binary_uuid
2727

2828
replays: 0006_add_bulk_delete_job
2929

30-
sentry: 0963_scheduleddeletion_json_field
30+
sentry: 0964_add_commitcomparison_table
3131

3232
social_auth: 0003_social_auth_json_field
3333

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Generated by Django 5.2.1 on 2025-08-06 20:24
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
import sentry.db.models.fields.bounded
7+
import sentry.db.models.fields.foreignkey
8+
from sentry.new_migrations.migrations import CheckedMigration
9+
10+
11+
class Migration(CheckedMigration):
12+
# This flag is used to mark that a migration shouldn't be automatically run in production.
13+
# This should only be used for operations where it's safe to run the migration after your
14+
# code has deployed. So this should not be used for most operations that alter the schema
15+
# of a table.
16+
# Here are some things that make sense to mark as post deployment:
17+
# - Large data migrations. Typically we want these to be run manually so that they can be
18+
# monitored and not block the deploy for a long period of time while they run.
19+
# - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
20+
# run this outside deployments so that we don't block them. Note that while adding an index
21+
# is a schema change, it's completely safe to run the operation after the code has deployed.
22+
# Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment
23+
24+
is_post_deployment = False
25+
26+
dependencies = [
27+
("sentry", "0963_scheduleddeletion_json_field"),
28+
]
29+
30+
operations = [
31+
migrations.CreateModel(
32+
name="CommitComparison",
33+
fields=[
34+
(
35+
"id",
36+
sentry.db.models.fields.bounded.BoundedBigAutoField(
37+
primary_key=True, serialize=False
38+
),
39+
),
40+
("date_updated", models.DateTimeField(auto_now=True)),
41+
("date_added", models.DateTimeField(auto_now_add=True)),
42+
(
43+
"organization_id",
44+
sentry.db.models.fields.bounded.BoundedBigIntegerField(db_index=True),
45+
),
46+
("head_sha", models.CharField(max_length=64)),
47+
("base_sha", models.CharField(max_length=64, null=True)),
48+
("provider", models.CharField(max_length=64, null=True)),
49+
("head_repo_name", models.CharField(max_length=255)),
50+
("base_repo_name", models.CharField(max_length=255, null=True)),
51+
("head_ref", models.CharField(max_length=255, null=True)),
52+
("base_ref", models.CharField(max_length=255, null=True)),
53+
("pr_number", models.PositiveIntegerField(null=True)),
54+
(
55+
"base_commit",
56+
sentry.db.models.fields.foreignkey.FlexibleForeignKey(
57+
null=True,
58+
on_delete=django.db.models.deletion.SET_NULL,
59+
related_name="base_commit_set",
60+
to="sentry.commit",
61+
),
62+
),
63+
(
64+
"head_commit",
65+
sentry.db.models.fields.foreignkey.FlexibleForeignKey(
66+
null=True,
67+
on_delete=django.db.models.deletion.SET_NULL,
68+
related_name="head_commit_set",
69+
to="sentry.commit",
70+
),
71+
),
72+
],
73+
options={
74+
"db_table": "sentry_commitcomparison",
75+
"indexes": [
76+
models.Index(
77+
fields=["organization_id", "head_repo_name", "head_sha"],
78+
name="sentry_comm_organiz_d9bea9_idx",
79+
),
80+
models.Index(
81+
fields=["organization_id", "head_repo_name", "base_sha"],
82+
name="sentry_comm_organiz_2c6634_idx",
83+
),
84+
],
85+
"constraints": [
86+
models.UniqueConstraint(
87+
condition=models.Q(("base_sha__isnull", False)),
88+
fields=("organization_id", "head_sha", "base_sha"),
89+
name="unique_commit_comparison",
90+
),
91+
models.UniqueConstraint(
92+
condition=models.Q(("base_sha__isnull", True)),
93+
fields=("organization_id", "head_sha"),
94+
name="unique_single_commit",
95+
),
96+
],
97+
},
98+
),
99+
]

src/sentry/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .broadcast import * # NOQA
1717
from .commit import * # NOQA
1818
from .commitauthor import * # NOQA
19+
from .commitcomparison import * # NOQA
1920
from .commitfilechange import * # noqa
2021
from .counter import * # NOQA
2122
from .dashboard import * # NOQA

src/sentry/models/commitcomparison.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from django.db import models
2+
3+
from sentry.backup.scopes import RelocationScope
4+
from sentry.db.models.base import DefaultFieldsModel, region_silo_model
5+
from sentry.db.models.fields.bounded import BoundedBigIntegerField
6+
from sentry.db.models.fields.foreignkey import FlexibleForeignKey
7+
8+
9+
@region_silo_model
10+
class CommitComparison(DefaultFieldsModel):
11+
"""
12+
Captures Git information provided by our users and links with our richer data models.
13+
Can represent a single commit (main branch) or a comparison between commits (PR branch).
14+
See https://www.notion.so/sentry/Emerge-Git-User-Experience-2148b10e4b5d80829406c85e010bd083
15+
"""
16+
17+
__relocation_scope__ = RelocationScope.Excluded
18+
19+
organization_id = BoundedBigIntegerField(db_index=True)
20+
21+
# Core commit information
22+
head_sha = models.CharField(max_length=64)
23+
base_sha = models.CharField(max_length=64, null=True) # merge-base
24+
25+
# Repository information
26+
provider = models.CharField(max_length=64, null=True)
27+
head_repo_name = models.CharField(max_length=255) # "owner/repo"
28+
base_repo_name = models.CharField(max_length=255, null=True) # for forks
29+
30+
# Branch/ref information
31+
head_ref = models.CharField(max_length=255, null=True) # "feature/xyz"
32+
base_ref = models.CharField(max_length=255, null=True) # "main"
33+
34+
# Pull request information
35+
pr_number = models.PositiveIntegerField(null=True)
36+
37+
# Sentry data, can be hydrated separately
38+
head_commit = FlexibleForeignKey(
39+
"sentry.Commit", null=True, on_delete=models.SET_NULL, related_name="head_commit_set"
40+
)
41+
base_commit = FlexibleForeignKey(
42+
"sentry.Commit", null=True, on_delete=models.SET_NULL, related_name="base_commit_set"
43+
)
44+
45+
class Meta:
46+
app_label = "sentry"
47+
db_table = "sentry_commitcomparison"
48+
indexes = [
49+
models.Index(fields=["organization_id", "head_repo_name", "head_sha"]),
50+
models.Index(fields=["organization_id", "head_repo_name", "base_sha"]),
51+
]
52+
constraints = [
53+
models.UniqueConstraint(
54+
fields=["organization_id", "head_sha", "base_sha"],
55+
condition=models.Q(base_sha__isnull=False),
56+
name="unique_commit_comparison",
57+
),
58+
models.UniqueConstraint(
59+
fields=["organization_id", "head_sha"],
60+
condition=models.Q(base_sha__isnull=True),
61+
name="unique_single_commit",
62+
),
63+
]

0 commit comments

Comments
 (0)