Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,41 @@
from github.Issue import Issue
from github.Repository import Repository
import os
import requests
import os
from github.IssueComment import IssueComment


def fetch_and_parse_github_actions_log(issue: Issue) -> str:
"""
Fetch and parse the GitHub Actions log for errors or tracebacks.

Args:
issue: The GitHub issue to fetch logs for.

Returns:
A string containing the parsed error or traceback.
"""
repo = issue.repository
workflow_runs = repo.get_workflow_runs(event='pull_request', branch=issue.pull_request.head.ref)

if workflow_runs.total_count == 0:
return "No workflow runs found for this pull request."

latest_run = workflow_runs[0]
logs_url = latest_run.logs_url

headers = {'Authorization': f'token {os.getenv("GITHUB_TOKEN")}'}
response = requests.get(logs_url, headers=headers)

if response.status_code != 200:
return f"Failed to fetch logs: {response.status_code} {response.reason}"

logs = response.text
return parse_github_action_logs(logs)
return parse_github_action_logs(logs)


# Determine base directory
src_dir = os.path.dirname(os.path.abspath(__file__))
base_dir = os.path.dirname(src_dir)
Expand Down Expand Up @@ -67,6 +100,19 @@ def add_signature_to_comment(comment_text: str, model: str) -> str:
return comment_text


def parse_github_action_logs(logs: str) -> str:
"""
Parse GitHub Actions logs for errors or tracebacks.

Args:
logs: The logs from a GitHub Actions workflow run.

Returns:
A string containing the parsed error or traceback messages.
"""
error_lines = [line for line in logs.splitlines() if "error" in line.lower() or "traceback" in line.lower()]
return "\n".join(error_lines) if error_lines else "No errors or tracebacks found in logs."

def get_github_client() -> Github:
"""Initialize and return authenticated GitHub client"""
load_dotenv()
Expand All @@ -90,6 +136,27 @@ def get_open_issues(repo: Repository) -> List[Issue]:
return list(repo.get_issues(state='open', sort='created', direction='asc'))


def parse_github_action_logs(logs: str) -> str:
"""
Parse GitHub Actions logs for errors or tracebacks.

Args:
logs: The logs from a GitHub Actions workflow run.

Returns:
A string containing the parsed error or traceback messages.
"""
error_lines = []
for line in logs.splitlines():
if "error" in line.lower() or "traceback" in line.lower():
error_lines.append(line)

if not error_lines:
return "No errors or tracebacks found in logs."

return "\n".join(error_lines)


def get_issue_comments(issue: Issue) -> List[IssueComment]:
"""Get all comments for a specific issue or pull request, ignoring Graphite-related comments"""
# Text to identify Graphite-related comments
Expand Down
11 changes: 11 additions & 0 deletions src/response_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import bot_tools
import os

import src.git_utils as git_utils
from src.git_utils import (
get_github_client,
get_repository,
Expand Down Expand Up @@ -142,6 +143,11 @@ def scrape_text_from_url(url: str) -> str:
Returns:
The scraped text content or a message if non-text content is detected.
"""
if triggers.has_catch_log_label(issue_or_pr):
parsed_log = git_utils.fetch_and_parse_github_actions_log(issue_or_pr)
write_issue_response(issue_or_pr, f"Error log:\n```\n{parsed_log}\n```")
return True, None

try:
response = requests.get(url, timeout=10)
response.raise_for_status() # Raise an error for bad responses
Expand Down Expand Up @@ -1001,6 +1007,11 @@ def process_issue(
print(f"Processing {entity_type} #{issue_or_pr.number}")

try:
if triggers.has_catch_log_label(issue_or_pr):
parsed_log = git_utils.fetch_and_parse_github_actions_log(issue_or_pr)
write_issue_response(issue_or_pr, f"Error log:\n```\n{parsed_log}\n```")
return True, None

has_bot_mention = triggers.has_blech_bot_tag(issue_or_pr) \
or '[ blech_bot ]' in (issue_or_pr.title or '').lower()
if not has_bot_mention:
Expand Down
25 changes: 25 additions & 0 deletions src/triggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,31 @@ def has_blech_bot_tag(issue: Issue) -> bool:
return any(label.name == "blech_bot" for label in issue.labels)


def has_catch_log_label(issue: Issue) -> bool:
"""
Check if the issue has the 'catch_log' label

Args:
issue: The GitHub issue to check

Returns:
True if the issue has the 'catch_log' label, False otherwise
"""
return any(label.name == "catch_log" for label in issue.labels)


def has_catch_log_label(issue: Issue) -> bool:
"""
Check if the issue has the 'catch_log' label

Args:
issue: The GitHub issue to check

Returns:
True if the issue has the 'catch_log' label, False otherwise
"""
return any(label.name == "catch_log" for label in issue.labels)

def has_generate_edit_command_trigger(issue: Issue) -> bool:
"""
Check if the issue comments contain the trigger for generate_edit_command
Expand Down