Parcourir la source

updated metadata handling and prompt display

xcad il y a 5 mois
Parent
commit
86684d20f0
6 fichiers modifiés avec 137 ajouts et 128 suppressions
  1. 14 16
      cli/core/module.py
  2. 29 14
      cli/core/prompt.py
  3. 2 2
      cli/core/template.py
  4. 4 4
      cli/core/variables.py
  5. 0 92
      cli/modules/compose.meta.yaml
  6. 88 0
      cli/modules/compose.py

+ 14 - 16
cli/core/module.py

@@ -29,23 +29,21 @@ class Module(ABC):
       )
     
     self.libraries = LibraryManager()
-    self.metadata = self._load_metadata()
+    self.metadata = self._build_metadata()
   
-  def _load_metadata(self) -> Dict[str, Any]:
-    """Load module metadata from .meta.yaml file if it exists."""
-    import inspect
-    # Get the path to the actual module file
-    module_path = Path(inspect.getfile(self.__class__))
-    meta_file = module_path.with_suffix('.meta.yaml')
-    
-    if meta_file.exists():
-      try:
-        with open(meta_file, 'r') as f:
-          return yaml.safe_load(f) or {}
-      except Exception as e:
-        logger.debug(f"Failed to load metadata for {self.name}: {e}")
-    
-    return {}
+  def _build_metadata(self) -> Dict[str, Any]:
+    """Build metadata from class attributes."""
+    metadata = {}
+    
+    # Add categories if defined
+    if hasattr(self, 'categories'):
+      metadata['categories'] = self.categories
+    
+    # Add variable metadata if defined
+    if hasattr(self, 'variable_metadata'):
+      metadata['variables'] = self.variable_metadata
+    
+    return metadata
 
 
   def list(self):

+ 29 - 14
cli/core/prompt.py

@@ -4,7 +4,7 @@ from collections import OrderedDict
 from rich.console import Console
 from rich.prompt import Prompt, Confirm, IntPrompt, FloatPrompt
 import logging
-from .variables import TemplateVariable
+from .variables import Variable
 
 logger = logging.getLogger('boilerplates')
 console = Console()
@@ -13,11 +13,11 @@ console = Console()
 class SimplifiedPromptHandler:
   """Prompt handler for template-detected variables."""
   
-  def __init__(self, variables: Dict[str, TemplateVariable]):
+  def __init__(self, variables: Dict[str, Variable]):
     """Initialize with template variables.
     
     Args:
-      variables: Dict of variable name to TemplateVariable object
+      variables: Dict of variable name to Variable object
     """
     self.variables = variables
     self.values = {}
@@ -80,8 +80,9 @@ class SimplifiedPromptHandler:
     # Deduplicate variables
     var_names = list(dict.fromkeys(var_names))  # Preserves order while removing duplicates
     
-    # Get icon for this category
+    # Get icon and description for this category
     icon = self._get_category_icon(display_name)
+    description = self._get_category_description(display_name)
     
     # Check if this group has an enabler
     group_name = display_name.lower()
@@ -90,14 +91,16 @@ class SimplifiedPromptHandler:
       enabler_var = self.variables[group_name]
       if enabler_var.is_enabler:
         enabler = group_name
-        # Show section header with icon
-        console.print(f"\n{icon}[bold cyan]{display_name}[/bold cyan]")
+        # Show section header with icon and description
+        header = f"\n{icon}[bold cyan]{display_name}[/bold cyan]"
+        if description:
+          header += f" [dim]- {description}[/dim]"
+        console.print(header)
         console.print()  # Add newline after header
         enabled = Confirm.ask(
           f"Enable {enabler}?", 
           default=bool(enabler_var.default)
         )
-        console.print()  # Add newline after enabler prompt
         self.values[enabler] = enabled
         
         if not enabled:
@@ -121,7 +124,10 @@ class SimplifiedPromptHandler:
     # Process required variables
     if required:
       if not enabler:  # Show header only if we haven't shown it for enabler
-        console.print(f"\n{icon}[bold cyan]{display_name}[/bold cyan]")
+        header = f"\n{icon}[bold cyan]{display_name}[/bold cyan]"
+        if description:
+          header += f" [dim]- {description}[/dim]"
+        console.print(header)
         console.print()  # Add newline after header
       for var_name in required:
         var = self.variables[var_name]
@@ -134,14 +140,15 @@ class SimplifiedPromptHandler:
       if display_optional:
         # Show section header if not already shown
         if not required and not enabler:
-          console.print(f"\n{icon}[bold cyan]{display_name}[/bold cyan]")
+          header = f"\n{icon}[bold cyan]{display_name}[/bold cyan]"
+          if description:
+            header += f" [dim]- {description}[/dim]"
+          console.print(header)
           console.print()  # Add newline after header
         
         # Show current values with label
