This file provides guidance to AI Agents when working with code in this repository.
This repository contains a sophisticated collection of templates (called boilerplates) for managing infrastructure across multiple technologies including Terraform, Docker, Ansible, Kubernetes, etc. The project also includes a Python CLI application that allows an easy management, creation, and deployment of boilerplates.
The CLI is a Python application built with Typer for the command-line interface and Jinja2 for templating.
cli/ - Python CLI application source code
cli/core/ - Core functionality (app, config, commands, logging)cli/modules/ - Technology-specific modules (terraform, docker, compose, etc.)library/ - Template collections organized by technology
library/terraform/ - OpenTofu/Terraform templates and exampleslibrary/compose/ - Docker Compose configurationslibrary/proxmox/ - Packer templates for Proxmoxlibrary/ansible/ - Ansible playbooks and configurationslibrary/kubernetes/ - Kubernetes deployments# Running commands
python3 -m cli
# Debugging commands
python3 -m cli --log-level DEBUG compose list
The CLI application is built with a modular and extensible architecture.
cli/__main__.py: The main entry point using Typer. It dynamically discovers and imports modules from the cli/modules directory, registering their commands with the main application.
cli/core/registry.py: Provides a ModuleRegistry which acts as a central store for all discovered module classes. This avoids magic and keeps module registration explicit.
cli/core/module.py: Defines the abstract Module base class. Each technology (e.g., compose, terraform) is a subclass of Module. It standardizes the list, search, show, and generate commands and handles their registration.
cli/core/library.py: Implements the LibraryManager and Library classes, which are responsible for finding template files within the library/ directory. It supports a priority system, allowing different template sources to override each other.
cli/core/template.py: Contains the Template class, which is the heart of the engine. It parses a template file, separating the YAML frontmatter (metadata, variable specifications) from the Jinja2 content. It intelligently merges variable definitions from both the module and the template file.
cli/core/variables.py: Defines the data structures for managing variables:
Variable: Represents a single variable, including its type, validation rules, and default value.VariableSection: Groups variables into logical sections for better presentation and conditional logic.VariableCollection: Manages the entire set of sections and variables for a template.cli/core/prompt.py: The PromptHandler provides the interactive CLI experience. It uses the rich library to prompt the user for variable values, organized by the sections defined in the VariableCollection.
cli/core/display.py: The DisplayManager handles all output rendering using rich. It provides consistent and visually appealing displays for lists, search results, variable summaries, and error messages.
Templates are directory-based. Each template is a directory containing all the necessary files and subdirectories for the boilerplate.
Every template directory must contain a main template file named either template.yaml or template.yml. This file serves as the entry point and contains the template's metadata and variable specifications in YAML frontmatter format.
Example template.yaml:
---
kind: compose
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
Jinja2 Templates (.j2): Any file within the template directory that has a .j2 extension will be rendered by the Jinja2 engine. The .j2 extension is removed from the final output file name (e.g., config.json.j2 becomes config.json). These files can use {% include %} and {% import %} statements to share code with other files in the template directory.
Static Files: Any file without a .j2 extension is treated as a static file and will be copied to the output directory as-is, preserving its relative path and filename.
library/compose/my-nginx-template/
├── template.yaml
├── compose.yaml.j2
├── config/
│ └── nginx.conf.j2
└── static/
└── README.md
Sub-templates are specialized templates that use dot notation in their directory names to create related template variations or components. They provide a way to organize templates hierarchically and create focused, reusable configurations.
Directory Naming Convention:
parent.sub-nametraefik.authentik-middleware, traefik.external-serviceVisibility:
list commandlist --all to show all templates including sub-templatesUsage Examples:
# Show only main templates (default behavior)
python3 -m cli compose list
# Show all templates including sub-templates
python3 -m cli compose list --all
# Search for templates by ID
python3 -m cli compose search traefik
# Search for templates including sub-templates
python3 -m cli compose search authentik --all
# Generate a sub-template
python3 -m cli compose generate traefik.authentik-middleware
Common Use Cases:
service.production, service.development)traefik.middleware, traefik.router)app.docker, app.kubernetes)Variables are a cornerstone of the CLI, allowing for dynamic and customizable template generation. They are defined and processed with a clear precedence and logic.
1. Definition and Precedence:
Variables are sourced and merged from multiple locations, with later sources overriding earlier ones:
spec (Lowest Precedence): Each module (e.g., cli/modules/compose.py) can define a base spec dictionary. This provides default variables and sections for all templates of that kind.spec: The spec block within the template.yaml or template.yml file can override or extend the module's spec. This is the single source of truth for defaults within the template.--var) (Highest Precedence): Providing a variable via the command line (--var KEY=VALUE) has the highest priority and will override any default or previously set value.The Variable.origin attribute is updated to reflect this chain (e.g., module -> template -> cli).
2. Required Sections:
spec can be marked as required: true.general section is implicitly required by default.3. Toggle Settings (Conditional Sections):
toggle property to the name of a boolean variable within that same section.toggle: "advanced_enabled"bool. This is validated at load-time by VariableCollection._validate_section_toggle().false), all other variables within that section are skipped, and the section is visually dimmed in the summary table. This provides a clean way to manage optional or advanced configurations.We use a convention to manage TODO items as GitHub issues directly from the codebase. This allows us to track our work and link it back to the specific code that needs attention.
The format for a TODO item is:
TODO[<issue-number>-<slug>] <description>
<issue-number>: The GitHub issue number.<slug>: A short, descriptive slug for the epic or feature.<description>: The description of the TODO item.When you find a TODO item that has not been converted to an issue yet (i.e., it's missing the [<issue-number>-<slug>] part), you can create an issue for it using the gh CLI:
gh issue create --title "<title>" --body "<description>" --assignee "@me" --project "<project-name>" --label "<label>"
After creating the issue, update the TODO line in the AGENTS.md file with the issue number and a descriptive slug.
DEBUG when they should use INFO, and vice versa.list command). This is wasteful when listing many templates.template.yaml or template.yml fileparent.sub-name)config/)external_url, smtp_port)type for new variablesdefault values when possible.j2 extension for all Jinja2 template files{% if %}) for optional features