Guidance for AI Agents working with this repository.
A Python CLI for managing infrastructure boilerplates, plus archetypes and template-loading logic. Built with Typer (CLI) and Jinja2 (templating).
Important library location note:
library/ directory here is legacy/backward-compatibility content for versions older than 0.2.0.0.2.0+, the active template library lives in the separate GitHub repository boilerplates-library.# Create and activate a local virtual environment first
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -e ".[test]"
# Run the CLI application
python3 -m cli
# Debugging and Testing commands
python3 -m cli --log-level DEBUG compose list
# Run tests
python3 -m pytest
For detailed information about testing boilerplates in a production-like environment before release, see WARP-LOCAL.md (local file, not in git). This document covers:
Should always happen before pushing anything to the repository and before making a release.
ruff for Python code:
ruff check --fix . - Check and auto-fix linting errors (including unused imports)ruff format . - Format code according to style guidelinesruff check --fix .ruff format .python3 -m pytestThe 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 technology-specific functionscli/__main__.py - CLI entry point, auto-discovers modules and registers commandslibrary/ - Legacy in-repo template content kept only for backward compatibility with versions older than 0.2.0
0.2.0+ library is maintained in the separate GitHub repository boilerplates-libraryarchetypes/ - 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, 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 versioningCurrently supported kinds in code:
ansiblecomposehelmkubernetespackerswarmterraformThese are the kinds supported by the registered CLI modules in cli/modules/. When answering "what kinds do we support?", prefer this code-level list over whatever legacy content happens to exist under library/.
Module Structure: Modules can be either single files or packages:
cli/modules/modulename.py (for simple modules)Creating Modules:
Module from cli/core/module.pyregistry.register(YourModule) at module bottomModule Discovery and Registration:
The system automatically discovers and registers modules at startup:
__main__.py imports all Python files in cli/modules/ directoryregistry.register(ModuleClass) at module levelBenefits:
cli/modules/Type-safe - registry validates module interfaces at registration time
compose/
v1.0.json
v1.1.json
v1.2.json
terraform/
v1.0.json
ansible/
v1.0.json
...other modules...
[
{
"key": "section_key",
"title": "Section Title",
"description": "Optional section description",
"toggle": "optional_toggle_variable_name",
"needs": "optional_dependency",
"required": true,
"vars": [
{
"name": "variable_name",
"type": "str",
"description": "Variable description",
"default": "default_value",
"required": true,
"sensitive": false,
"autogenerated": false,
"options": ["option1", "option2"],
"needs": "other_var=value",
"extra": "Additional help text"
}
]
}
]
class MyModule(Module):
name = "mymodule"
# Convert JSON array to OrderedDict format
return self._convert_json_to_dict(json_spec)
Auto-created toggle variables: Sections with toggle automatically create boolean variables
Conditional visibility: Variables use needs constraints to show/hide based on other variable values
Mode-based organization: Related settings grouped by operational mode (e.g., network_mode, volume_mode)
Older Python-based spec_v*.py files have been migrated to JSON. The module __init__.py now:
Existing Modules:
(Work in Progress): terraform, docker, ansible, kubernetes, packer modules
~/.config/boilerplates/libraries/{name}/library/ directory is legacy compatibility data only and should not be treated as the primary source for current template development0.2.0+ template work, use the external GitHub repository boilerplates-libraryLibrary 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.
How templates are loaded and rendered:
template.jsonneeds constraintsneeds constraints are evaluated for visibility.j2) are rendered with final variable valuesKey Architecture Points:
needs constraints (evaluated at prompt/render time){% include %} and {% import %} for compositionlibrary/ tree in this repo is legacy compatibility data, not the canonical 0.2.0+ library sourceRequires template.json with metadata and variables:
{
"slug": "my-service",
"kind": "compose",
"metadata": {
"name": "My Service Template",
"description": "A template for a service.",
"author": "Your Name",
"date": "2024-01-01",
"version": {
"name": "v1.0.0"
}
},
"variables": [
{
"name": "general",
"title": "General",
"items": [
{
"name": "service_name",
"type": "str",
"title": "Service name"
}
]
}
]
}
Template Version Field:
The metadata.version field in template.json is a structured object and may be partial:
namesource_dep_namesource_dep_versionsource_dep_digestupstream_refnotesRationale: This separates the user-facing template version label from the tracked upstream dependency metadata used by the template snapshot.
Application Version Variables:
image: nginx:1.25.3)nginx_version variable)Exception: Only create version variables if there's a strong technical reason (e.g., multi-component version pinning)
{
"slug": "my-template",
"kind": "compose",
"metadata": {
"name": "My Template",
"version": {
"name": "v1.0.0"
}
},
"variables": [
{
"name": "general",
"title": "General",
"items": []
}
]
}
How It Works:
Behavior:
When to Use:
Single-File Module Example:
class SimpleModule(Module):
name = "simple"
description = "Simple module"
spec = VariableCollection.from_dict({...}) # Single spec
# cli/modules/modulename/__init__.py
class ExampleModule(Module):
name = "modulename"
description = "Module description"
# Dynamically load the appropriate spec version
spec_module = importlib.import_module(f".{version_file}", package=__package__)
return spec_module.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.:warning:, :info:, :docker:) which are automatically replaced with Nerd Font icons during display. Add new shortcodes to IconManager.SHORTCODES dict.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/ directory for reference implementations of Traefik integration patternstraefik.docker.network={{ traefik_network }} label must be present in both standard labels: and deploy.labels: sectionsHow Templates Inherit Variables:
When to Define Template Variables:
You only need to define variables in your template's spec section when:
service_name for your specific app)Variable 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)Template Variable Override Rules:
default value)type, description, or other properties if they remain unchanged from module specExample:
# Template spec ONLY needs to override specific defaults:
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:
toggle: "bool_var_name". If false, section is skipped.
toggle: "var_name", that boolean variable is AUTO-CREATED by the systemports section with toggle: "ports_enabled" automatically provides ports_enabled booleanneeds: "section_name" or needs: ["sec1", "sec2"]. Dependent sections only shown when dependencies are enabled.Dependency Resolution Architecture:
Sections and variables support needs constraints to control visibility based on other variables.
Section-Level Dependencies:
needs: "section_name" or needs: ["sec1", "sec2"]Variable-Level Dependencies:
needs: "var_name=value" or needs: "var1=val1;var2=val2" (semicolon-separated)needs: "network_mode=bridge")needs: "network_mode=bridge,macvlan" (comma = OR)Validation:
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> -o <directory> - Generate from template (supports --dry-run, --var, --no-interactive)validate [template_id] - Validate template(s) (Jinja2 + semantic). Omit template_id to validate all templatesdefaults - 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.
# List available archetypes for a module
python3 -m archetypes compose list
# Preview an archetype component
python3 -m archetypes compose generate <archetype-name>
# Test with variable overrides
python3 -m archetypes compose generate <archetype-name> \
--var traefik_enabled=true \
--var swarm_enabled=true
# Validate templates against archetypes
python3 -m archetypes compose validate # All templates
python3 -m archetypes compose validate <template> # Single template
The validate command compares templates against archetypes to measure coverage and identify which archetype patterns are being used.
What it does:
{% if %}, {% elif %}, {% else %}, {% for %} structures{{ }} placeholders (not specific names)__ANY__, __ANYSTR__, __ANYINT__, __ANYBOOL__{# @repeat-start #} / {# @repeat-end #}{# @optional-start #} / {# @optional-end #}grafana_data vs alloy_data)Archetypes support special annotations for flexible pattern matching:
Wildcard Placeholders (match any value):
__ANY__ - Matches anything__ANYSTR__ - Matches any string__ANYINT__ - Matches any integer__ANYBOOL__ - Matches any booleanRepeat Markers (pattern can appear 1+ times):
{# @repeat-start #}
pattern
{# @repeat-end #}
Optional Markers (section may or may not exist):
{# @optional-start #}
pattern
{# @optional-end #}
Example:
volumes:
{# @repeat-start #}
__ANY__:
driver: local
{# @repeat-end #}
Matches any number of volumes with driver: local
Usage:
# Validate all templates in library - shows summary table
python3 -m archetypes compose validate
# Validate specific template - shows detailed archetype breakdown
python3 -m archetypes compose validate whoami
# Validate templates in custom location
python3 -m archetypes compose validate --library /path/to/templates
Output:
Use cases:
list command to see available archetype components for your modulecompose validate to check Jinja2 syntax and semantic correctnessKey Concepts:
archetypes/<module>/ directory with reusable componentsgenerate command NEVER writes files - always shows preview outputHow it works:
{% include %} directives