diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index d71a029f..a8adabb0 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -39,7 +39,7 @@ class AzureDevopsProvider(GitProvider): "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.workspace_slug = None self.repo_slug = None @@ -593,8 +593,9 @@ class AzureDevopsProvider(GitProvider): credentials = BasicAuthentication("", auth_token) azure_devops_connection = Connection(base_url=org, creds=credentials) 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): if self.repo is None: @@ -635,4 +636,50 @@ class AzureDevopsProvider(GitProvider): last = commits[0] url = self.azure_devops_client.normalized_url + "/" + self.workspace_slug + "/_git/" + self.repo_slug + "/commit/" + last.commit_id return url - \ No newline at end of file + + 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 [] diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index 8c69e612..789bcc19 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -199,6 +199,13 @@ Ticket Description: {{ ticket.body }} ##### {%- endif %} + +{%- if ticket.requirements %} +Ticket Requirements: +##### +{{ ticket.requirements }} +##### +{%- endif %} ===== {% endfor %} {%- endif %} diff --git a/pr_agent/tools/ticket_pr_compliance_check.py b/pr_agent/tools/ticket_pr_compliance_check.py index 45baa0d2..02abcabc 100644 --- a/pr_agent/tools/ticket_pr_compliance_check.py +++ b/pr_agent/tools/ticket_pr_compliance_check.py @@ -3,6 +3,7 @@ import traceback from pr_agent.config_loader import get_settings from pr_agent.git_providers import GithubProvider +from pr_agent.git_providers import AzureDevopsProvider from pr_agent.log import get_logger # Compile the regex pattern once, outside the function @@ -131,6 +132,32 @@ async def extract_tickets(git_provider): 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: get_logger().error(f"Error extracting tickets error= {e}", artifact={"traceback": traceback.format_exc()})