xcad 4 сар өмнө
parent
commit
fe1f079c39
1 өөрчлөгдсөн 72 нэмэгдсэн , 131 устгасан
  1. 72 131
      AGENTS.md

+ 72 - 131
AGENTS.md

@@ -1,12 +1,10 @@
 # AGENTS.md
 
-This file provides guidance to AI Agents when working with code in this repository.
+Guidance for AI Agents working with this repository.
 
 ## Project Overview
 
-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.
+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).
 
 ## Repository Structure
 
@@ -65,30 +63,44 @@ python3 -m cli compose defaults list
 
 ## Common Development Tasks
 
-## Architecture Notes
+## Release Management
 
-### Key Components
+**Process:** Tag-based workflow via `.github/workflows/release.yaml`. Push a semver tag (e.g., `v1.2.3`) to trigger.
 
-The CLI application is built with a modular and extensible architecture.
+**Workflow Steps:**
+1. Extracts version from tag
+2. Auto-updates `pyproject.toml` and `cli/__main__.py` with version
+3. Recreates tag pointing to version bump commit
+4. Builds wheel/tarball
+5. Creates GitHub release (marks alpha/beta/rc as pre-release)
 
-- **`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.
+**Important:** Never manually edit version numbers - they're placeholders (`0.0.0`) that get auto-updated.
 
-- **`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.
+**User Installation:**
+```bash
+# Latest
+curl -fsSL https://raw.githubusercontent.com/christianlempa/boilerplates/main/scripts/install.sh | bash
 
-- **`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.
+# Specific version
+curl -fsSL https://raw.githubusercontent.com/christianlempa/boilerplates/main/scripts/install.sh | bash -s -- --version v1.2.3
+```
 
-- **`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.
+The `install.sh` script downloads the release tarball and installs via pipx. PyPI publishing is currently disabled.
 
-- **`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.
+## Architecture Notes
 
-- **`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.
+### Key Components
 
-- **`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`.
+Modular architecture with dynamic module discovery:
 
