""" Issue creation using the Gitea facade. This module now acts as an adapter to the new gitea package, maintaining backwards compatibility while using the cleaner API. """ import os from typing import Dict, Any, Optional, List from gitea import GiteaClient, GiteaConfig, Priority from .config import get_config from .exceptions import IssueError class IssueCreator: """Creates new issues using the Gitea facade.""" def __init__(self, config=None, auth_token=None): self.config = config or get_config() self.auth_token = auth_token or os.getenv('GITEA_API_TOKEN') # 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) def create_issue(self, title: str, body: str, **kwargs) -> Dict[str, Any]: """Create a new issue via POST operation. Args: title: Issue title (required) body: Issue description/body (required) **kwargs: Optional fields (assignees, milestone, labels, etc.) Returns: Dict containing created issue data including issue number Raises: IssueError: If creation fails """ # Validate input if not title or not title.strip(): raise IssueError("Issue title cannot be empty") try: issue = self.gitea_client.issues.create( title=title, body=body, assignees=kwargs.get('assignees', []), milestone=kwargs.get('milestone'), labels=kwargs.get('labels', []) ) # Convert back to dict format for backwards compatibility return { 'number': issue.number, 'title': issue.title, 'body': issue.body, 'state': issue.state, 'html_url': issue.html_url, 'created_at': issue.created_at.isoformat(), 'updated_at': issue.updated_at.isoformat(), 'assignee': {'login': issue.assignee.login} if issue.assignee else None, 'labels': [{'name': label.name} for label in issue.labels] } except Exception as e: raise IssueError(f"Failed to create issue: {e}") def create_enhancement_issue(self, title: str, use_case: str, technical_requirements: str = "", acceptance_criteria: Optional[List[str]] = None, dependencies: Optional[List[str]] = None, priority: str = "Medium") -> Dict[str, Any]: """Create an enhancement issue with structured format. Args: title: Issue title use_case: UseCase description technical_requirements: Technical implementation details acceptance_criteria: List of acceptance criteria dependencies: List of dependency descriptions priority: Priority level (High, Medium, Low) Returns: Dict containing created issue data """ # Build structured body body_parts = [f"UseCase: {use_case}"] if technical_requirements: body_parts.extend([ "", "Technical Requirements:", technical_requirements ]) if acceptance_criteria: body_parts.extend([ "", "Acceptance Criteria:" ]) for criterion in acceptance_criteria: body_parts.append(f"- [ ] {criterion}") if dependencies: body_parts.extend([ "", "Dependencies:" ]) for dep in dependencies: body_parts.append(f"- {dep}") body = "\n".join(body_parts) # Create with enhancement label return self.create_issue( title=title, body=body, labels=[priority.lower(), "enhancement"] ) def create_bug_issue(self, title: str, description: str, steps_to_reproduce: Optional[List[str]] = None, expected_behavior: str = "", actual_behavior: str = "", environment: str = "") -> Dict[str, Any]: """Create a bug issue with structured format. Args: title: Bug title description: Bug description steps_to_reproduce: List of reproduction steps expected_behavior: What should happen actual_behavior: What actually happens environment: Environment details Returns: Dict containing created issue data """ body_parts = [description] if steps_to_reproduce: body_parts.extend([ "", "Steps to Reproduce:" ]) for i, step in enumerate(steps_to_reproduce, 1): body_parts.append(f"{i}. {step}") if expected_behavior: body_parts.extend([ "", f"Expected Behavior: {expected_behavior}" ]) if actual_behavior: body_parts.extend([ "", f"Actual Behavior: {actual_behavior}" ]) if environment: body_parts.extend([ "", f"Environment: {environment}" ]) body = "\n".join(body_parts) # Create with bug label return self.create_issue( title=title, body=body, labels=["bug"] ) def create_from_template(self, template_file: str, **template_vars) -> Dict[str, Any]: """Create issue from a template file. Args: template_file: Path to template file **template_vars: Variables to substitute in template Returns: Dict containing created issue data """ try: with open(template_file, 'r') as f: template_content = f.read() # Simple template variable substitution for key, value in template_vars.items(): template_content = template_content.replace(f"{{{key}}}", str(value)) # Extract title (first line) and body (rest) lines = template_content.strip().split('\n') if not lines or (len(lines) == 1 and not lines[0].strip()): raise IssueError("Template file is empty") title = lines[0].replace('Title: ', '').strip() body = '\n'.join(lines[1:]).strip() return self.create_issue(title=title, body=body) except FileNotFoundError: raise IssueError(f"Template file not found: {template_file}") except Exception as e: raise IssueError(f"Failed to process template: {e}")