Skip to content

Commit 9540056

Browse files
authored
Revert "feat(commit-context): Refactor Issue Owner auto-assignment (#40048)" (#40081)
This reverts commit 712ba34.
1 parent 182b5f1 commit 9540056

File tree

5 files changed

+217
-472
lines changed

5 files changed

+217
-472
lines changed

src/sentry/models/activity.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,3 @@ class ActivityIntegration(Enum):
145145
PROJECT_OWNERSHIP = "projectOwnership"
146146
SLACK = "slack"
147147
MSTEAMS = "msteams"
148-
SUSPECT_COMMITTER = "suspectCommitter"

src/sentry/models/projectownership.py

Lines changed: 49 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TYPE_CHECKING, Any, Mapping, Optional, Sequence, Tuple, Union
1+
from typing import Any, Mapping, Optional, Sequence, Tuple, Union
22

33
from django.db import models
44
from django.db.models.signals import post_delete, post_save
@@ -7,14 +7,10 @@
77
from sentry.db.models import Model, region_silo_only_model, sane_repr
88
from sentry.db.models.fields import FlexibleForeignKey, JSONField
99
from sentry.models import ActorTuple
10-
from sentry.models.groupowner import OwnerRuleType
1110
from sentry.ownership.grammar import Rule, load_schema, resolve_actors
1211
from sentry.utils import metrics
1312
from sentry.utils.cache import cache
1413

15-
if TYPE_CHECKING:
16-
from sentry.models import Team, User
17-
1814
READ_CACHE_DURATION = 3600
1915

2016

@@ -131,46 +127,44 @@ def get_owners(
131127
return ordered_actors, rules
132128

133129
@classmethod
134-
def _hydrate_rules(cls, project_id, rules, type=OwnerRuleType.OWNERSHIP_RULE.value):
130+
def _find_actors(cls, project_id, rules, limit):
135131
"""
136132
Get the last matching rule to take the most precedence.
137133
"""
138134
owners = [owner for rule in rules for owner in rule.owners]
135+
owners.reverse()
139136
actors = {
140137
key: val
141138
for key, val in resolve_actors({owner for owner in owners}, project_id).items()
142139
if val
143140
}
144-
result = [
145-
(rule, ActorTuple.resolve_many([actors[owner] for owner in rule.owners]), type)
146-
for rule in rules
147-
]
148-
return result
141+
actors = [actors[owner] for owner in owners if owner in actors][:limit]
142+
return actors
149143

150144
@classmethod
151-
def get_issue_owners(
152-
cls, project_id, data, limit=2
153-
) -> Sequence[
154-
Tuple[
155-
"Rule",
156-
Sequence[Union["Team", "User"]],
157-
Union[OwnerRuleType.OWNERSHIP_RULE.value, OwnerRuleType.CODEOWNERS.value],
158-
]
159-
]:
145+
def get_autoassign_owners(cls, project_id, data, limit=2):
160146
"""
161-
Get the issue owners for a project if there are any.
147+
Get the auto-assign owner for a project if there are any.
162148
163149
We combine the schemas from IssueOwners and CodeOwners.
164150
165-
Returns list of tuple (rule, owners, rule_type)
151+
Returns a tuple of (
152+
auto_assignment_enabled: boolean,
153+
list_of_owners,
154+
assigned_by_codeowners: boolean,
155+
auto_assigned_rule: Rule | None,
156+
owner_source: List[str]
157+
)
166158
"""
167159
from sentry.models import ProjectCodeOwners
160+
from sentry.models.groupowner import OwnerRuleType
168161

169162
with metrics.timer("projectownership.get_autoassign_owners"):
170163
ownership = cls.get_ownership_cached(project_id)
171164
codeowners = ProjectCodeOwners.get_codeowners_cached(project_id)
165+
assigned_by_codeowners = False
172166
if not (ownership or codeowners):
173-
return []
167+
return False, [], assigned_by_codeowners, None, []
174168

175169
if not ownership:
176170
ownership = cls(project_id=project_id)
@@ -181,120 +175,43 @@ def get_issue_owners(
181175
)
182176

183177
if not (codeowners_rules or ownership_rules):
184-
return []
185-
186-
hydrated_ownership_rules = cls._hydrate_rules(
187-
project_id, ownership_rules, OwnerRuleType.OWNERSHIP_RULE.value
188-
)
189-
hydrated_codeowners_rules = cls._hydrate_rules(
190-
project_id, codeowners_rules, OwnerRuleType.CODEOWNERS.value
191-
)
192-
193-
rules_in_evaluation_order = [
194-
*hydrated_ownership_rules[::-1],
195-
*hydrated_codeowners_rules[::-1],
196-
]
197-
rules_with_owners = list(
198-
filter(
199-
lambda item: len(item[1]) > 0,
200-
rules_in_evaluation_order,
201-
)
178+
return ownership.auto_assignment, [], assigned_by_codeowners, None, []
179+
180+
ownership_actors = cls._find_actors(project_id, ownership_rules, limit)
181+
codeowners_actors = cls._find_actors(project_id, codeowners_rules, limit)
182+
183+
# Can happen if the ownership rule references a user/team that no longer
184+
# is assigned to the project or has been removed from the org.
185+
if not (ownership_actors or codeowners_actors):
186+
return ownership.auto_assignment, [], assigned_by_codeowners, None, []
187+
188+
# Ownership rules take precedence over codeowner rules.
189+
actors = [*ownership_actors, *codeowners_actors][:limit]
190+
actor_source = [
191+
*([OwnerRuleType.OWNERSHIP_RULE.value] * len(ownership_actors)),
192+
*([OwnerRuleType.CODEOWNERS.value] * len(codeowners_actors)),
193+
][:limit]
194+
195+
# Only the first item in the list is used for assignment, the rest are just used to suggest suspect owners.
196+
# So if ownership_actors is empty, it will be assigned by codeowners_actors
197+
if len(ownership_actors) == 0:
198+
assigned_by_codeowners = True
199+
200+
# The rule that would be used for auto assignment
201+
auto_assignment_rule = (
202+
codeowners_rules[0] if assigned_by_codeowners else ownership_rules[0]
202203
)
203204

204-
return rules_with_owners[:limit]
205-
206-
@classmethod
207-
def handle_auto_assignment(cls, project_id, event):
208-
"""
209-
Get the auto-assign owner for a project if there are any.
205+
from sentry.models import ActorTuple
210206

211-
We combine the schemas from IssueOwners and CodeOwners.
212-
213-
"""
214-
from sentry import analytics
215-
from sentry.models import ActivityIntegration, GroupAssignee, GroupOwner, GroupOwnerType
216-
217-
with metrics.timer("projectownership.get_autoassign_owners"):
218-
ownership = cls.get_ownership_cached(project_id)
219-
queue = []
220-
221-
if ownership.suspect_committer_auto_assignment:
222-
try:
223-
committer = GroupOwner.objects.filter(
224-
group=event.group,
225-
type=GroupOwnerType.SUSPECT_COMMIT.value,
226-
project_id=project_id,
227-
)
228-
except GroupOwner.DoesNotExist:
229-
committer = []
230-
231-
if len(committer) > 0:
232-
queue.append(
233-
(
234-
committer[0].owner(),
235-
{
236-
"integration": ActivityIntegration.SUSPECT_COMMITTER.value,
237-
},
238-
)
239-
)
240-
241-
# Skip if we already found a Suspect Committer
242-
if ownership.auto_assignment and len(queue) == 0:
243-
ownership_rules = GroupOwner.objects.filter(
244-
group=event.group,
245-
type=GroupOwnerType.OWNERSHIP_RULE.value,
246-
project_id=project_id,
247-
)
248-
codeowners = GroupOwner.objects.filter(
249-
group=event.group,
250-
type=GroupOwnerType.CODEOWNERS.value,
251-
project_id=project_id,
252-
)
253-
254-
for issue_owner in ownership_rules:
255-
queue.append(
256-
(
257-
issue_owner.owner(),
258-
{
259-
"integration": ActivityIntegration.PROJECT_OWNERSHIP.value,
260-
"rule": (issue_owner.context or {}).get("rule", ""),
261-
},
262-
)
263-
)
264-
265-
for issue_owner in codeowners:
266-
queue.append(
267-
(
268-
issue_owner.owner(),
269-
{
270-
"integration": ActivityIntegration.CODEOWNERS.value,
271-
"rule": (issue_owner.context or {}).get("rule", ""),
272-
},
273-
)
274-
)
275-
276-
try:
277-
owner, details = queue.pop(0)
278-
except IndexError:
279-
return
280-
281-
assignment = GroupAssignee.objects.assign(
282-
event.group,
283-
owner.resolve(),
284-
create_only=True,
285-
extra=details,
207+
return (
208+
ownership.auto_assignment,
209+
ActorTuple.resolve_many(actors),
210+
assigned_by_codeowners,
211+
auto_assignment_rule,
212+
actor_source,
286213
)
287214

288-
if assignment["new_assignment"] or assignment["updated_assignment"]:
289-
analytics.record(
290-
"codeowners.assignment"
291-
if details.get("integration") == ActivityIntegration.CODEOWNERS.value
292-
else "issueowners.assignment",
293-
organization_id=ownership.project.organization_id,
294-
project_id=project_id,
295-
group_id=event.group.id,
296-
)
297-
298215
@classmethod
299216
def _matching_ownership_rules(
300217
cls, ownership: "ProjectOwnership", project_id: int, data: Mapping[str, Any]

0 commit comments

Comments
 (0)