mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-02 11:50:37 +08:00
feat: enhance Azure DevOps integration by adding work item as a ticket retrieval methods - Supporting ticket context for Azure DevOps
Signed-off-by: abishlal <abishlalns03@gmail.com>
This commit is contained in:
@ -39,7 +39,7 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
"Azure DevOps provider is not available. Please install the required dependencies."
|
"Azure DevOps provider is not available. Please install the required dependencies."
|
||||||
)
|
)
|
||||||
|
|
||||||
self.azure_devops_client = self._get_azure_devops_client()
|
self.azure_devops_client, self.azure_devops_board_client = self._get_azure_devops_client()
|
||||||
self.diff_files = None
|
self.diff_files = None
|
||||||
self.workspace_slug = None
|
self.workspace_slug = None
|
||||||
self.repo_slug = None
|
self.repo_slug = None
|
||||||
@ -593,8 +593,9 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
credentials = BasicAuthentication("", auth_token)
|
credentials = BasicAuthentication("", auth_token)
|
||||||
azure_devops_connection = Connection(base_url=org, creds=credentials)
|
azure_devops_connection = Connection(base_url=org, creds=credentials)
|
||||||
azure_devops_client = azure_devops_connection.clients.get_git_client()
|
azure_devops_client = azure_devops_connection.clients.get_git_client()
|
||||||
|
azure_devops_board_client = azure_devops_connection.clients.get_work_item_tracking_client()
|
||||||
|
|
||||||
return azure_devops_client
|
return azure_devops_client, azure_devops_board_client
|
||||||
|
|
||||||
def _get_repo(self):
|
def _get_repo(self):
|
||||||
if self.repo is None:
|
if self.repo is None:
|
||||||
@ -635,4 +636,50 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
last = commits[0]
|
last = commits[0]
|
||||||
url = self.azure_devops_client.normalized_url + "/" + self.workspace_slug + "/_git/" + self.repo_slug + "/commit/" + last.commit_id
|
url = self.azure_devops_client.normalized_url + "/" + self.workspace_slug + "/_git/" + self.repo_slug + "/commit/" + last.commit_id
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
def get_linked_work_items(self) -> list:
|
||||||
|
"""
|
||||||
|
Get linked work items from the PR.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
work_items = self.azure_devops_client.get_pull_request_work_item_refs(
|
||||||
|
project=self.workspace_slug,
|
||||||
|
repository_id=self.repo_slug,
|
||||||
|
pull_request_id=self.pr_num,
|
||||||
|
)
|
||||||
|
ids = [work_item.id for work_item in work_items]
|
||||||
|
if not work_items:
|
||||||
|
return []
|
||||||
|
items = self.get_work_items(ids)
|
||||||
|
return items
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().exception(f"Failed to get linked work items, error: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_work_items(self, work_item_ids: int) -> list:
|
||||||
|
"""
|
||||||
|
Get work items by their IDs.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
raw_work_items = self.azure_devops_board_client.get_work_items(
|
||||||
|
project=self.workspace_slug,
|
||||||
|
ids=work_item_ids,
|
||||||
|
)
|
||||||
|
work_items = []
|
||||||
|
for item in raw_work_items:
|
||||||
|
work_items.append(
|
||||||
|
{
|
||||||
|
"id": item.id,
|
||||||
|
"title": item.fields.get("System.Title", ""),
|
||||||
|
"url": item.url,
|
||||||
|
"body": item.fields.get("System.Description", ""),
|
||||||
|
"state": item.fields.get("System.State", ""),
|
||||||
|
"acceptance_criteria": item.fields.get(
|
||||||
|
"Microsoft.VSTS.Common.AcceptanceCriteria", ""
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return work_items
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().exception(f"Failed to get work items, error: {e}")
|
||||||
|
return []
|
||||||
|
@ -199,6 +199,13 @@ Ticket Description:
|
|||||||
{{ ticket.body }}
|
{{ ticket.body }}
|
||||||
#####
|
#####
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if ticket.requirements %}
|
||||||
|
Ticket Requirements:
|
||||||
|
#####
|
||||||
|
{{ ticket.requirements }}
|
||||||
|
#####
|
||||||
|
{%- endif %}
|
||||||
=====
|
=====
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
@ -3,6 +3,7 @@ import traceback
|
|||||||
|
|
||||||
from pr_agent.config_loader import get_settings
|
from pr_agent.config_loader import get_settings
|
||||||
from pr_agent.git_providers import GithubProvider
|
from pr_agent.git_providers import GithubProvider
|
||||||
|
from pr_agent.git_providers import AzureDevopsProvider
|
||||||
from pr_agent.log import get_logger
|
from pr_agent.log import get_logger
|
||||||
|
|
||||||
# Compile the regex pattern once, outside the function
|
# Compile the regex pattern once, outside the function
|
||||||
@ -131,6 +132,32 @@ async def extract_tickets(git_provider):
|
|||||||
|
|
||||||
return tickets_content
|
return tickets_content
|
||||||
|
|
||||||
|
elif isinstance(git_provider, AzureDevopsProvider):
|
||||||
|
tickets_info = git_provider.get_linked_work_items()
|
||||||
|
tickets_content = []
|
||||||
|
for ticket in tickets_info:
|
||||||
|
try:
|
||||||
|
ticket_body_str = ticket.get("body", "")
|
||||||
|
if len(ticket_body_str) > MAX_TICKET_CHARACTERS:
|
||||||
|
ticket_body_str = ticket_body_str[:MAX_TICKET_CHARACTERS] + "..."
|
||||||
|
|
||||||
|
tickets_content.append(
|
||||||
|
{
|
||||||
|
"ticket_id": ticket.get("id"),
|
||||||
|
"ticket_url": ticket.get("url"),
|
||||||
|
"title": ticket.get("title"),
|
||||||
|
"body": ticket_body_str,
|
||||||
|
"labels": ", ".join(ticket.get("labels", [])),
|
||||||
|
"requirements": ticket.get("acceptance_criteria", ""),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().error(
|
||||||
|
f"Error processing Azure DevOps ticket: {e}",
|
||||||
|
artifact={"traceback": traceback.format_exc()},
|
||||||
|
)
|
||||||
|
return tickets_content
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
get_logger().error(f"Error extracting tickets error= {e}",
|
get_logger().error(f"Error extracting tickets error= {e}",
|
||||||
artifact={"traceback": traceback.format_exc()})
|
artifact={"traceback": traceback.format_exc()})
|
||||||
|
Reference in New Issue
Block a user