Guidance for AI Agents working with this repository.
A sophisticated collection of infrastructure templates (boilerplates) with a Python CLI for management. Supports Terraform, Docker, Ansible, Kubernetes, etc. Built with Typer (CLI) and Jinja2 (templating).
# Run the CLI application
python3 -m cli
# Debugging and Testing commands
python3 -m cli --log-level DEBUG compose list
Should always happen before pushing anything to the repository.
yamllint for YAML files and ruff for Python code.The project is stored in a public GitHub Repository, use issues, and branches for features/bugfixes and open PRs for merging.
Naming Conventions and Best-Practices:
feature/2314-add-feature, problem/1249-fix-bugtype(scope): subject (e.g., fix(compose): correct variable parsing).cli/ - Python CLI application source code
cli/core/ - Core Components of the CLI applicationcli/modules/ - Modules implementing variable specs and technology-specific functionscli/__main__.py - CLI entry point, auto-discovers modules and registers commandslibrary/ - Template collections organized by module
library/ansible/ - Ansible playbooks and configurationslibrary/compose/ - Docker Compose configurationslibrary/docker/ - Docker templateslibrary/kubernetes/ - Kubernetes deploymentslibrary/packer/ - Packer templateslibrary/terraform/ - OpenTofu/Terraform templates and examplesarchetypes/ - Testing tool for template snippets (archetype development)
archetypes/__init__.py - Package initializationarchetypes/__main__.py - CLI tool entry pointarchetypes/<module>/ - Module-specific archetype snippets (e.g., archetypes/compose/)cli/core/collection.py - VariableCollection class (manages sections and variables)
_sections (dict of VariableSection objects), _variable_map (flat lookup dict)get_satisfied_values() (returns enabled variables), apply_defaults(), sort_sections()cli/core/config.py - Configuration management (loading, saving, validation)cli/core/display.py - Centralized CLI output rendering (Always use this to display output - never print directly)cli/core/exceptions.py - Custom exceptions for error handling (Always use this for raising errors)cli/core/library.py - LibraryManager for template discovery from git-synced libraries and static file pathscli/core/module.py - Abstract base class for modules (defines standard commands)cli/core/prompt.py - Interactive CLI prompts using rich librarycli/core/registry.py - Central registry for module classes (auto-discovers modules)cli/core/repo.py - Repository management for syncing git-based template librariescli/core/section.py - VariableSection class (stores section metadata and variables)
key, title, toggle, required, needs, variables (dict of Variable objects)cli/core/template.py - Template Class for parsing, managing and rendering templatescli/core/variable.py - Variable class (stores variable metadata and values)
name, type, value (stores default or current value), description, sensitive, needsvalue attribute, NOT in a separate default attributecli/core/validators.py - Semantic validators for template content (Docker Compose, YAML, etc.)cli/core/version.py - Version comparison utilities for semantic versioningModule Structure: Modules can be either single files or packages:
cli/modules/modulename.py (for simple modules)cli/modules/modulename/ with __init__.py (for multi-schema modules)Creating Modules:
Module from cli/core/module.pyname, description, and schema_version class attributesspec_v1_0.py, spec_v1_1.py)registry.register(YourModule) at module bottomModule Spec: Module-wide variable specification defining defaults for all templates of that kind.
Important: The spec variable is an OrderedDict (or regular dict), NOT a VariableCollection object. It's converted to VariableCollection when needed.
Example:
from collections import OrderedDict
# Spec is a dict/OrderedDict, not a VariableCollection
spec = OrderedDict({
"general": {
"title": "General",
"vars": {
"common_var": {
"type": "str",
"default": "value",
"description": "A common variable"
}
}
},
"networking": {
"title": "Network",
"toggle": "net_enabled",
"vars": {...}
}
})
# To use the spec, convert it to VariableCollection:
from cli.core.collection import VariableCollection
variable_collection = VariableCollection(spec)
Multi-Schema Modules: For modules supporting multiple schema versions, use package structure:
cli/modules/compose/
__init__.py # Module class, loads appropriate spec
spec_v1_0.py # Schema 1.0 specification
spec_v1_1.py # Schema 1.1 specification
Existing Modules:
cli/modules/compose/ - Docker Compose package with schema 1.0 and 1.1 support
spec_v1_0.py - Basic compose specspec_v1_1.py - Extended with network_mode, swarm support(Work in Progress): terraform, docker, ansible, kubernetes, packer modules
~/.config/boilerplates/libraries/{name}/Library Types:
git: Requires url, branch, directory fieldsstatic: Requires path field (absolute or relative to config)Duplicate Handling:
DuplicateTemplateErroralloy.default, alloy.local)compose show alloy loads from first librarycompose show alloy.localConfig Example:
libraries:
- name: default # Highest priority (checked first)
type: git
url: https://github.com/user/templates.git
branch: main
directory: library
- name: local # Lower priority
type: static
path: ~/my-templates
url: '' # Backward compatibility fields
branch: main
directory: .
Note: Static libraries include dummy url/branch/directory fields for backward compatibility with older CLI versions.
~/.config/boilerplates/config.yamlExternal code should NEVER directly call IconManager or console.print, instead always use DisplayManager methods.
DisplayManager provides a centralized interface for ALL CLI output rendering (Use display_*** methods from DisplayManager for ALL output)IconManager provides Nerd Font icons internally for DisplayManager, don't use Emojis or direct console accessTemplates are directory-based. Each template is a directory containing all the necessary files and subdirectories for the boilerplate.
Requires template.yaml or template.yml with metadata and variables:
---
kind: compose
schema: "1.0" # Optional: Defaults to 1.0 if not specified
metadata:
name: My Nginx Template
description: >
A template for a simple Nginx service.
Project: https://...
Source: https://
Documentation: https://
version: 0.1.0
author: Christian Lempa
date: '2024-10-01'
spec:
general:
vars:
nginx_version:
type: string
description: The Nginx version to use.
default: latest
Templates and modules use schema versioning to ensure compatibility. Each module defines a supported schema version, and templates declare which schema version they use.
---
kind: compose
schema: "1.0" # Defaults to 1.0 if not specified
metadata:
name: My Template
version: 1.0.0
# ... other metadata fields
spec:
# ... variable specifications
How It Works:
schema_version (e.g., "1.1")schema at the top level (defaults to "1.0")IncompatibleSchemaVersionErrorBehavior:
schema field default to "1.0" (backward compatible)When to Use:
schema: "1.1"Single-File Module Example:
class SimpleModule(Module):
name = "simple"
description = "Simple module"
schema_version = "1.0"
spec = VariableCollection.from_dict({...}) # Single spec
Multi-Schema Module Example:
# cli/modules/compose/__init__.py
class ComposeModule(Module):
name = "compose"
description = "Manage Docker Compose configurations"
schema_version = "1.1" # Highest schema version supported
def get_spec(self, template_schema: str) -> VariableCollection:
"""Load spec based on template schema version."""
if template_schema == "1.0":
from .spec_v1_0 import get_spec
elif template_schema == "1.1":
from .spec_v1_1 import get_spec
return get_spec()
Version Management:
cli/__init__.py as __version____version__ for releases.j2): Rendered by Jinja2, .j2 extension removed in output. Support {% include %} and {% import %}..j2 files copied as-is.Precedence (lowest to highest):
spec (defaults for all templates of that kind)spec (overrides module defaults)config.yaml (overrides template and module defaults)--var (highest priority)Variable Types:
str (default), int, float, boolemail - Email validation with regexurl - URL validation (requires scheme and host)hostname - Hostname/domain validationenum - Choice from options listVariable Properties:
sensitive: true - Masked in prompts/display (e.g., passwords)autogenerated: true - Auto-generates value if empty (shows *auto placeholder)default - Default valuedescription - Variable descriptionprompt - Custom prompt text (overrides description)extra - Additional help textoptions - List of valid values (for enum type)Section Features:
required: true (general is implicit). Users must provide all values.toggle: "bool_var_name". If false, section is skipped.needs: "section_name" or needs: ["sec1", "sec2"]. Dependent sections only shown when dependencies are enabled. Auto-validated (detects circular/missing/self dependencies). Topologically sorted.Example Section with Dependencies:
spec:
traefik:
title: Traefik
required: false
toggle: traefik_enabled
vars:
traefik_enabled:
type: bool
default: false
traefik_host:
type: hostname
traefik_tls:
title: Traefik TLS/SSL
needs: traefik
toggle: traefik_tls_enabled
vars:
traefik_tls_enabled:
type: bool
default: true
traefik_tls_certresolver:
type: str
sensitive: false
default: myresolver
Jinja2 Validation:
Semantic Validation:
cli/core/validators.pyContentValidator abstract base classDockerComposeValidator, YAMLValidatorcompose validate command with --semantic flag (enabled by default)Uses rich library for interactive prompts. Supports:
sensitive: true variables)*auto placeholder, generate on render)To skip the prompt use the --no-interactive flag, which will use defaults or empty values.
Standard Module Commands (auto-registered for all modules):
list - List all templatessearch <query> - Search templates by IDshow <id> - Show template detailsgenerate <id> [directory] - Generate from template (supports --dry-run, --var, --no-interactive)validate [id] - Validate templates (Jinja2 + semantic)defaults - Manage config defaults (get, set, rm, clear, list)Core Commands:
repo sync - Sync git-based librariesrepo list - List configured librariesThe archetypes package provides a testing tool for developing and testing individual template snippets (Jinja2 files) without needing a full template directory structure.
Archetypes are template "snippets" or "parts" that can be tested in isolation. This is useful for:
# Run the archetypes tool
python3 -m archetypes
# List all archetypes for a module
python3 -m archetypes compose list
# Show details of an archetype (displays variables and content)
python3 -m archetypes compose show network-v1
# Preview generated output (always in preview mode - never writes files)
python3 -m archetypes compose generate network-v1
# Preview with variable overrides
python3 -m archetypes compose generate network-v1 \
--var network_mode=macvlan \
--var network_macvlan_ipv4_address=192.168.1.100
# Preview with reference directory (for context only - no files written)
python3 -m archetypes compose generate network-v1 /tmp/output --var network_mode=host
archetypes/
__init__.py # Package initialization
__main__.py # CLI tool (auto-discovers modules)
compose/ # Module-specific archetypes
network-v1.j2 # Archetype snippet (just a .j2 file)
volumes-v1.j2 # Another archetype
terraform/ # Another module's archetypes
vpc.j2
archetypes/ for subdirectories (module names)cli/modules/<module>/spec_v*.py for defaultslist, show, generategenerate command NEVER writes files - it always shows preview output onlyHow it works:
archetypes/ (e.g., compose).j2 files (no template.yaml needed)cli/modules/<module>/spec_v*.pyArchetypeTemplate class:
--var) on top of spec defaultsVariable defaults source:
# Defaults come from module spec files
from cli.modules.compose import spec # OrderedDict with variable definitions
vc = VariableCollection(spec) # Convert to VariableCollection
# Extract all variables with their default values
for section_name, section in vc._sections.items():
for var_name, var in section.variables.items():
if var.value is not None: # var.value stores the default
render_context[var_name] = var.value