Skip to content

Commit 64d53a3

Browse files
committed
feat(azure-devops): CODEOWNERS support
1 parent b296710 commit 64d53a3

File tree

4 files changed

+59
-4
lines changed

4 files changed

+59
-4
lines changed

src/sentry/integrations/vsts/client.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from sentry.constants import ObjectStatus
1212
from sentry.exceptions import InvalidIdentity
13-
from sentry.integrations.base import IntegrationFeatureNotImplementedError
1413
from sentry.integrations.client import ApiClient
1514
from sentry.integrations.services.integration.service import integration_service
1615
from sentry.integrations.source_code_management.repository import RepositoryClient
@@ -172,12 +171,12 @@ def identity(self):
172171

173172
def request(self, method: str, *args: Any, **kwargs: Any) -> Any:
174173
api_preview = kwargs.pop("api_preview", False)
175-
new_headers = prepare_headers(
174+
base_headers = prepare_headers(
176175
api_version=self.api_version,
177176
method=method,
178177
api_version_preview=self.api_version_preview if api_preview else "",
179178
)
180-
kwargs.setdefault("headers", {}).update(new_headers)
179+
kwargs["headers"] = {**base_headers, **(kwargs.get("headers", {}))}
181180

182181
return self._request(method, *args, **kwargs)
183182

@@ -450,4 +449,19 @@ def check_file(self, repo: Repository, path: str, version: str | None) -> object
450449
def get_file(
451450
self, repo: Repository, path: str, ref: str | None, codeowners: bool = False
452451
) -> str:
453-
raise IntegrationFeatureNotImplementedError
452+
response = self.get_cached(
453+
path=VstsApiPath.items.format(
454+
instance=repo.config["instance"],
455+
project=quote(repo.config["project"]),
456+
repo_id=quote(repo.config["name"]),
457+
),
458+
params={
459+
"path": path,
460+
"api-version": "7.0",
461+
"versionDescriptor.version": ref,
462+
"download": "true",
463+
},
464+
headers={"Accept": "*/*"},
465+
raw_response=True,
466+
)
467+
return response.text

src/sentry/integrations/vsts/integration.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@
100100
""",
101101
IntegrationFeatures.STACKTRACE_LINK,
102102
),
103+
FeatureDescription(
104+
"""
105+
Import your Azure DevOps codeowners file into Sentry and use it alongside your
106+
ownership rules to assign Sentry issues.
107+
""",
108+
IntegrationFeatures.CODEOWNERS,
109+
),
103110
FeatureDescription(
104111
"""
105112
Automatically create Azure DevOps work items based on Issue Alert conditions.
@@ -129,6 +136,8 @@ class VstsIntegration(RepositoryIntegration, VstsIssuesSpec):
129136
outbound_assignee_key = "sync_forward_assignment"
130137
inbound_assignee_key = "sync_reverse_assignment"
131138

139+
codeowners_locations = ["CODEOWNERS", ".sentry/CODEOWNERS"]
140+
132141
def __init__(self, *args: Any, **kwargs: Any) -> None:
133142
super().__init__(*args, **kwargs)
134143
self.default_identity: RpcIdentity | None = None
@@ -406,6 +415,7 @@ class VstsIntegrationProvider(IntegrationProvider):
406415
IntegrationFeatures.ISSUE_BASIC,
407416
IntegrationFeatures.ISSUE_SYNC,
408417
IntegrationFeatures.STACKTRACE_LINK,
418+
IntegrationFeatures.CODEOWNERS,
409419
IntegrationFeatures.TICKET_RULES,
410420
]
411421
)

src/sentry/models/commitfilechange.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,15 @@ def process_resource_change(instance, **kwargs):
5151
from sentry.integrations.bitbucket.integration import BitbucketIntegration
5252
from sentry.integrations.github.integration import GitHubIntegration
5353
from sentry.integrations.gitlab.integration import GitlabIntegration
54+
from sentry.integrations.vsts.integration import VstsIntegration
5455
from sentry.tasks.codeowners import code_owners_auto_sync
5556

5657
def _spawn_task():
5758
filepaths = (
5859
set(GitHubIntegration.codeowners_locations)
5960
| set(GitlabIntegration.codeowners_locations)
6061
| set(BitbucketIntegration.codeowners_locations)
62+
| set(VstsIntegration.codeowners_locations)
6163
)
6264

6365
# CODEOWNERS file added or modified, trigger auto-sync

tests/sentry/integrations/vsts/test_client.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,35 @@ def test_check_no_file(self):
282282
with pytest.raises(ApiError):
283283
client.check_file(repo, path, version)
284284

285+
@responses.activate
286+
def test_get_file(self):
287+
self.assert_installation()
288+
integration, installation = self._get_integration_and_install()
289+
with assume_test_silo_mode(SiloMode.REGION):
290+
repo = Repository.objects.create(
291+
provider="visualstudio",
292+
name="example",
293+
organization_id=self.organization.id,
294+
config={
295+
"instance": self.vsts_base_url,
296+
"project": "project-name",
297+
"name": "example",
298+
},
299+
integration_id=integration.id,
300+
external_id="albertos-apples",
301+
)
302+
303+
client = installation.get_client()
304+
305+
path = "README.md"
306+
version = "master"
307+
url = f"https://myvstsaccount.visualstudio.com/project-name/_apis/git/repositories/{repo.name}/items?path={path}&api-version=7.0&versionDescriptor.version={version}&download=true"
308+
309+
responses.add(method=responses.GET, url=url, body="Hello, world!")
310+
311+
resp = client.get_file(repo, path, version)
312+
assert resp == "Hello, world!"
313+
285314
@responses.activate
286315
def test_get_stacktrace_link(self):
287316
self.assert_installation()

0 commit comments

Comments
 (0)