Skip to content

Commit 7db459a

Browse files
authored
Fix #1205: add phabricator attachment links to jira (#1206)
1 parent 3f8bf31 commit 7db459a

File tree

8 files changed

+142
-1
lines changed

8 files changed

+142
-1
lines changed

docs/actions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,4 @@ linked Jira issue status to "Closed". If the bug changes to a status not listed
161161
- `sync_whiteboard_labels`:
162162
Syncs the Bugzilla whitboard tags field to the Jira labels field.
163163
- `maybe_update_components`: looks at the component that's set on the bug (if any) and any components added to the project configuration with the `jira_components` parameter (see above). If those components are available on the Jira side as well, they're added to the Jira issue
164+
- `maybe_add_phabricator_link`: looks at an attachment and if it is a phabricator attachment, it gets added as a link or updated if the attachment was previously added.

jbi/bugzilla/models.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datetime
22
import logging
3+
import re
34
from typing import Any, Optional, TypedDict
45
from urllib.parse import ParseResult, urlparse
56

@@ -102,6 +103,36 @@ class WebhookAttachment(BaseModel, frozen=True):
102103
is_patch: bool
103104
is_private: bool
104105

106+
def is_phabricator_patch(self) -> bool:
107+
"""
108+
Returns True if this attachment is a phabricator patch attachment.
109+
110+
We identify an attachment as a patch if the content type contains "phabricator-request"
111+
"""
112+
return "phabricator-request" in self.content_type
113+
114+
def phabricator_url(self, base_url: str) -> str | None:
115+
"""
116+
Returns the Phabricator patch URL from the file name if the attachment is a patch, otherwise, it returns None.
117+
"""
118+
if not self.is_phabricator_patch():
119+
return None
120+
121+
match = re.search(r'D\d+', self.file_name)
122+
if not match:
123+
logger.info(
124+
"Expected that attachment with name %s is a patch, but we couldn't extract the phabricator id (e.g D1234)",
125+
self.file_name,
126+
extra={
127+
"bug": {
128+
"id": self.id,
129+
}
130+
},
131+
)
132+
return None
133+
134+
revision_id = match.group(0)
135+
return f"{base_url}/{revision_id}"
105136

106137
class Bug(BaseModel, frozen=True):
107138
"""Bugzilla Bug Object"""

jbi/environment.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class Settings(BaseSettings):
4040
bugzilla_base_url: str = "https://bugzilla-dev.allizom.org"
4141
bugzilla_api_key: str
4242

43+
# Phabricator
44+
phabricator_base_url: str = "https://phabricator.services.mozilla.com"
45+
4346
# Logging
4447
log_level: str = "info"
4548
log_format: str = "json" # set to "text" for human-readable logs

jbi/jira/service.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ def add_jira_comment(self, context: ActionContext):
132132
formatted_comment += (
133133
f"\n*Filename*: {att.file_name} ({att.content_type})"
134134
)
135+
if phabricator_url := att.phabricator_url(base_url=settings.phabricator_base_url):
136+
formatted_comment += f"\n*Phabricator URL*: {phabricator_url}"
135137

136138
else:
137139
comment = context.bug.comment

jbi/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class ActionSteps(BaseModel, frozen=True):
4545
]
4646
attachment: list[str] = [
4747
"create_comment",
48+
"maybe_add_phabricator_link",
4849
]
4950

5051
@field_validator("*")

