diff --git a/pr_agent/git_providers/utils.py b/pr_agent/git_providers/utils.py index 6038e861..7927e30e 100644 --- a/pr_agent/git_providers/utils.py +++ b/pr_agent/git_providers/utils.py @@ -101,3 +101,22 @@ def set_claude_model(): get_settings().set('config.model', model_claude) get_settings().set('config.model_weak', model_claude) get_settings().set('config.fallback_models', [model_claude]) + + +def is_user_name_a_bot(name: str) -> bool: + if not name: + return False + bot_indicators = ['codium', 'bot_', 'bot-', '_bot', '-bot', 'qodo', "service", "github", "jenkins", "auto", + "cicd", "validator", "ci-", "assistant", "srv-"] + return any(indicator in name.lower() for indicator in bot_indicators) + + +def is_pr_description_indicating_bot(description: str) -> bool: + if not description: + return False + bot_descriptions = ["Snyk has created this PR", "This PR was created automatically by", + "This PR was created by a bot", + "This pull request was automatically generated by"] + # Check is it's a Snyk bot + if any(bot_description in description for bot_description in bot_descriptions): + return True \ No newline at end of file diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index 2175db27..ab422bd4 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -19,7 +19,7 @@ from starlette_context.middleware import RawContextMiddleware from pr_agent.agent.pr_agent import PRAgent from pr_agent.algo.utils import update_settings_from_args from pr_agent.config_loader import get_settings, global_settings -from pr_agent.git_providers.utils import apply_repo_settings +from pr_agent.git_providers.utils import apply_repo_settings, is_user_name_a_bot, is_pr_description_indicating_bot from pr_agent.identity_providers import get_identity_provider from pr_agent.identity_providers.identity_provider import Eligibility from pr_agent.log import LoggingFormat, get_logger, setup_logger @@ -102,11 +102,22 @@ async def _perform_commands_bitbucket(commands_conf: str, agent: PRAgent, api_ur def is_bot_user(data) -> bool: try: actor = data.get("data", {}).get("actor", {}) + description = data.get("data", {}).get("pullrequest", {}).get("description", "") # allow actor type: user . if it's "AppUser" or "team" then it is a bot user allowed_actor_types = {"user"} if actor and actor["type"].lower() not in allowed_actor_types: get_logger().info(f"BitBucket actor type is not 'user', skipping: {actor}") return True + + username = actor.get("username", "") + if username and is_user_name_a_bot(username): + get_logger().info(f"BitBucket actor is a bot user, skipping: {username}") + return True + + if description and is_pr_description_indicating_bot(description): + get_logger().info(f"Description indicates a bot user: {actor}", + artifact={"description": description}) + return True except Exception as e: get_logger().error(f"Failed 'is_bot_user' logic: {e}") return False diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 6191862d..b0e509b3 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -18,7 +18,7 @@ from pr_agent.config_loader import get_settings, global_settings from pr_agent.git_providers import (get_git_provider, get_git_provider_with_context) from pr_agent.git_providers.git_provider import IncrementalPR -from pr_agent.git_providers.utils import apply_repo_settings +from pr_agent.git_providers.utils import apply_repo_settings, is_user_name_a_bot, is_pr_description_indicating_bot from pr_agent.identity_providers import get_identity_provider from pr_agent.identity_providers.identity_provider import Eligibility from pr_agent.log import LoggingFormat, get_logger, setup_logger @@ -238,13 +238,22 @@ def get_log_context(body, event, action, build_number): return log_context, sender, sender_id, sender_type -def is_bot_user(sender, sender_type): +def is_bot_user(sender, sender_type, user_description): try: # logic to ignore PRs opened by bot - if get_settings().get("GITHUB_APP.IGNORE_BOT_PR", False) and sender_type == "Bot": - if 'pr-agent' not in sender: + if get_settings().get("GITHUB_APP.IGNORE_BOT_PR", False): + if sender_type.lower() == "bot": + if 'pr-agent' not in sender: + get_logger().info(f"Ignoring PR from '{sender=}' because it is a bot") + return True + if is_user_name_a_bot(sender): get_logger().info(f"Ignoring PR from '{sender=}' because it is a bot") - return True + return True + # Ignore PRs opened by bot users based on their description + if isinstance(user_description, str) and is_pr_description_indicating_bot(user_description): + get_logger().info(f"Description indicates a bot user: {sender}", + artifact={"description": user_description}) + return True except Exception as e: get_logger().error(f"Failed 'is_bot_user' logic: {e}") return False @@ -307,7 +316,8 @@ async def handle_request(body: Dict[str, Any], event: str): log_context, sender, sender_id, sender_type = get_log_context(body, event, action, build_number) # logic to ignore PRs opened by bot, PRs with specific titles, labels, source branches, or target branches - if is_bot_user(sender, sender_type) and 'check_run' not in body: + pr_description = body.get("pull_request", {}).get("body", "") + if is_bot_user(sender, sender_type, pr_description) and 'check_run' not in body: return {} if action != 'created' and 'check_run' not in body: if not should_process_pr_logic(body): diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 2cba7ac5..e97217df 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -15,7 +15,7 @@ from starlette_context.middleware import RawContextMiddleware from pr_agent.agent.pr_agent import PRAgent from pr_agent.algo.utils import update_settings_from_args from pr_agent.config_loader import get_settings, global_settings -from pr_agent.git_providers.utils import apply_repo_settings +from pr_agent.git_providers.utils import apply_repo_settings, is_user_name_a_bot, is_pr_description_indicating_bot from pr_agent.log import LoggingFormat, get_logger, setup_logger from pr_agent.secret_providers import get_secret_provider @@ -86,10 +86,14 @@ def is_bot_user(data) -> bool: try: # logic to ignore bot users (unlike Github, no direct flag for bot users in gitlab) sender_name = data.get("user", {}).get("name", "unknown").lower() - bot_indicators = ['codium', 'bot_', 'bot-', '_bot', '-bot'] - if any(indicator in sender_name for indicator in bot_indicators): + if is_user_name_a_bot(sender_name): get_logger().info(f"Skipping GitLab bot user: {sender_name}") return True + pr_description = data.get('object_attributes', {}).get('description', '') + if pr_description and is_pr_description_indicating_bot(pr_description): + get_logger().info(f"Description indicates a bot user: {sender_name}", + artifact={"description": pr_description}) + return True except Exception as e: get_logger().error(f"Failed 'is_bot_user' logic: {e}") return False