-- **`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.
+- **`cli/__main__.py`**: Entry point. Auto-discovers modules and registers commands.
+- **`cli/core/registry.py`**: Central module class store.
+- **`cli/core/module.py`**: Abstract `Module` base class for standardized commands (list, search, show, generate).
+- **`cli/core/library.py`**: `LibraryManager` finds templates in `library/` with priority system.
+- **`cli/core/template.py`**: Parses templates, merges YAML frontmatter with Jinja2 content.
+- **`cli/core/variables.py`**: Variable data structures (`Variable`, `VariableSection`, `VariableCollection`).
+- **`cli/core/prompt.py`**: Interactive CLI prompts via `rich` library.
+- **`cli/core/display.py`**: Consistent output rendering.
 
 ### Template Format
 
@@ -96,9 +108,7 @@ Templates are directory-based. Each template is a directory containing all the n
 
 #### Main Template File
 
-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`:
+Requires `template.yaml` or `template.yml` with metadata and variables in YAML frontmatter:
 
 ```yaml
 ---
@@ -128,16 +138,9 @@ spec:
 
 #### Template Files
 
--   **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.
-
--   **Content Sanitization**: All rendered Jinja2 templates are automatically sanitized to improve output quality:
-    - Multiple consecutive blank lines are reduced to a single blank line
-    - Leading blank lines are removed
-    - Trailing whitespace is stripped from each line
-    - Files are ensured to end with exactly one newline character
-    - This prevents common formatting issues from conditional Jinja2 blocks
+- **Jinja2 Templates (`.j2`)**: Rendered by Jinja2, `.j2` extension removed in output. Support `{% include %}` and `{% import %}`.
+- **Static Files**: Non-`.j2` files copied as-is.
+- **Sanitization**: Auto-sanitized (single blank lines, no leading blanks, trimmed whitespace, single trailing newline).
 
 #### Example Directory Structure
 
@@ -153,50 +156,15 @@ library/compose/my-nginx-template/
 
 #### Variables
 
-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:
-
-1.  **Module `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`.
-2.  **Template `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.
-3.  **CLI Overrides (`--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:**
-
-- A section in the `spec` can be marked as `required: true`.
-- The `general` section is implicitly required by default.
-- During an interactive session, users must provide inputs for all variables within a required section and cannot skip it.
-
-**3. Toggle Settings (Conditional Sections):**
-
-- A section can be made conditional by setting the `toggle` property to the name of a boolean variable within that same section.
-- **Example**: `toggle: "advanced_enabled"`
-- **Validation**: The toggle variable MUST be of type `bool`. This is validated at load-time by `VariableCollection._validate_section_toggle()`.
-- During an interactive session, the CLI will first ask the user to enable or disable the section by prompting for the toggle variable (e.g., "Enable advanced settings?").
-- If the section is disabled (the toggle is `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.
-
-**4. Section Dependencies:**
+**Precedence** (lowest to highest):
+1. Module `spec` (defaults for all templates of that kind)
+2. Template `spec` (overrides module defaults)
+3. CLI `--var` (highest priority)
 
-- Sections can declare dependencies on other sections using the `needs` property.
-- **Example**: `needs: "traefik"` or `needs: ["database", "redis"]` for multiple dependencies.
-- **Purpose**: Ensures that dependent sections are only shown/processed when their dependency sections are enabled.
-- **Behavior**:
-  - During interactive prompting: If a dependency is not satisfied (disabled or not enabled), the dependent section is automatically skipped with a message like `⊘ Section Name (skipped - requires dependency_name to be enabled)`.
-  - During non-interactive generation: Variables from sections with unsatisfied dependencies are excluded from the Jinja2 rendering context.
-  - During validation: Sections with unsatisfied dependencies are skipped.
-- **Validation**: Dependencies are validated at template load time:
-  - Circular dependencies are detected and cause an error (e.g., A needs B, B needs A).
-  - Missing dependencies cause an error (e.g., A needs B, but B doesn't exist).
-  - Self-dependencies cause an error (e.g., A needs A).
-- **Sorting**: Sections are automatically sorted using topological sort to ensure dependencies come before dependents, while preserving the original order within priority groups (required, enabled, disabled).
-- **Use Cases**:
-  - Split complex configurations: e.g., `traefik` (basic) and `traefik_tls` (needs traefik) sections.
-  - Conditional features: e.g., `database_backup` (needs database) or `monitoring_alerts` (needs monitoring).
-  - Hierarchical settings: e.g., `email` (basic) and `email_advanced` (needs email) sections.
+**Key Features:**
+- **Required Sections**: Mark with `required: true` (general is implicit). Users must provide all values.
+- **Toggle Settings**: Conditional sections via `toggle: "bool_var_name"`. If false, section is skipped.
+- **Dependencies**: Use `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:**
 
@@ -224,62 +192,35 @@ spec:
         type: "str"
 ```
 
-## Best Practices for Template Development
+## Best Practices
 
 ### Template Structure
-- Always include a main `template.yaml` or `template.yml` file
-- Use descriptive template IDs (directory names) with lowercase and hyphens
-- Place Jinja2 templates in subdirectories when appropriate (e.g., `config/`)
-- For application-specific configs, create templates in the `config` module instead of using complex directory structures
-
-### Variable Definitions
-- **Prefer module spec variables first**: Always check if a variable exists in the module spec before adding template-specific variables. Use existing module variables where possible to maintain consistency across templates.
-- **Override module variables when needed**: If a module variable needs different behavior for a specific template, override its `description`, `default`, or `extra` properties in the template spec rather than creating a new variable.
-- **Add template-specific variables only when necessary**: Only create new variables in the template spec when they are truly unique to that template and don't fit into existing module variables.
-- Use descriptive names with underscores (e.g., `external_url`, `smtp_port`)
-- Always specify `type` and provide sensible `default` values
-- Mark sensitive data with `sensitive: true` and `autogenerated: true` for auto-generated secrets
-- Use `pwgen` filter for password generation: `{{ secret_key if secret_key else (none | pwgen(50)) }}`
-
-**Example**: For the Traefik template, use the existing `authentik` section from the module spec instead of creating custom `authentik_middleware_*` variables. Override the section's `description` and `extra` to provide Traefik-specific guidance.
-
-### Jinja2 Templates
-- Use `.j2` extension and always use `| default()` filter for safe fallbacks
-- Use conditional blocks for optional features and keep logic simple
-- Add descriptive comments in generated files
-
-### Docker Compose Specific Practices
-
-#### Variable Naming Conventions
-- **Service/Container**: `service_name`, `container_name`, `container_timezone`, `restart_policy`
-- **Application**: Prefix with app name (e.g., `authentik_secret_key`, `gitea_root_url`)
-- **Database**: `database_type`, `database_enabled`, `database_external`, `database_host`, `database_port`, `database_name`, `database_user`, `database_password`
-- **Network**: `network_enabled`, `network_name`, `network_external`
-- **Traefik**: `traefik_enabled`, `traefik_host`, `traefik_tls_enabled`, `traefik_tls_entrypoint`, `traefik_tls_certresolver`
-- **Ports**: `ports_enabled`, `ports_http`, `ports_https`, `ports_ssh`
-- **Email**: `email_enabled`, `email_host`, `email_port`, `email_username`, `email_password`, `email_from`
-
-#### Scoped Environment Files
-Use separate `.env.{service}.j2` files for different services (e.g., `.env.authentik.j2`, `.env.postgres.j2`):
-
-- Benefits: Better security, cleaner organization, easier management, reusable configs
-- Usage: Reference via `env_file` directive in `compose.yaml.j2`
-- Structure: Group related settings with comments (e.g., `# Database Connection`)
-
-#### Common Toggle Patterns
-- `database_enabled`, `email_enabled`, `traefik_enabled`, `ports_enabled`, `network_enabled`
-
-#### Docker Compose Template Patterns
-- Always define `depends_on` for startup ordering and use named volumes for persistence
-- Include health checks for database services
-- Use `{% if not database_external %}` for conditional service creation
-- Group Traefik labels logically with proper service/router configuration
-- Always include `restart: {{ restart_policy | default('unless-stopped') }}`
-- Support both internal and external databases/services with conditionals
-
-### Module Specs
-- Define common sections that apply to all templates of that kind
-- Use toggle variables for optional sections
-- Provide comprehensive descriptions for user guidance
-- Group related variables into logical sections
-- Validate toggle variables are boolean type
+- Include `template.yaml`/`template.yml` with descriptive IDs (lowercase-with-hyphens)
+- Use subdirectories for Jinja2 templates (e.g., `config/`)
+- Prefer `config` module for app-specific configs vs complex directories
+
+### Variables
+- **Priority**: Prefer module spec → override when needed → add new only if unique
+- Use descriptive underscore names, always specify `type`
+- **Defaults**: Define sensible `default` values in `template.yaml` for all non-required variables (improves non-interactive generation)
+- **Credentials**: Mark with `sensitive: true` (hides input), `autogenerated: true` (auto-generates secure values when empty)
+
+### Jinja2
+- Keep logic simple, add descriptive comments
+
+### Docker Compose
+
+**Naming Conventions:**
+- Service: `service_name`, `container_name`, `container_timezone`, `restart_policy`
+- App: Prefix with app name (e.g., `authentik_secret_key`)
+- Database: `database_*` (type, enabled, external, host, port, name, user, password)
+- Network: `network_*` (enabled, name, external)
+- Traefik: `traefik_*` (enabled, host, tls_enabled, tls_entrypoint, tls_certresolver)
+- Ports: `ports_*` (enabled, http, https, ssh)
+- Email: `email_*` (enabled, host, port, username, password, from)
+
+**Patterns:**
+- Use scoped `.env.{service}.j2` files for better security/organization
+- Always: `depends_on`, named volumes, health checks (DB), `restart: {{ restart_policy | default('unless-stopped') }}`
+- Conditionals: `{% if not database_external %}` for service creation
+- Common toggles: `database_enabled`, `email_enabled`, `traefik_enabled`, `ports_enabled`, `network_enabled`