jbi/steps.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,51 @@ def add_link_to_bugzilla(
118118
context = context.append_responses(jira_response)
119119
return (StepStatus.SUCCESS, context)
120120

121+
def maybe_add_phabricator_link(
122+
context: ActionContext,
123+
*,
124+
jira_service: JiraService,
125+
) -> StepResult:
126+
"""Add a phabricator link to the Jira issue if an attachment is a phabricator attachment"""
127+
if context.event.target != "attachment" or not context.bug.attachment:
128+
return (StepStatus.NOOP, context)
129+
130+
attachment = context.bug.attachment
131+
132+
settings = get_settings()
133+
phabricator_url = attachment.phabricator_url(base_url=settings.phabricator_base_url)
134+
135+
if not phabricator_url:
136+
return (StepStatus.NOOP, context)
137+
138+
description = attachment.description
139+
if attachment.is_obsolete:
140+
description = f"{0} - {1}".format("Abandoned", attachment.description)
141+
142+
issue_key = context.jira.issue
143+
144+
jira_response = jira_service.client.create_or_update_issue_remote_links(
145+
issue_key=issue_key,
146+
global_id=f"{context.bug.id}-{attachment.id}",
147+
link_url=phabricator_url,
148+
title=description,
149+
)
150+
151+
if jira_response:
152+
logger.info(
153+
"Phabricator patch added or updated in Jira issue %s",
154+
issue_key,
155+
extra=context.update(operation=Operation.LINK).model_dump(),
156+
)
157+
context = context.append_responses(jira_response)
158+
return (StepStatus.SUCCESS, context)
159+
else:
160+
logger.info(
161+
"Failed to add or update phabricator url in Jira issue %s",
162+
issue_key,
163+
)
164+
165+
return (StepStatus.NOOP, context)
121166

122167
def maybe_delete_duplicate(
123168
context: ActionContext,

tests/unit/bugzilla/test_models.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,31 @@ def test_attachments_flags(bug_factory):
66
],
77
)
88
# not raising is a success
9+
10+
def test_patch_attachment_returns_valid_phabricator_url(bug_factory):
11+
bug = bug_factory.build(
12+
with_attachment=True,
13+
attachment__is_patch=True,
14+
attachment__file_name="phabricator-D262912-url.txt",
15+
attachment__content_type="text/x-phabricator-request",
16+
)
17+
base_url = "https://phabricator.services.mozilla.com"
18+
assert bug.attachment.phabricator_url(base_url=base_url) == "https://phabricator.services.mozilla.com/D262912"
19+
20+
def test_non_patch_attachment_returns_no_phabricator_url(bug_factory):
21+
bug = bug_factory.build(
22+
with_attachment=True,
23+
attachment__file_name="screenshot.png",
24+
attachment__content_type="image/png",
25+
)
26+
base_url = "https://phabricator.services.mozilla.com"
27+
assert bug.attachment.phabricator_url(base_url=base_url) is None
28+
29+
def test_patch_attachment_with_unexpected_file_name_returns_no_phabricator_url(bug_factory):
30+
bug = bug_factory.build(
31+
with_attachment=True,
32+
attachment__file_name="phabricator-F262912-url.txt", # normally should be "phabricator-D<>-url.txt"
33+
attachment__content_type="text/x-phabricator-request",
34+
)
35+
base_url = "https://phabricator.services.mozilla.com"
36+
assert bug.attachment.phabricator_url(base_url=base_url) is None

tests/unit/test_steps.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"maybe_assign_jira_user",
2626
"maybe_update_issue_status",
2727
"maybe_update_issue_resolution",
28+
"maybe_add_phabricator_link",
2829
],
2930
"comment": [
3031
"create_comment",
@@ -245,7 +246,36 @@ def test_added_attachment(
245246

246247
mocked_jira.issue_add_comment.assert_called_once_with(
247248
issue_key="JBI-234",
248-
comment="*[email protected]* created an attachment:\n*Description*: Bug 1337 - Stop war r?peace\n*Filename*: phabricator-D1234-url.txt (text/x-phabricator-request)",
249+
comment="*[email protected]* created an attachment:\n*Description*: Bug 1337 - Stop war r?peace\n*Filename*: phabricator-D1234-url.txt (text/x-phabricator-request)\n*Phabricator URL*: https://phabricator.services.mozilla.com/D1234",
250+
)
251+
252+
def test_added_phabricator_attachment(
253+
action_context_factory, mocked_jira, action_params_factory
254+
):
255+
phabricator_attachment_context = action_context_factory(
256+
operation=Operation.ATTACHMENT,
257+
bug__with_attachment=True,
258+
bug__id=5555,
259+
bug__attachment__is_patch=True,
260+
bug__attachment__is_obsolete=False,
261+
bug__attachment__id=123456,
262+
bug__attachment__file_name="phabricator-D1234-url.txt",
263+
bug__attachment__description="Bug 1234 - Fix all the bugs",
264+
bug__attachment__content_type="text/x-phabricator-request",
265+
event__target="attachment",
266+
jira__issue="JBI-234",
267+
)
268+
callable_object = Executor(
269+
action_params_factory(jira_project_key=phabricator_attachment_context.jira.project)
270+
)
271+
272+
callable_object(context=phabricator_attachment_context)
273+
274+
mocked_jira.create_or_update_issue_remote_links.assert_called_once_with(
275+
issue_key="JBI-234",
276+
link_url="https://phabricator.services.mozilla.com/D1234",
277+
title="Bug 1234 - Fix all the bugs",
278+
global_id="5555-123456",
249279
)
250280

251281

0 commit comments

Comments
 (0)