-        console.print()  # Add newline before values
-        console.print("[white]Default values:[/white]")
+        console.print("\n[white]Default values [/white]", end="")
         self._show_variables_inline(display_optional)
-        console.print()  # Add newline after values
         
         if Confirm.ask("Do you want to change any values?", default=False):
           console.print()  # Add newline after prompt
@@ -168,7 +175,7 @@ class SimplifiedPromptHandler:
           formatted_value = f"'{value}'"
         else:
           formatted_value = str(value)
-        items.append(f"{var.display_name}: {formatted_value}")
+        items.append(f"{var.display_name}: [cyan]({formatted_value})[/cyan]")
     
     if items:
       console.print(f"[dim white]{', '.join(items)}[/dim white]")
@@ -182,9 +189,17 @@ class SimplifiedPromptHandler:
         return cat_meta['icon'] + ' '
     return ''  # No icon if not defined in metadata
   
+  def _get_category_description(self, category: str) -> str:
+    """Get description for a category."""
+    if self.category_metadata and category.lower() in self.category_metadata:
+      cat_meta = self.category_metadata[category.lower()]
+      if 'description' in cat_meta:
+        return cat_meta['description']
+    return ''  # No description if not defined in metadata
+  
   def _prompt_variable(
     self, 
-    var: TemplateVariable, 
+    var: Variable,
     required: bool = False,
     current_value: Any = None
   ) -> Any:

+ 2 - 2
cli/core/template.py

@@ -6,7 +6,7 @@ import re
 from jinja2 import Environment, BaseLoader, meta, nodes, TemplateSyntaxError
 import frontmatter
 from .exceptions import TemplateValidationError
-from .variables import TemplateVariable, analyze_template_variables
+from .variables import Variable, analyze_template_variables
 
 
 @dataclass
@@ -37,7 +37,7 @@ class Template:
   # Template variable analysis results
   vars: Set[str] = field(default_factory=set, init=False)
   var_defaults: Dict[str, Any] = field(default_factory=dict, init=False)
-  variables: Dict[str, TemplateVariable] = field(default_factory=dict, init=False)  # Analyzed variables
+  variables: Dict[str, Variable] = field(default_factory=dict, init=False)  # Analyzed variables
   
   def __post_init__(self):
     """Initialize computed properties after dataclass initialization."""

+ 4 - 4
cli/core/variables.py

@@ -3,7 +3,7 @@ from dataclasses import dataclass, field
 
 
 @dataclass
-class TemplateVariable:
+class Variable:
   """Variable detected from template analysis.
   
   Represents a variable found in a template with all its properties:
@@ -46,7 +46,7 @@ def analyze_template_variables(
   vars_used: Set[str],
   var_defaults: Dict[str, Any],
   template_content: str
-) -> Dict[str, TemplateVariable]:
+) -> Dict[str, Variable]:
   """Analyze template variables and create TemplateVariable objects.
   
   Args:
@@ -55,7 +55,7 @@ def analyze_template_variables(
     template_content: The raw template content for additional analysis
   
   Returns:
-    Dict mapping variable name to TemplateVariable object
+    Dict mapping variable name to Variable object
   """
   variables = {}
   
@@ -63,7 +63,7 @@ def analyze_template_variables(
   enablers = _detect_enablers(template_content)
   
   for var_name in vars_used:
-    var = TemplateVariable(
+    var = Variable(
       name=var_name,
       default=var_defaults.get(var_name)
     )

+ 0 - 92
cli/modules/compose.meta.yaml

@@ -1,92 +0,0 @@
-# Metadata for Docker Compose module
-# Provides hints, icons, and descriptions for variables
-
-categories:
-  general:
-    icon: "📦"
-    description: "General container settings"
-  network:
-    icon: "🌐"
-    description: "Network configuration"
-    tip: "Use external networks for cross-container communication"
-  traefik:
-    icon: "🔀"
-    description: "Reverse proxy and load balancer"
-    tip: "Automatic SSL certificates with Let's Encrypt"
-  swarm:
-    icon: "🐝"
-    description: "Docker Swarm orchestration"
-  service_port:
-    icon: "🔌"
-    description: "Port mappings"
-  nginx_dashboard:
-    icon: "📊"
-    description: "Nginx monitoring dashboard"
-
-variables:
-  service_name:
-    hint: "e.g., webapp, api, database"
-    validation: "^[a-z][a-z0-9-]*$"
-  
-  container_name:
-    hint: "Leave empty to use service name"
-    description: "Custom container name"
-  
-  # Network variables
-  network:
-    description: "Enable custom network configuration"
-  
-  network.name:
-    hint: "e.g., frontend, backend, bridge"
-    description: "Docker network name"
-  
-  network.external:
-    hint: "Use 'true' for existing networks"
-    tip: "External networks must be created before running"
-  
-  # Traefik variables
-  traefik:
-    description: "Enable Traefik reverse proxy"
-    tip: "Requires Traefik to be running separately"
-  
-  traefik.host:
-    hint: "e.g., app.example.com, api.mydomain.org"
-    description: "Domain name for your service"
-    validation: "^[a-z0-9][a-z0-9.-]*[a-z0-9]$"
-  
-  traefik.tls:
-    description: "Enable HTTPS/TLS"
-    tip: "Requires valid domain and DNS configuration"
-  
-  traefik.certresolver:
-    hint: "e.g., letsencrypt, staging"
-    description: "Certificate resolver name"
-  
-  # Swarm variables
-  swarm:
-    description: "Enable Docker Swarm mode"
-    tip: "Requires Docker Swarm to be initialized"
-  
-  swarm.replicas:
-    hint: "Number of container instances"
-    validation: "^[1-9][0-9]*$"
-  
-  # Port variables
-  service_port_http:
-    hint: "e.g., 8080, 3000, 80"
-    description: "HTTP port mapping"
-    validation: "^[1-9][0-9]{0,4}$"
-  
-  service_port_https:
-    hint: "e.g., 8443, 3443, 443"
-    description: "HTTPS port mapping"
-    validation: "^[1-9][0-9]{0,4}$"
-  
-  # Nginx dashboard
-  nginx_dashboard:
-    description: "Enable Nginx status dashboard"
-  
-  nginx_dashboard_port_dashboard:
-    hint: "e.g., 8081, 9090"
-    description: "Dashboard port"
-    validation: "^[1-9][0-9]{0,4}$"

+ 88 - 0
cli/modules/compose.py

@@ -7,6 +7,94 @@ class ComposeModule(Module):
   name = "compose"
   description = "Manage Docker Compose configurations"
   files = ["docker-compose.yml", "compose.yml", "compose.yaml"]
+  
+  # Category metadata
+  categories = {
+    "general": {
+      "icon": "󰖷 ",
+      "description": "General container settings"
+    },
+    "network": {
+      "icon": "󰈀 ",
+      "description": "Network configuration",
+      "tip": "Use external networks for cross-container communication"
+    },
+    "traefik": {
+      "icon": " ",
+      "description": "Reverse proxy and load balancer",
+      "tip": "Automatic SSL certificates with Let's Encrypt"
+    },
+    "swarm": {
+      "icon": " ",
+      "description": "Docker Swarm orchestration"
+    }
+  }
+  
+  # Variable metadata
+  variable_metadata = {
+    "service_name": {
+      "hint": "e.g., webapp, api, database",
+      "validation": "^[a-z][a-z0-9-]*$"
+    },
+    "container_name": {
+      "hint": "Leave empty to use service name",
+      "description": "Custom container name"
+    },
+    "network": {
+      "description": "Enable custom network configuration"
+    },
+    "network.name": {
+      "hint": "e.g., frontend, backend, bridge",
+      "description": "Docker network name"
+    },
+    "network.external": {
+      "hint": "Use 'true' for existing networks",
+      "tip": "External networks must be created before running"
+    },
+    "traefik": {
+      "description": "Enable Traefik reverse proxy",
+      "tip": "Requires Traefik to be running separately"
+    },
+    "traefik.host": {
+      "hint": "e.g., app.example.com, api.mydomain.org",
+      "description": "Domain name for your service",
+      "validation": "^[a-z0-9][a-z0-9.-]*[a-z0-9]$"
+    },
+    "traefik.tls": {
+      "description": "Enable HTTPS/TLS",
+      "tip": "Requires valid domain and DNS configuration"
+    },
+    "traefik.certresolver": {
+      "hint": "e.g., letsencrypt, staging",
+      "description": "Certificate resolver name"
+    },
+    "swarm": {
+      "description": "Enable Docker Swarm mode",
+      "tip": "Requires Docker Swarm to be initialized"
+    },
+    "swarm.replicas": {
+      "hint": "Number of container instances",
+      "validation": "^[1-9][0-9]*$"
+    },
+    "service_port_http": {
+      "hint": "e.g., 8080, 3000, 80",
+      "description": "HTTP port mapping",
+      "validation": "^[1-9][0-9]{0,4}$"
+    },
+    "service_port_https": {
+      "hint": "e.g., 8443, 3443, 443",
+      "description": "HTTPS port mapping",
+      "validation": "^[1-9][0-9]{0,4}$"
+    },
+    "nginx_dashboard": {
+      "description": "Enable Nginx status dashboard"
+    },
+    "nginx_dashboard_port_dashboard": {
+      "hint": "e.g., 8081, 9090",
+      "description": "Dashboard port",
+      "validation": "^[1-9][0-9]{0,4}$"
+    }
+  }
 
 # Register the module
 registry.register(ComposeModule)