fix: Ignore OpenHands bot GitHub resolver events (#14832)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Rohit Malhotra
2026-06-15 18:04:20 -04:00
committed by GitHub
co-authored by openhands
parent 7107ab8891
commit 19471ba1d1
2 changed files with 82 additions and 0 deletions
@@ -42,6 +42,12 @@ from openhands.app_server.types import (
)
from openhands.app_server.utils.logger import openhands_logger as logger
IGNORED_GITHUB_EVENT_SENDERS = frozenset(
{
'openhands-ai[bot]',
}
)
class GithubManager(Manager[GithubViewType]):
def __init__(
@@ -123,6 +129,13 @@ class GithubManager(Manager[GithubViewType]):
return False
def _get_ignored_sender_login(self, message: Message) -> str | None:
payload = message.message.get('payload', {})
login = payload.get('sender', {}).get('login')
if login and login.lower() in IGNORED_GITHUB_EVENT_SENDERS:
return login
return None
def _get_issue_number_from_payload(self, message: Message) -> int | None:
"""Extract issue/PR number from a GitHub webhook payload.
@@ -196,6 +209,11 @@ class GithubManager(Manager[GithubViewType]):
async def is_job_requested(self, message: Message) -> bool:
self._confirm_incoming_source_type(message)
ignored_sender = self._get_ignored_sender_login(message)
if ignored_sender:
logger.info('[GitHub] Ignoring event from %s', ignored_sender)
return False
installation_id = message.message['installation']
payload = message.message.get('payload', {})
repo_obj = payload.get('repository')
@@ -236,6 +254,12 @@ class GithubManager(Manager[GithubViewType]):
async def receive_message(self, message: Message):
self._confirm_incoming_source_type(message)
ignored_sender = self._get_ignored_sender_login(message)
if ignored_sender:
logger.info('[GitHub] Ignoring event from %s', ignored_sender)
return
try:
await self.data_collector.process_payload(message)
except Exception:
@@ -7,6 +7,7 @@ Covers:
- All supported trigger types: labeled issues, issue comments, PR comments, inline PR comments
"""
from copy import deepcopy
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
@@ -316,6 +317,63 @@ class TestGithubManagerUserNotFound:
assert "haven't created an OpenHands account" in comment_text
assert 'sign up' in comment_text.lower()
@pytest.mark.asyncio
@patch('integrations.github.github_manager.Auth')
@patch('integrations.github.github_manager.GithubIntegration')
async def test_is_job_requested_ignores_openhands_bot_sender(
self,
mock_github_integration,
mock_auth,
mock_token_manager,
mock_data_collector,
github_issue_message,
):
"""Test that bot-authored comments from the OpenHands GitHub App do not start jobs."""
github_issue_message = deepcopy(github_issue_message)
github_issue_message.message['payload']['sender'] = {
'id': 188912522,
'login': 'openhands-ai[bot]',
}
github_issue_message.message['payload']['comment']['body'] = (
'Documented upstream changes in @openhands/extensions.'
)
manager = GithubManager(mock_token_manager, mock_data_collector)
manager._user_has_write_access_to_repo = MagicMock(return_value=True)
assert await manager.is_job_requested(github_issue_message) is False
manager._user_has_write_access_to_repo.assert_not_called()
@pytest.mark.asyncio
@patch('integrations.github.github_manager.Auth')
@patch('integrations.github.github_manager.GithubIntegration')
async def test_receive_message_ignores_openhands_bot_sender_before_processing(
self,
mock_github_integration,
mock_auth,
mock_token_manager,
mock_data_collector,
github_issue_message,
):
"""Test that ignored bot events are dropped before collection or account lookup."""
github_issue_message = deepcopy(github_issue_message)
github_issue_message.message['payload']['sender'] = {
'id': 188912522,
'login': 'openhands-ai[bot]',
}
github_issue_message.message['payload']['comment']['body'] = (
'Documented upstream changes in @openhands/extensions.'
)
manager = GithubManager(mock_token_manager, mock_data_collector)
manager.is_job_requested = AsyncMock(return_value=True)
await manager.receive_message(github_issue_message)
mock_data_collector.process_payload.assert_not_awaited()
manager.is_job_requested.assert_not_awaited()
mock_token_manager.get_user_id_from_idp_user_id.assert_not_called()
@patch('integrations.github.github_manager.Auth')
@patch('integrations.github.github_manager.GithubIntegration')
@patch('integrations.github.github_manager.logger')