基础设施模板CLI工具:Boilerplates

Boilerplates CLI

Boilerplates 是一个用于管理基础设施模板(boilerplates)的复杂集合,并配备了一个Python CLI工具。它支持Terraform、Docker、Ansible、Kubernetes等多种技术,帮助您快速生成、定制和部署配置模板。

功能特性

  • 多技术模板支持:提供Docker Compose、Terraform、Ansible、Kubernetes、Packer等基础设施模板。
  • 交互式变量收集:通过交互式CLI提示,引导用户为模板变量输入值。
  • 智能默认值管理:支持保存常用变量的默认值,并在多个项目中复用。
  • Git仓库集成:模板库基于Git管理,支持添加、更新和移除自定义模板仓库。
  • Jinja2模板渲染:使用Jinja2引擎渲染模板,支持条件语句和变量替换。
  • 模块化架构:通过模块系统(Module)组织不同技术的模板和命令。
  • 配置文件管理:提供配置管理功能,存储用户默认值和偏好设置。
  • 安全与验证:包含模板语法验证、变量类型检查和路径安全防护。

安装指南

使用安装脚本(推荐)

通过自动化安装脚本安装Boilerplates CLI:

bash 复制代码
# 安装最新版本
curl -fsSL https://raw.githubusercontent.com/christianlempa/boilerplates/main/scripts/install.sh | bash

# 安装特定版本
curl -fsSL https://raw.githubusercontent.com/christianlempa/boilerplates/main/scripts/install.sh | bash -s -- --version v1.2.3

安装脚本使用pipx为CLI工具创建一个隔离环境。安装完成后,终端中即可使用boilerplates命令。

依赖要求

  • Python 3+
  • Git(用于模板库管理)
  • pipx(用于隔离安装,安装脚本会自动检查)

使用说明

基础命令

bash 复制代码
# 查看帮助信息
boilerplates --help

# 查看版本
boilerplates --version

# 更新模板库
boilerplates repo update

# 列出所有已配置的库
boilerplates repo list

模板操作

bash 复制代码
# 列出所有可用的Docker Compose模板
boilerplates compose list

# 查看特定模板的详细信息
boilerplates compose show nginx

# 生成模板(交互式模式)
boilerplates compose generate authentik

# 生成模板到自定义输出目录
boilerplates compose generate nginx my-nginx-server

# 非交互式模式,直接指定变量值
boilerplates compose generate traefik my-proxy \
  --var service_name=traefik \
  --var traefik_enabled=true \
  --var traefik_host=proxy.example.com \
  --no-interactive

管理默认值

bash 复制代码
# 设置变量的默认值
boilerplates compose defaults set container_timezone "America/New_York"
boilerplates compose defaults set restart_policy "unless-stopped"

模板库管理

bash 复制代码
# 添加自定义模板库
boilerplates repo add my-templates https://github.com/user/templates \
  --directory library \
  --branch main

# 移除模板库
boilerplates repo remove my-templates

核心代码

CLI主入口 (cli/__main__.py)

python 复制代码
#!/usr/bin/env python3
"""
Main entry point for the Boilerplates CLI application.
This file serves as the primary executable when running the CLI.
"""
from __future__ import annotations

import importlib
import logging
import pkgutil
import sys
from pathlib import Path
from typing import Optional
from typer import Typer, Context, Option
from rich.console import Console
import cli.modules
from cli.core.registry import registry
from cli.core import repo
# Using standard Python exceptions instead of custom ones

# NOTE: Placeholder version - will be overwritten by release script (.github/workflows/release.yaml)
__version__ = "0.0.0"

app = Typer(
  help="CLI tool for managing infrastructure boilerplates.\n\n[dim]Easily generate, customize, and deploy templates for Docker Compose, Terraform, Kubernetes, and more.\n\n [white]Made with 💜 by [bold]Christian Lempa[/bold]",
  add_completion=True,
  rich_markup_mode="rich",
)
console = Console()

def setup_logging(log_level: str = "WARNING") -> None:
  """Configure the logging system with the specified log level.
  
  Args:
      log_level: The logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  
  Raises:
      ValueError: If the log level is invalid
      RuntimeError: If logging configuration fails
  """
  numeric_level = getattr(logging, log_level.upper(), None)
  if not isinstance(numeric_level, int):
    raise ValueError(
      f"Invalid log level '{log_level}'. Valid levels: DEBUG, INFO, WARNING, ERROR, CRITICAL"
    )
  
  try:
    logging.basicConfig(
      level=numeric_level,
      format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
      datefmt='%Y-%m-%d %H:%M:%S'
    )

    logger = logging.getLogger(__name__)
    logger.setLevel(numeric_level)
  except Exception as e:
    raise RuntimeError(f"Failed to configure logging: {e}")

@app.callback(invoke_without_command=True)
def main(
  version: Optional[bool] = Option(
    None,
    "--version",
    "-v",
    help="Show the application version and exit",
  ),
  log_level: str = Option(
    "WARNING",
    "--log-level",
    "-l",
    help="Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
  ),
  ctx: Context = None,
) -> None:
  """Main entry point for the boilerplates CLI.
  
  Args:
      version: Show version information
      log_level: Logging level for the application
      ctx: Typer context object
  """
  if version:
    console.print(f"Boilerplates CLI [bold]v{__version__}[/bold]")
    raise SystemExit(0)
  
  try:
    setup_logging(log_level)
  except (ValueError, RuntimeError) as e:
    console_err.print(f"[red]Failed to set up logging: {e}[/red]")
    sys.exit(1)
  
  # Discover and register modules
  _discover_modules()
  
  # Register repository management commands
  app.add_typer(repo.app, name="repo")
  
  if ctx.invoked_subcommand is None:
    console.print(app.info.help)
    raise SystemExit(0)

