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 filesruff for Python code:
ruff check --fix . - Check and auto-fix linting errors (including unused imports)ruff format . - Format code according to style guidelinesThe 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/ - Centralized CLI output rendering package (Always use DisplayManager - never print directly)
__init__.py - Package exports (DisplayManager, DisplaySettings, IconManager)display_manager.py - Main DisplayManager facadedisplay_settings.py - DisplaySettings configuration classicon_manager.py - IconManager for Nerd Font iconsvariable_display.py - VariableDisplayManager for variable renderingtemplate_display.py - TemplateDisplayManager for template displaystatus_display.py - StatusDisplayManager for status messagestable_display.py - TableDisplayManager for table renderingcli/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 supportCompose Module Schema Differences:
Schema 1.0:
network section: Has toggle: "network_enabled" with explicit network_enabled boolean variable (default: False)ports section: Has toggle: "ports_enabled" with explicit ports_enabled boolean variable (default: True){% if network_enabled %} and {% if ports_enabled %}Schema 1.1:
network section: NO toggle - always available, controlled by network_mode enum (bridge/host/macvlan)ports section: Has toggle: "ports_enabled" but the variable is AUTO-CREATED and NOT available in templates{% if network_mode == 'bridge' %} for networks and {% if network_mode == 'bridge' and not traefik_enabled %} for portsnetwork_enabled doesn't exist - use network_mode conditionals insteadports_enabled is not usable in templates - use network_mode and traefik_enabled conditionals instead(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.yamlCRITICAL RULE - NEVER violate this:
console.print() outside of display manager classes (cli/core/display/ directory)Console from rich.console except in display manager classes or cli/__main__.pymodule_instance.display.display_*() or display.display_*() methods for ALL outputcli/core/display/*.py) are the ONLY exception - they implement console outputRationale:
DisplayManager provides a centralized interface for ALL CLI output renderingIconManager provides Nerd Font icons internally for DisplayManager - never use emojis or direct iconsDisplayManager Architecture (Refactored for Single Responsibility Principle):
DisplayManager acts as a facade that delegates to specialized manager classes:
VariableDisplayManager - Handles all variable-related rendering
render_variable_value() - Variable value formatting with context awarenessrender_section() - Section header displayrender_variables_table() - Complete variables table with dependenciesTemplateDisplayManager - Handles all template-related rendering
render_template() - Main template display coordinatorrender_template_header() - Template metadata displayrender_file_tree() - Template file structure visualizationrender_file_generation_confirmation() - Files preview before generationStatusDisplayManager - Handles status messages and error display
display_message() - Core message formatting with level-based routingdisplay_error(), display_warning(), display_success(), display_info() - Convenience methodsdisplay_template_render_error() - Detailed render error displaydisplay_warning_with_confirmation() - Interactive warning promptsTableDisplayManager - Handles table rendering
render_templates_table() - Templates list with library indicatorsrender_status_table() - Status tables with success/error indicatorsrender_config_tree() - Configuration tree visualizationUsage Pattern:
# External code uses DisplayManager methods (backward compatible)
display = DisplayManager()
display.display_template(template, template_id)
# Internally, DisplayManager delegates to specialized managers
# display.templates.render_template(template, template_id)
Design Principles:
DisplayManager methods onlyDisplayManager delegates to specialized managers internallyself.parentTemplates 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
Template Version Field:
The metadata.version field in template.yaml should reflect the version of the underlying application or resource:
nginx:1.25.3 → version: 1.25.3)version: 5.23.0)latest or increment template-specific version (e.g., 0.1.0, 0.2.0) only when no specific application version appliesRationale: This helps users identify which version of the application/provider the template is designed for and ensures template versions track upstream changes.
Application Version Variables:
image: nginx:1.25.3)nginx_version variable)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.Traefik Integration:
When using Traefik with Docker Compose, the traefik.docker.network label is CRITICAL for stacks with multiple networks. When containers are connected to multiple networks, Traefik must know which network to use for routing.
Implementation:
archetypes/compose/traefik-v1.j2 (standard mode) and archetypes/compose/swarm-v1.j2 (swarm mode)traefik.docker.network={{ traefik_network }} label must be present in both standard labels: and deploy.labels: sectionsPrecedence (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)Template Variable Overrides:
Template spec variables can:
default value)type, description, or other properties if they remain unchanged from module specExample:
# Module spec defines: service_name (type: str, no default)
# Template spec overrides:
spec:
general:
vars:
service_name:
default: whoami # Only override the default, type already defined in module
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.
toggle: "var_name", that boolean variable is AUTO-CREATED by the systemvarsports section with toggle: "ports_enabled" automatically provides ports_enabled booleanneeds: "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 reusable, standardized template building blocks for creating boilerplates. Archetypes are modular Jinja2 snippets that represent specific configuration sections (e.g., networks, volumes, service labels).
archetypes/
__init__.py # Package initialization
__main__.py # CLI tool (auto-discovers modules)
compose/ # Module-specific archetypes
archetypes.yaml # Configuration: schema version + variable overrides
compose.yaml.j2 # Main composition file (includes all components)
service-*.j2 # Service-level components (networks, ports, volumes, labels, etc.)
networks-*.j2 # Top-level network definitions
volumes-*.j2 # Top-level volume definitions
configs-*.j2 # Config definitions
secrets-*.j2 # Secret definitions
Key Files:
archetypes.yaml: Configures schema version and variable overrides for testingcompose.yaml.j2: Main composition file that includes all archetype components to test complete configurations*.j2 files: Modular components for specific configuration sections# List available archetypes
python3 -m archetypes compose list
# Preview complete composition (all components together)
python3 -m archetypes compose generate compose.yaml
# Preview individual component
python3 -m archetypes compose generate networks-v1
# Test with variable overrides
python3 -m archetypes compose generate compose.yaml \
--var traefik_enabled=true \
--var swarm_enabled=true
python3 -m archetypes compose generatecompose validate to check Jinja2 syntax and semantic correctnessHow it works:
archetypes.yaml{% include %} directivesgenerate command NEVER writes files - always shows preview output