mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-05 13:20:39 +08:00
Azure: handle line comments
This commit is contained in:
@ -117,6 +117,10 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
get_logger().warning(f"Azure failed to publish code suggestion, error: {e}")
|
get_logger().warning(f"Azure failed to publish code suggestion, error: {e}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def reply_to_comment_from_comment_id(self, comment_id: int, body: str, is_temporary: bool = False) -> Comment:
|
||||||
|
# comment_id is actually thread_id
|
||||||
|
return self.reply_to_thread(comment_id, body, is_temporary)
|
||||||
|
|
||||||
def get_pr_description_full(self) -> str:
|
def get_pr_description_full(self) -> str:
|
||||||
return self.pr.description
|
return self.pr.description
|
||||||
|
|
||||||
@ -537,6 +541,29 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool:
|
def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def set_like(self, thread_id: int, comment_id: int, create: bool = True):
|
||||||
|
if create:
|
||||||
|
self.azure_devops_client.create_like(self.repo_slug, self.pr_num, thread_id, comment_id, project=self.workspace_slug)
|
||||||
|
else:
|
||||||
|
self.azure_devops_client.delete_like(self.repo_slug, self.pr_num, thread_id, comment_id, project=self.workspace_slug)
|
||||||
|
|
||||||
|
def set_thread_status(self, thread_id: int, status: str):
|
||||||
|
try:
|
||||||
|
self.azure_devops_client.update_thread(CommentThread(status=status), self.repo_slug, self.pr_num, thread_id, self.workspace_slug)
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().exception(f"Failed to set thread status, error: {e}")
|
||||||
|
|
||||||
|
def reply_to_thread(self, thread_id: int, body: str, is_temporary: bool = False) -> Comment:
|
||||||
|
try:
|
||||||
|
comment = Comment(content=body)
|
||||||
|
response = self.azure_devops_client.create_comment(comment, self.repo_slug, self.pr_num, thread_id, self.workspace_slug)
|
||||||
|
response.thread_id = thread_id
|
||||||
|
if is_temporary:
|
||||||
|
self.temp_comments.append(response)
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().exception(f"Failed to reply to thread, error: {e}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]:
|
def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]:
|
||||||
parsed_url = urlparse(pr_url)
|
parsed_url = urlparse(pr_url)
|
||||||
|
@ -22,6 +22,7 @@ from starlette_context.middleware import RawContextMiddleware
|
|||||||
from pr_agent.agent.pr_agent import PRAgent, command2class
|
from pr_agent.agent.pr_agent import PRAgent, command2class
|
||||||
from pr_agent.algo.utils import update_settings_from_args
|
from pr_agent.algo.utils import update_settings_from_args
|
||||||
from pr_agent.config_loader import get_settings
|
from pr_agent.config_loader import get_settings
|
||||||
|
from pr_agent.git_providers import get_git_provider_with_context
|
||||||
from pr_agent.git_providers.utils import apply_repo_settings
|
from pr_agent.git_providers.utils import apply_repo_settings
|
||||||
from pr_agent.log import LoggingFormat, get_logger, setup_logger
|
from pr_agent.log import LoggingFormat, get_logger, setup_logger
|
||||||
|
|
||||||
@ -33,14 +34,18 @@ azure_devops_server = get_settings().get("azure_devops_server")
|
|||||||
WEBHOOK_USERNAME = azure_devops_server.get("webhook_username")
|
WEBHOOK_USERNAME = azure_devops_server.get("webhook_username")
|
||||||
WEBHOOK_PASSWORD = azure_devops_server.get("webhook_password")
|
WEBHOOK_PASSWORD = azure_devops_server.get("webhook_password")
|
||||||
|
|
||||||
async def handle_request_comment( url: str, body: str, log_context: dict
|
async def handle_request_comment(url: str, body: str, thread_id: int, comment_id: int, log_context: dict):
|
||||||
):
|
|
||||||
log_context["action"] = body
|
log_context["action"] = body
|
||||||
log_context["api_url"] = url
|
log_context["api_url"] = url
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with get_logger().contextualize(**log_context):
|
with get_logger().contextualize(**log_context):
|
||||||
await PRAgent().handle_request(url, body)
|
agent = PRAgent()
|
||||||
|
provider = get_git_provider_with_context(pr_url=url)
|
||||||
|
handled = await agent.handle_request(url, body, notify=lambda: provider.reply_to_thread(thread_id, "On it! ⏳", True))
|
||||||
|
# mark command comment as closed
|
||||||
|
if handled:
|
||||||
|
provider.set_thread_status(thread_id, "closed")
|
||||||
|
provider.remove_initial_comment()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
get_logger().exception(f"Failed to handle webhook", artifact={"url": url, "body": body}, error=str(e))
|
get_logger().exception(f"Failed to handle webhook", artifact={"url": url, "body": body}, error=str(e))
|
||||||
|
|
||||||
@ -83,7 +88,6 @@ async def _perform_commands_azure(commands_conf: str, agent: PRAgent, api_url: s
|
|||||||
|
|
||||||
|
|
||||||
async def handle_request_azure(data, log_context):
|
async def handle_request_azure(data, log_context):
|
||||||
actions = []
|
|
||||||
if data["eventType"] == "git.pullrequest.created":
|
if data["eventType"] == "git.pullrequest.created":
|
||||||
# API V1 (latest)
|
# API V1 (latest)
|
||||||
pr_url = unquote(data["resource"]["_links"]["web"]["href"].replace("_apis/git/repositories", "_git"))
|
pr_url = unquote(data["resource"]["_links"]["web"]["href"].replace("_apis/git/repositories", "_git"))
|
||||||
@ -95,11 +99,16 @@ async def handle_request_azure(data, log_context):
|
|||||||
content=jsonable_encoder({"message": "webhook triggered successfully"})
|
content=jsonable_encoder({"message": "webhook triggered successfully"})
|
||||||
)
|
)
|
||||||
elif data["eventType"] == "ms.vss-code.git-pullrequest-comment-event" and "content" in data["resource"]["comment"]:
|
elif data["eventType"] == "ms.vss-code.git-pullrequest-comment-event" and "content" in data["resource"]["comment"]:
|
||||||
if available_commands_rgx.match(data["resource"]["comment"]["content"]):
|
comment = data["resource"]["comment"]
|
||||||
|
if available_commands_rgx.match(comment["content"]):
|
||||||
if(data["resourceVersion"] == "2.0"):
|
if(data["resourceVersion"] == "2.0"):
|
||||||
repo = data["resource"]["pullRequest"]["repository"]["webUrl"]
|
repo = data["resource"]["pullRequest"]["repository"]["webUrl"]
|
||||||
pr_url = unquote(f'{repo}/pullrequest/{data["resource"]["pullRequest"]["pullRequestId"]}')
|
pr_url = unquote(f'{repo}/pullrequest/{data["resource"]["pullRequest"]["pullRequestId"]}')
|
||||||
actions = [data["resource"]["comment"]["content"]]
|
action = comment["content"]
|
||||||
|
thread_url = comment["_links"]["threads"]["href"]
|
||||||
|
thread_id = int(thread_url.split("/")[-1])
|
||||||
|
comment_id = int(comment["id"])
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
# API V1 not supported as it does not contain the PR URL
|
# API V1 not supported as it does not contain the PR URL
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
@ -119,15 +128,14 @@ async def handle_request_azure(data, log_context):
|
|||||||
log_context["event"] = data["eventType"]
|
log_context["event"] = data["eventType"]
|
||||||
log_context["api_url"] = pr_url
|
log_context["api_url"] = pr_url
|
||||||
|
|
||||||
for action in actions:
|
try:
|
||||||
try:
|
await handle_request_comment(pr_url, action, thread_id, comment_id, log_context)
|
||||||
await handle_request_comment(pr_url, action, log_context)
|
except Exception as e:
|
||||||
except Exception as e:
|
get_logger().error("Azure DevOps Trigger failed. Error:" + str(e))
|
||||||
get_logger().error("Azure DevOps Trigger failed. Error:" + str(e))
|
return JSONResponse(
|
||||||
return JSONResponse(
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
content=json.dumps({"message": "Internal server error"}),
|
||||||
content=json.dumps({"message": "Internal server error"}),
|
)
|
||||||
)
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_202_ACCEPTED, content=jsonable_encoder({"message": "webhook triggered successfully"})
|
status_code=status.HTTP_202_ACCEPTED, content=jsonable_encoder({"message": "webhook triggered successfully"})
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user