def _discover_modules() -> None:
  """Dynamically discover and register all modules in the modules package."""
  try:
    for _, name, _ in pkgutil.iter_modules(cli.modules.__path__):
      module_path = f"cli.modules.{name}"
      try:
        importlib.import_module(module_path)
        logger.debug(f"Successfully imported module: {name}")
      except Exception as e:
        logger.warning(f"Failed to import module '{name}': {e}")
        continue
    logger.info(f"Module discovery completed. Total modules: {len(list(registry.iter_module_classes()))}")
  except Exception as e:
    logger.error(f"Module discovery failed: {e}")
    raise

if __name__ == "__main__":
  app()

变量集合管理 (cli/core/collection.py)

python 复制代码
from __future__ import annotations

from collections import defaultdict
from typing import Any, Dict, List, Optional, Set, Union
import logging

from .variable import Variable
from .section import VariableSection

logger = logging.getLogger(__name__)


class VariableCollection:
  """Manages variables grouped by sections and builds Jinja context."""

  def __init__(self, spec: dict[str, Any]) -> None:
    """Initialize VariableCollection from a specification dictionary.
    
    Args:
        spec: Dictionary containing the complete variable specification structure
              Expected format (as used in compose.py):
              {
                "section_key": {
                  "title": "Section Title",
                  "prompt": "Optional prompt text",
                  "toggle": "optional_toggle_var_name", 
                  "description": "Optional description",
                  "vars": {
                    "var_name": {
                      "description": "Variable description",
                      "type": "str",
                      "default": "default_value",
                      ...
                    }
                  }
                }
              }
    """
    if not isinstance(spec, dict):
      raise ValueError("Spec must be a dictionary")
    
    self._sections: Dict[str, VariableSection] = {}
    # NOTE: The _variable_map provides a flat, O(1) lookup for any variable by its name,
    # avoiding the need to iterate through sections. It stores references to the same
    # Variable objects contained in the _set structure.
    self._variable_map: Dict[str, Variable] = {}
    self._initialize_sections(spec)
    # Validate dependencies after all sections are loaded
    self._validate_dependencies()

  def _initialize_sections(self, spec: dict[str, Any]) -> None:
    """Initialize sections from the spec."""
    for section_key, section_data in spec.items():
      if not isinstance(section_data, dict):
        continue
      
      # Create VariableSection
      section_dict = {
        "key": section_key,
        "title": section_data.get("title", section_key.title()),
        "description": section_data.get("description"),
        "toggle": section_data.get("toggle"),
        "required": section_data.get("required", section_key == "general"),
      }
      
      # Handle dependencies
      if needs := section_data.get("needs"):
        section_dict["needs"] = needs
      
      section = VariableSection(section_dict)
      
      # Add variables to section
      if "vars" in section_data and isinstance(section_data["vars"], dict):
        for var_name, var_data in section_data["vars"].items():
          if not isinstance(var_data, dict):
            continue
          
          # Create Variable
          var_dict = var_data.copy()
          var_dict["name"] = var_name
          var_dict["origin"] = "template"
          
          variable = Variable(var_dict)
          
          # Store in section and flat map
          section.variables[var_name] = variable
          self._variable_map[var_name] = variable
      
      self._sections[section_key] = section
  
  def _validate_dependencies(self) -> None:
    """Validate that all section dependencies exist."""
    for section_key, section in self._sections.items():
      for dep in section.needs:
        if dep not in self._sections:
          logger.warning(
            f"Section '{section_key}' depends on non-existent section '{dep}'"
          )

  def get_section(self, section_key: str) -> Optional[VariableSection]:
    """Get a section by its key."""
    return self._sections.get(section_key)

  def get_sections(self) -> Dict[str, VariableSection]:
    """Get all sections."""
    return self._sections.copy()

  def is_section_satisfied(self, section_key: str) -> bool:
    """Check if a section's dependencies are satisfied."""
    section = self._sections.get(section_key)
    if not section:
      return False
    
    # General section is always satisfied
    if section_key == "general":
      return True
    
    # Check all dependencies
    for dep in section.needs:
      dep_section = self._sections.get(dep)
      if not dep_section or not dep_section.is_enabled():
        return False
    
    return True

  def get_jinja_context(self) -> Dict[str, Any]:
    """Build Jinja2 template context from all variables.
    
    Returns:
        Dictionary mapping variable names to their values for
相关推荐
likerhood2 小时前
4. pytorch线性回归
人工智能·pytorch·线性回归
趁你还年轻_2 小时前
claude skills 介绍
人工智能
Calebbbbb2 小时前
从 Vibe Coding 到 SDD:淘宝 AI 编码最佳实践分享
人工智能·ai编程
GIOTTO情2 小时前
Infoseek 危机公关系统技术实现深度解析:AI 驱动的全链路舆情处置架构与工程实践
人工智能·架构
ayingmeizi1632 小时前
智慧养老的数字化转型:AI CRM如何重构全链路增长
大数据·人工智能·重构
GEO_NEWS2 小时前
AI重构全球贸易版图?世贸报告:2040年或增长近四成
人工智能
wfeqhfxz25887822 小时前
自动驾驶环境中的车辆目标检测-Mask-RCNN模型应用与参数配置
人工智能·目标检测·自动驾驶
艾醒(AiXing-w)2 小时前
大模型原理剖析——多头潜在注意力 (MLA) 详解
人工智能·机器学习
A林玖2 小时前
【深度学习】transformer架构
人工智能·深度学习·transformer