markitect-main/tddai/issue_writer.py

120 lines
5.2 KiB
Python
Raw Normal View History

"""
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.
"""
import os
from typing import Dict, Any, Optional
from gitea import GiteaClient, GiteaConfig
from .config import get_config
from .exceptions import IssueError
class IssueWriter:
"""Writes issue updates using the Gitea integration facade."""
def __init__(self, config=None, auth_token=None):
self.config = config or get_config()
feat: Implement comprehensive IssueCreator system and create CLI roadmap issues IssueCreator Implementation: - Add tddai/issue_creator.py with full POST API functionality for issue creation - Support multiple creation methods: basic, enhancement, bug, template-based - Include structured issue formatting with acceptance criteria and dependencies - Template system with variable substitution for reusable issue creation Authentication Fix: - Fix critical authentication bug: use GITEA_API_TOKEN instead of GITEA_TOKEN - Update both IssueCreator and IssueWriter for consistency - Update all tests and documentation to reflect correct environment variable Comprehensive Test Suite: - Add 15 unit tests for IssueCreator (tests/test_issue_creator.py) - Add 5 integration tests for full API lifecycle (tests/test_issue_integration.py) - Create test_environment_variable_detection to prevent future auth issues - Total 33 tests covering complete issue handling workflow CLI Integration: - Enhance tddai_cli.py with 3 new commands: create-issue, create-enhancement, create-from-template - Add comprehensive argument parsing with optional fields and priority support - Include user-friendly output with next step guidance - Update package exports to include IssueCreator CLI Roadmap Execution: - Successfully create 8 CLI implementation issues (#12-#19) in Gitea - Resolve mismatch between NEXT.md roadmap and actual Gitea issues - Issues prioritized for core USPs: Database Query CLI and AST Query CLI - Remove local MISSING_ISSUES.md file after successful creation Framework Maturity: - Complete CRUD operations for issue management (Create, Read, Update, Delete) - Robust error handling and API integration patterns - Full authentication and environment variable management - Ready for production CLI implementation workflow 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-24 23:36:07 +02:00
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 update_issue(self, issue_number: int, update_data: Dict[str, Any]) -> Dict[str, Any]:
"""Update an issue via the gitea integration."""
if not self.auth_token:
raise IssueError("Authentication token required for issue updates")
try:
issue = self.gitea_client.issues.update(issue_number, **update_data)
return self.gitea_client.issues.to_dict(issue)
except Exception as e:
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."""
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}")
def update_issue_body(self, issue_number: int, new_body: str) -> Dict[str, Any]:
"""Update only the body of an issue."""
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}")
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'")
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}")
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."""
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)."""
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}")
def remove_from_milestone(self, issue_number: int) -> Dict[str, Any]:
"""Remove issue from its current milestone."""
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}")
def update_labels(self, issue_number: int, labels: list) -> Dict[str, Any]:
"""Update issue labels completely."""
if not self.auth_token:
raise IssueError("Authentication token required for label updates")
try:
issue = self.gitea_client.issues.set_labels(issue_number, labels)
return self.gitea_client.issues.to_dict(issue)
except Exception as e:
raise IssueError(f"Failed to update labels for issue #{issue_number}: {e}")
def add_labels(self, issue_number: int, new_labels: list) -> Dict[str, Any]:
"""Add labels to issue (preserving existing labels)."""
try:
issue = self.gitea_client.issues.add_labels(issue_number, new_labels)
return self.gitea_client.issues.to_dict(issue)
except Exception as e:
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:
issue = self.gitea_client.issues.remove_labels(issue_number, labels_to_remove)
return self.gitea_client.issues.to_dict(issue)
except Exception as e:
raise IssueError(f"Failed to remove labels from issue #{issue_number}: {e}")