From 0147ff152a9e589c1b8594caaa1da1877cbb37b5 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Wed, 4 Feb 2026 16:59:26 +0000 Subject: [PATCH] fix: Add Gitea Actions CI workflow and fix linting issues --- src/templates.py | 148 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/templates.py diff --git a/src/templates.py b/src/templates.py new file mode 100644 index 0000000..dae2a2a --- /dev/null +++ b/src/templates.py @@ -0,0 +1,148 @@ +"""Template management for commit message formatting.""" + +import re +from dataclasses import dataclass +from typing import Any, Dict, List, Optional + + +@dataclass +class TemplateVariable: + """Represents a template variable with its value.""" + name: str + value: str + display_name: Optional[str] = None + + +class TemplateManager: + """Manages commit message templates.""" + + DEFAULT_TEMPLATES = { + "simple": "{type}{scope}: {description}", + "detailed": "{type}({scope}): {description}\n\n{body}", + "conventional": "{type}{scope}: {description}\n\n{files}", + "verbose": "{type}{scope}: {description}\n\n{files}\n\n{body}", + } + + def __init__(self, template: Optional[str] = None): + """Initialize template manager. + + Args: + template: Template string to use. If not provided, uses simple template. + """ + self.template = template or self.DEFAULT_TEMPLATES["simple"] + + def render( + self, + type: str, + scope: str, + description: str, + body: str = "", + files: Optional[List[str]] = None, + **kwargs: Any + ) -> str: + """Render a commit message from template. + + Args: + type: Commit type (feat, fix, docs, etc.) + scope: Commit scope. + description: Commit description. + body: Optional extended body text. + files: Optional list of changed files. + **kwargs: Additional template variables. + + Returns: + Rendered commit message string. + """ + files_part = "" + if files: + files_str = "\n".join(f" - {f}" for f in files[:10]) + if len(files) > 10: + files_str += f"\n ... and {len(files) - 10} more" + files_part = f"\n\nFiles changed:\n{files_str}" + + variables = { + "type": type, + "scope": scope, + "description": description, + "body": body, + "files": files_part, + **kwargs + } + + result = self.template + for key, value in variables.items(): + placeholder = f"{{{key}}}" + result = result.replace(placeholder, str(value)) + + return result.strip() + + @classmethod + def get_default_templates(cls) -> Dict[str, str]: + """Get all default templates. + + Returns: + Dictionary of template name to template string. + """ + return cls.DEFAULT_TEMPLATES.copy() + + @classmethod + def validate_template(cls, template: str) -> tuple[bool, Optional[str]]: + """Validate a template string. + + Args: + template: Template string to validate. + + Returns: + Tuple of (is_valid, error_message). + """ + try: + placeholders = re.findall(r"\{(\w+)\}", template) + valid_placeholders = {"type", "scope", "description", "body", "files"} + invalid = set(placeholders) - valid_placeholders + if invalid: + return False, f"Invalid placeholders: {', '.join(sorted(invalid))}" + return True, None + except re.error as e: + return False, f"Invalid regex in template: {e}" + + def generate_suggestions(self, changes: List[str]) -> List[str]: + """Generate description suggestions based on changed files. + + Args: + changes: List of changed file paths. + + Returns: + List of suggested descriptions. + """ + suggestions = [] + for path in changes[:5]: + filename = path.split("/")[-1] + base_name = ".".join(filename.split(".")[:-1]) + if base_name: + suggestions.append(f"update {base_name}") + else: + suggestions.append(f"update {filename}") + return suggestions + + +def format_message( + type: str, + scope: str, + description: str, + template: Optional[str] = None, + files: Optional[List[str]] = None +) -> str: + """Format a commit message. + + Args: + type: Commit type. + scope: Commit scope. + description: Commit description. + template: Optional custom template. + files: Optional list of changed files. + + Returns: + Formatted commit message. + """ + manager = TemplateManager(template) + return manager.render(type, scope, description, files=files)