2025-09-23 23:41:43 +02:00
|
|
|
"""
|
2025-09-28 23:44:51 +02:00
|
|
|
Issue writing using the Gitea integration facade.
|
|
|
|
|
|
|
|
|
|
This module now acts as an adapter to the new gitea package,
|
|
|
|
|
maintaining backwards compatibility while using the cleaner API.
|
2025-09-23 23:41:43 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
from typing import Dict, Any, Optional
|
|
|
|
|
|
2025-09-28 23:44:51 +02:00
|
|
|
from gitea import GiteaClient, GiteaConfig
|
2025-09-23 23:41:43 +02:00
|
|
|
from .config import get_config
|
|
|
|
|
from .exceptions import IssueError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IssueWriter:
|
2025-09-28 23:44:51 +02:00
|
|
|
"""Writes issue updates using the Gitea integration facade."""
|
2025-09-23 23:41:43 +02:00
|
|
|
|
|
|
|
|
def __init__(self, config=None, auth_token=None):
|
|
|
|
|
self.config = config or get_config()
|
2025-09-24 23:36:07 +02:00
|
|
|
self.auth_token = auth_token or os.getenv('GITEA_API_TOKEN')
|
2025-09-23 23:41:43 +02:00
|
|
|
|
2025-09-28 23:44:51 +02:00
|
|
|
# Create Gitea client from tddai config
|
|
|
|
|
gitea_config = GiteaConfig.from_tddai_config(self.config)
|
|
|
|
|
if self.auth_token:
|
|
|
|
|
gitea_config.auth_token = self.auth_token
|
|
|
|
|
self.gitea_client = GiteaClient(gitea_config)
|
|
|
|
|
|
2025-09-23 23:41:43 +02:00
|
|
|
def update_issue(self, issue_number: int, update_data: Dict[str, Any]) -> Dict[str, Any]:
|
2025-09-28 23:44:51 +02:00
|
|
|
"""Update an issue via the gitea integration."""
|
2025-09-23 23:41:43 +02:00
|
|
|
if not self.auth_token:
|
|
|
|
|
raise IssueError("Authentication token required for issue updates")
|
|
|
|
|
|
|
|
|
|
try:
|
2025-09-28 23:44:51 +02:00
|
|
|
issue = self.gitea_client.issues.update(issue_number, **update_data)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2025-09-23 23:41:43 +02:00
|
|
|
raise IssueError(f"Failed to update issue #{issue_number}: {e}")
|
|
|
|
|
|
|
|
|
|
def update_issue_title(self, issue_number: int, new_title: str) -> Dict[str, Any]:
|
|
|
|
|
"""Update only the title of an issue."""
|
2025-09-28 23:44:51 +02:00
|
|
|
try:
|
|
|
|
|
issue = self.gitea_client.issues.update_title(issue_number, new_title)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise IssueError(f"Failed to update issue title #{issue_number}: {e}")
|
2025-09-23 23:41:43 +02:00
|
|
|
|
|
|
|
|
def update_issue_body(self, issue_number: int, new_body: str) -> Dict[str, Any]:
|
|
|
|
|
"""Update only the body of an issue."""
|
2025-09-28 23:44:51 +02:00
|
|
|
try:
|
|
|
|
|
issue = self.gitea_client.issues.update_body(issue_number, new_body)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise IssueError(f"Failed to update issue body #{issue_number}: {e}")
|
2025-09-23 23:41:43 +02:00
|
|
|
|
|
|
|
|
def update_issue_state(self, issue_number: int, new_state: str) -> Dict[str, Any]:
|
|
|
|
|
"""Update only the state of an issue (open/closed)."""
|
|
|
|
|
if new_state not in ['open', 'closed']:
|
|
|
|
|
raise IssueError(f"Invalid state '{new_state}'. Must be 'open' or 'closed'")
|
2025-09-28 23:44:51 +02:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if new_state == 'closed':
|
|
|
|
|
issue = self.gitea_client.issues.close(issue_number)
|
|
|
|
|
else:
|
|
|
|
|
issue = self.gitea_client.issues.reopen(issue_number)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise IssueError(f"Failed to update issue state #{issue_number}: {e}")
|
2025-09-23 23:41:43 +02:00
|
|
|
|
|
|
|
|
def close_issue(self, issue_number: int) -> Dict[str, Any]:
|
|
|
|
|
"""Close an issue."""
|
|
|
|
|
return self.update_issue_state(issue_number, 'closed')
|
|
|
|
|
|
|
|
|
|
def reopen_issue(self, issue_number: int) -> Dict[str, Any]:
|
|
|
|
|
"""Reopen a closed issue."""
|
2025-09-24 23:51:29 +02:00
|
|
|
return self.update_issue_state(issue_number, 'open')
|
|
|
|
|
|
|
|
|
|
def assign_to_milestone(self, issue_number: int, milestone_id: int) -> Dict[str, Any]:
|
|
|
|
|
"""Assign issue to a milestone (project)."""
|
2025-09-28 23:44:51 +02:00
|
|
|
try:
|
|
|
|
|
issue = self.gitea_client.issues.assign_to_milestone(issue_number, milestone_id)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise IssueError(f"Failed to assign issue #{issue_number} to milestone: {e}")
|
2025-09-24 23:51:29 +02:00
|
|
|
|
|
|
|
|
def remove_from_milestone(self, issue_number: int) -> Dict[str, Any]:
|
|
|
|
|
"""Remove issue from its current milestone."""
|
2025-09-28 23:44:51 +02:00
|
|
|
try:
|
|
|
|
|
issue = self.gitea_client.issues.remove_from_milestone(issue_number)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise IssueError(f"Failed to remove issue #{issue_number} from milestone: {e}")
|
2025-09-24 23:51:29 +02:00
|
|
|
|
|
|
|
|
def update_labels(self, issue_number: int, labels: list) -> Dict[str, Any]:
|
2025-09-28 23:44:51 +02:00
|
|
|
"""Update issue labels completely."""
|
2025-09-25 00:31:37 +02:00
|
|
|
if not self.auth_token:
|
|
|
|
|
raise IssueError("Authentication token required for label updates")
|
|
|
|
|
|
|
|
|
|
try:
|
2025-09-28 23:44:51 +02:00
|
|
|
issue = self.gitea_client.issues.set_labels(issue_number, labels)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
2025-09-25 00:31:37 +02:00
|
|
|
raise IssueError(f"Failed to update labels for issue #{issue_number}: {e}")
|
2025-09-24 23:51:29 +02:00
|
|
|
|
|
|
|
|
def add_labels(self, issue_number: int, new_labels: list) -> Dict[str, Any]:
|
|
|
|
|
"""Add labels to issue (preserving existing labels)."""
|
|
|
|
|
try:
|
2025-09-28 23:44:51 +02:00
|
|
|
issue = self.gitea_client.issues.add_labels(issue_number, new_labels)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
2025-09-24 23:51:29 +02:00
|
|
|
raise IssueError(f"Failed to add labels to issue #{issue_number}: {e}")
|
|
|
|
|
|
|
|
|
|
def remove_labels(self, issue_number: int, labels_to_remove: list) -> Dict[str, Any]:
|
|
|
|
|
"""Remove specific labels from issue."""
|
|
|
|
|
try:
|
2025-09-28 23:44:51 +02:00
|
|
|
issue = self.gitea_client.issues.remove_labels(issue_number, labels_to_remove)
|
|
|
|
|
return self.gitea_client.issues.to_dict(issue)
|
|
|
|
|
except Exception as e:
|
2025-09-24 23:51:29 +02:00
|
|
|
raise IssueError(f"Failed to remove labels from issue #{issue_number}: {e}")
|