mirror of
https://github.com/ChristianLempa/boilerplates.git
synced 2026-08-24 10:04:44 -05:00
wiki updates and file cleanup
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
# https://editorconfig.org/
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.json]
|
||||
indent_size = 2
|
||||
|
||||
[*.{js,jsx,ts,tsx}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
indent_size = unset
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
[{*.{yaml,yml},.yamllint}]
|
||||
indent_size = 2
|
||||
@@ -1,252 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate GitHub Wiki documentation for module variables.
|
||||
|
||||
This script auto-generates variable documentation in GitHub Wiki markdown format
|
||||
for all registered modules, using the latest schema version for each.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add project root to path (script is in .github/scripts, so go up twice)
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
# ruff: noqa: E402
|
||||
# Import all modules to register them
|
||||
import cli.modules.ansible
|
||||
import cli.modules.compose
|
||||
import cli.modules.helm
|
||||
import cli.modules.kubernetes
|
||||
import cli.modules.packer
|
||||
import cli.modules.terraform # noqa: F401
|
||||
from cli.core.registry import registry # Module import after path manipulation
|
||||
|
||||
|
||||
def format_value(value):
|
||||
"""Format value for markdown display."""
|
||||
if value is None or value == "":
|
||||
return "_none_"
|
||||
if isinstance(value, bool):
|
||||
return "✓" if value else "✗"
|
||||
if isinstance(value, list):
|
||||
return ", ".join(f"`{v}`" for v in value)
|
||||
return f"`{value}`"
|
||||
|
||||
|
||||
def generate_module_docs(module_name: str, output_dir: Path): # noqa: PLR0912, PLR0915
|
||||
"""Generate wiki documentation for a single module."""
|
||||
# Get module class from registry
|
||||
module_classes = dict(registry.iter_module_classes())
|
||||
|
||||
if module_name not in module_classes:
|
||||
sys.stderr.write(f"Warning: Module '{module_name}' not found, skipping\n")
|
||||
return False
|
||||
|
||||
module_cls = module_classes[module_name]
|
||||
schema_version = module_cls.schema_version
|
||||
|
||||
# Get the spec for the latest schema version
|
||||
if hasattr(module_cls, "schemas") and schema_version in module_cls.schemas:
|
||||
spec = module_cls.schemas[schema_version]
|
||||
elif hasattr(module_cls, "spec"):
|
||||
spec = module_cls.spec
|
||||
else:
|
||||
sys.stderr.write(f"Warning: No spec found for module '{module_name}', skipping\n")
|
||||
return False
|
||||
|
||||
# Generate markdown content
|
||||
lines = []
|
||||
|
||||
# Header
|
||||
lines.append(f"# {module_name.title()} Variables")
|
||||
lines.append("")
|
||||
lines.append(f"**Module:** `{module_name}` ")
|
||||
lines.append(f"**Schema Version:** `{schema_version}` ")
|
||||
lines.append(f"**Description:** {module_cls.description}")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append(
|
||||
"This page documents all available variables for the "
|
||||
+ f"{module_name} module. Variables are organized into sections "
|
||||
+ "that can be enabled/disabled based on your configuration needs."
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
# Table of contents
|
||||
lines.append("## Table of Contents")
|
||||
lines.append("")
|
||||
for section_key, section_data in spec.items():
|
||||
section_title = section_data.get("title", section_key)
|
||||
anchor = section_title.lower().replace(" ", "-").replace("/", "")
|
||||
lines.append(f"- [{section_title}](#{anchor})")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
|
||||
# Process each section
|
||||
for section_key, section_data in spec.items():
|
||||
section_title = section_data.get("title", section_key)
|
||||
section_desc = section_data.get("description", "")
|
||||
section_toggle = section_data.get("toggle", "")
|
||||
section_needs = section_data.get("needs", "")
|
||||
section_required = section_data.get("required", False)
|
||||
section_vars = section_data.get("vars", {})
|
||||
|
||||
# Section header
|
||||
lines.append(f"## {section_title}")
|
||||
lines.append("")
|
||||
|
||||
# Section metadata
|
||||
metadata = []
|
||||
if section_required:
|
||||
metadata.append("**Required:** Yes")
|
||||
if section_toggle:
|
||||
metadata.append(f"**Toggle Variable:** `{section_toggle}`")
|
||||
if section_needs:
|
||||
if isinstance(section_needs, list):
|
||||
needs_str = ", ".join(f"`{n}`" for n in section_needs)
|
||||
else:
|
||||
needs_str = f"`{section_needs}`"
|
||||
metadata.append(f"**Depends On:** {needs_str}")
|
||||
|
||||
if metadata:
|
||||
lines.append(" \n".join(metadata))
|
||||
lines.append("")
|
||||
|
||||
if section_desc:
|
||||
lines.append(section_desc)
|
||||
lines.append("")
|
||||
|
||||
# Skip sections with no variables
|
||||
if not section_vars:
|
||||
lines.append("_No variables defined in this section._")
|
||||
lines.append("")
|
||||
continue
|
||||
|
||||
# Variables table
|
||||
lines.append("| Variable | Type | Default | Description |")
|
||||
lines.append("|----------|------|---------|-------------|")
|
||||
|
||||
for var_name, var_data in section_vars.items():
|
||||
var_type = var_data.get("type", "str")
|
||||
var_default = format_value(var_data.get("default"))
|
||||
var_description = var_data.get("description", "").replace("\n", " ")
|
||||
|
||||
# Add extra metadata to description
|
||||
extra_parts = []
|
||||
if var_data.get("sensitive"):
|
||||
extra_parts.append("**Sensitive**")
|
||||
if var_data.get("autogenerated"):
|
||||
extra_parts.append("**Auto-generated**")
|
||||
if "options" in var_data:
|
||||
opts = ", ".join(f"`{o}`" for o in var_data["options"])
|
||||
extra_parts.append(f"**Options:** {opts}")
|
||||
if "needs" in var_data:
|
||||
extra_parts.append(f"**Needs:** `{var_data['needs']}`")
|
||||
if "extra" in var_data:
|
||||
extra_parts.append(var_data["extra"])
|
||||
|
||||
if extra_parts:
|
||||
var_description += "<br>" + " • ".join(extra_parts)
|
||||
|
||||
lines.append(f"| `{var_name}` | `{var_type}` | {var_default} | {var_description} |")
|
||||
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
|
||||
# Footer
|
||||
lines.append("## Notes")
|
||||
lines.append("")
|
||||
lines.append("- **Required sections** must be configured")
|
||||
lines.append("- **Toggle variables** enable/disable entire sections")
|
||||
lines.append("- **Dependencies** (`needs`) control when sections/variables are available")
|
||||
lines.append("- **Sensitive variables** are masked during prompts")
|
||||
lines.append("- **Auto-generated variables** are populated automatically if not provided")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append(f"_Last updated: Schema version {schema_version}_")
|
||||
|
||||
# Write to file
|
||||
output_file = output_dir / f"Variables-{module_name.title()}.md"
|
||||
output_file.write_text("\n".join(lines))
|
||||
|
||||
sys.stdout.write(f"Generated: {output_file.name}\n")
|
||||
return True
|
||||
|
||||
|
||||
def generate_variables_index(modules: list[str], output_dir: Path):
|
||||
"""Generate index page for all variable documentation."""
|
||||
lines = []
|
||||
|
||||
lines.append("# Variables Documentation")
|
||||
lines.append("")
|
||||
lines.append("This section contains auto-generated documentation for all " + "available variables in each module.")
|
||||
lines.append("")
|
||||
lines.append("## Available Modules")
|
||||
lines.append("")
|
||||
|
||||
for module_name in sorted(modules):
|
||||
lines.append(f"- [{module_name.title()}](Variables-{module_name.title()})")
|
||||
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append("Each module page includes:")
|
||||
lines.append("")
|
||||
lines.append("- Schema version information")
|
||||
lines.append("- Complete list of sections and variables")
|
||||
lines.append("- Variable types, defaults, and descriptions")
|
||||
lines.append("- Section dependencies and toggle configurations")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append("_This documentation is auto-generated from module schemas._")
|
||||
|
||||
output_file = output_dir / "Variables.md"
|
||||
output_file.write_text("\n".join(lines))
|
||||
|
||||
sys.stdout.write(f"Generated: {output_file.name}\n")
|
||||
|
||||
|
||||
# Minimum required arguments
|
||||
MIN_ARGS = 2
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
if len(sys.argv) < MIN_ARGS:
|
||||
sys.stderr.write("Usage: python3 scripts/generate_wiki_docs.py <output_directory>\n")
|
||||
sys.exit(1)
|
||||
|
||||
output_dir = Path(sys.argv[1])
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
sys.stdout.write(f"Generating wiki documentation in: {output_dir}\n")
|
||||
sys.stdout.write("\n")
|
||||
|
||||
# Get all registered modules
|
||||
module_classes = dict(registry.iter_module_classes())
|
||||
successful_modules = []
|
||||
|
||||
for module_name in sorted(module_classes.keys()):
|
||||
if generate_module_docs(module_name, output_dir):
|
||||
successful_modules.append(module_name)
|
||||
|
||||
sys.stdout.write("\n")
|
||||
|
||||
# Generate index page
|
||||
if successful_modules:
|
||||
generate_variables_index(successful_modules, output_dir)
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.write(f"✓ Successfully generated documentation for {len(successful_modules)} module(s)\n")
|
||||
else:
|
||||
sys.stderr.write("Error: No documentation generated\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
name: Docs - Update Wiki
|
||||
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'cli/core/schema/**/*.json' # JSON schema files
|
||||
- '.wiki/**' # Static wiki pages
|
||||
- '.github/scripts/generate_wiki_docs.py' # Wiki generation script
|
||||
- '.github/workflows/docs-update-wiki.yaml' # This workflow
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-wiki:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Checkout wiki repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: ${{ github.repository }}.wiki
|
||||
path: wiki
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -e .
|
||||
|
||||
- name: Generate variable documentation
|
||||
run: |
|
||||
python3 .github/scripts/generate_wiki_docs.py wiki/
|
||||
|
||||
- name: Sync wiki pages from .wiki directory
|
||||
run: |
|
||||
# Copy all markdown files from .wiki/ to wiki/ (except Variables-*.md which are auto-generated)
|
||||
if [ -d ".wiki" ]; then
|
||||
echo "Syncing wiki pages from .wiki/ directory..."
|
||||
for file in .wiki/*.md; do
|
||||
filename=$(basename "$file")
|
||||
# Skip auto-generated variable documentation files
|
||||
if [[ ! "$filename" =~ ^Variables- ]]; then
|
||||
echo " Copying $filename"
|
||||
cp "$file" "wiki/$filename"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "No .wiki directory found, skipping static wiki pages sync"
|
||||
fi
|
||||
|
||||
- name: Check for changes
|
||||
id: changes
|
||||
working-directory: wiki
|
||||
run: |
|
||||
git add .
|
||||
if git diff --staged --quiet; then
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
echo "No changes detected in wiki documentation"
|
||||
else
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
echo "Changes detected in wiki documentation"
|
||||
fi
|
||||
|
||||
- name: Commit and push changes
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
working-directory: wiki
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git commit -m "Auto-update wiki pages"
|
||||
|
||||
# Pull with rebase to handle any remote changes, then push
|
||||
# GitHub wikis use master as default branch
|
||||
git pull --rebase origin master
|
||||
git push origin master
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
if [ "${{ steps.changes.outputs.has_changes }}" == "true" ]; then
|
||||
echo "Wiki variable documentation updated successfully"
|
||||
echo "View at: https://github.com/${{ github.repository }}/wiki"
|
||||
else
|
||||
echo "No changes to wiki documentation"
|
||||
fi
|
||||
@@ -4,7 +4,7 @@ Save time by setting default values for variables you use frequently. This page
|
||||
|
||||
## What are Default Variables?
|
||||
|
||||
**Default variables** are user-defined values that override module and template defaults. They allow you to:
|
||||
**Default variables** are user-defined values that override template manifest values for a module. They allow you to:
|
||||
- Avoid repetitive typing during template generation
|
||||
- Standardize values across multiple templates
|
||||
- Customize your environment once, use everywhere
|
||||
@@ -13,13 +13,13 @@ Save time by setting default values for variables you use frequently. This page
|
||||
|
||||
Variables are resolved in this order (lowest to highest priority):
|
||||
|
||||
1. Module runtime defaults
|
||||
2. Template defaults from `template.json`
|
||||
3. **User config** (your saved defaults) ← This page
|
||||
4. `--var-file`
|
||||
5. CLI arguments (`--var`)
|
||||
1. Template manifest values from `template.json`
|
||||
2. **User config** (your saved defaults) ← This page
|
||||
3. `--var-file`
|
||||
4. CLI arguments (`--var`)
|
||||
5. Interactive prompt answers
|
||||
|
||||
Your saved defaults override module and template defaults, but they can still be overridden at generation time.
|
||||
Your saved defaults override template manifest values, but they can still be overridden at generation time.
|
||||
|
||||
## Managing Defaults
|
||||
|
||||
@@ -104,7 +104,7 @@ Response:
|
||||
Removed default: container_timezone
|
||||
```
|
||||
|
||||
The variable will now use module/template defaults again.
|
||||
The variable will now use template manifest values again.
|
||||
|
||||
### Clear All Defaults
|
||||
|
||||
@@ -123,11 +123,13 @@ Cleared all defaults for compose
|
||||
|
||||
## Configuration Storage
|
||||
|
||||
Defaults are stored in:
|
||||
Defaults are usually stored in:
|
||||
```
|
||||
~/.config/boilerplates/config.yaml
|
||||
```
|
||||
|
||||
If a local `./config.yaml` exists in your current working directory, the CLI uses that file instead.
|
||||
|
||||
Example content:
|
||||
```yaml
|
||||
libraries:
|
||||
@@ -153,13 +155,15 @@ defaults:
|
||||
You can manually edit the config file:
|
||||
|
||||
```bash
|
||||
# Edit configuration
|
||||
# Edit the global configuration example
|
||||
nano ~/.config/boilerplates/config.yaml
|
||||
|
||||
# Verify defaults
|
||||
boilerplates compose defaults list
|
||||
```
|
||||
|
||||
If you are using a local `./config.yaml`, edit that file instead.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Timezone Configuration
|
||||
@@ -251,6 +255,8 @@ Defaults don't transfer between modules—they're module-specific.
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
The examples below use the global config path. If the CLI is using a local `./config.yaml`, substitute that path instead.
|
||||
|
||||
### Backup Configuration
|
||||
|
||||
Save your configuration:
|
||||
@@ -332,7 +338,7 @@ cat ~/.config/boilerplates/config.yaml
|
||||
If config file is corrupted:
|
||||
|
||||
```bash
|
||||
# Validate YAML syntax
|
||||
# Validate global config YAML syntax
|
||||
python3 -c "import yaml; yaml.safe_load(open('~/.config/boilerplates/config.yaml'))"
|
||||
|
||||
# Or remove and recreate
|
||||
@@ -346,15 +352,16 @@ If wrong values appear:
|
||||
|
||||
```bash
|
||||
# Check precedence
|
||||
# 1. Module spec
|
||||
# 2. Template spec
|
||||
# 3. User defaults ← Check here
|
||||
# 1. Template manifest values
|
||||
# 2. User defaults ← Check here
|
||||
# 3. --var-file
|
||||
# 4. CLI --var
|
||||
# 5. Interactive answers
|
||||
|
||||
# Verify your defaults
|
||||
boilerplates compose defaults list
|
||||
|
||||
# Check template spec
|
||||
# Check the loaded template state
|
||||
boilerplates compose show <template-name>
|
||||
```
|
||||
|
||||
|
||||
@@ -40,14 +40,28 @@ libraries:
|
||||
|
||||
## Local Storage
|
||||
|
||||
Git libraries are stored under:
|
||||
Git libraries are stored under the active config directory's `libraries/` folder.
|
||||
|
||||
Global-config default:
|
||||
|
||||
```text
|
||||
~/.config/boilerplates/libraries/
|
||||
```
|
||||
|
||||
If the CLI is using a local `./config.yaml`, the library checkout location becomes `./libraries/`.
|
||||
|
||||
The configured `directory` is applied inside that checkout. For the official library the directory is `.`.
|
||||
|
||||
## Configuration Location
|
||||
|
||||
By default, Boilerplates uses:
|
||||
|
||||
```text
|
||||
~/.config/boilerplates/config.yaml
|
||||
```
|
||||
|
||||
If a local `./config.yaml` exists in your current working directory, the CLI uses that file instead.
|
||||
|
||||
## Discovery Rules
|
||||
|
||||
Boilerplates discovers templates by module directory, for example:
|
||||
@@ -79,11 +93,21 @@ boilerplates repo update
|
||||
Add a custom Git library:
|
||||
|
||||
```bash
|
||||
boilerplates repo add my-templates https://github.com/user/templates \
|
||||
boilerplates repo add my-templates \
|
||||
--library-type git \
|
||||
--url https://github.com/user/templates \
|
||||
--directory . \
|
||||
--branch main
|
||||
```
|
||||
|
||||
Add a static library:
|
||||
|
||||
```bash
|
||||
boilerplates repo add local \
|
||||
--library-type static \
|
||||
--path ~/my-templates
|
||||
```
|
||||
|
||||
Remove a library:
|
||||
|
||||
```bash
|
||||
@@ -127,12 +151,6 @@ Templates with `metadata.draft: true` are excluded from normal listings and look
|
||||
|
||||
## Config File
|
||||
|
||||
Library configuration lives in:
|
||||
|
||||
```text
|
||||
~/.config/boilerplates/config.yaml
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# Templates
|
||||
|
||||
Templates are the core unit of Boilerplates. A template is a directory with a `template.json` manifest and a `files/` directory containing the files to render.
|
||||
Templates are the core unit of Boilerplates. A supported template is a directory with a `template.json` manifest and a `files/` directory containing every renderable output file.
|
||||
|
||||
## Template Structure
|
||||
|
||||
Every supported template looks like this:
|
||||
## Required Layout
|
||||
|
||||
```text
|
||||
my-template/
|
||||
@@ -16,11 +14,13 @@ my-template/
|
||||
└── app.yaml
|
||||
```
|
||||
|
||||
Only `template.json` is a supported manifest format. Legacy `template.yaml` and `template.yml` manifests are not supported.
|
||||
Rules:
|
||||
- `template.json` is the only supported manifest format
|
||||
- all rendered content must live under `files/`
|
||||
- legacy `template.yaml`, `template.yml`, and top-level `.j2` layouts are incompatible with the current runtime
|
||||
|
||||
## Manifest Shape
|
||||
## Top-Level Manifest Shape
|
||||
|
||||
Top-level fields:
|
||||
- `slug`
|
||||
- `kind`
|
||||
- `metadata`
|
||||
@@ -35,6 +35,8 @@ Example:
|
||||
"metadata": {
|
||||
"name": "My Template",
|
||||
"description": "Short human description",
|
||||
"author": "Your Name",
|
||||
"date": "2026-04-22",
|
||||
"tags": ["infra", "dev"],
|
||||
"icon": {
|
||||
"provider": "mdi",
|
||||
@@ -48,7 +50,7 @@ Example:
|
||||
"source_dep_version": "1.1.0",
|
||||
"source_dep_digest": "sha256:abc123def456",
|
||||
"upstream_ref": "release-2026-04-22",
|
||||
"notes": "Tracks upstream container release used by this template snapshot"
|
||||
"notes": "Tracks the tested upstream dependency snapshot"
|
||||
}
|
||||
},
|
||||
"variables": [
|
||||
@@ -68,24 +70,38 @@ Example:
|
||||
}
|
||||
```
|
||||
|
||||
## `slug` and Template IDs
|
||||
|
||||
`slug` is the canonical template ID exposed by the CLI.
|
||||
|
||||
Behavior:
|
||||
- if `slug` is present, it wins over the directory name
|
||||
- if `slug` ends with `-<kind>`, that suffix is normalized away for CLI use
|
||||
- if `slug` is missing, the directory name is used
|
||||
|
||||
Example:
|
||||
- directory: `portainer/`
|
||||
- `kind`: `compose`
|
||||
- `slug`: `portainer-compose`
|
||||
- CLI ID: `portainer`
|
||||
|
||||
## Metadata
|
||||
|
||||
Common metadata fields:
|
||||
- `name`
|
||||
- `description`
|
||||
- `author`
|
||||
- `date`
|
||||
- `tags`
|
||||
- `icon`
|
||||
- `draft`
|
||||
- `author`
|
||||
- `date`
|
||||
- `guide`
|
||||
- `version`
|
||||
|
||||
### Version Metadata
|
||||
|
||||
`metadata.version` is optional. If present, it must be an object.
|
||||
`metadata.version` is optional, but when present it must be an object.
|
||||
|
||||
Supported version fields:
|
||||
Supported fields:
|
||||
- `name`
|
||||
- `source_dep_name`
|
||||
- `source_dep_version`
|
||||
@@ -93,57 +109,34 @@ Supported version fields:
|
||||
- `upstream_ref`
|
||||
- `notes`
|
||||
|
||||
Important rules:
|
||||
- the whole `version` object may be omitted
|
||||
- any individual `version` field may be omitted
|
||||
- the CLI uses `metadata.version.name` as the visible version label in list/show output
|
||||
Important behavior:
|
||||
- `metadata.version.name` is the user-facing version label shown in list/show output
|
||||
- the rest of the version object is for upstream dependency tracking
|
||||
- the full object may be omitted
|
||||
- individual fields inside the object may also be omitted
|
||||
|
||||
This means these are all valid:
|
||||
## Variable Declarations Are Mandatory
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"name": "My Template"
|
||||
}
|
||||
}
|
||||
```
|
||||
Any variable used in files under `files/` must be declared in `template.json`.
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"name": "My Template",
|
||||
"version": {
|
||||
"name": "v1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
If a file references an undeclared variable, the template fails validation and load/render operations surface a template error.
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"name": "My Template",
|
||||
"version": {
|
||||
"source_dep_name": "ghcr.io/example/my-image",
|
||||
"source_dep_version": "1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Use the [Variables](Core-Concepts-Variables) page for the manifest structure.
|
||||
|
||||
## Files and Rendering
|
||||
|
||||
All files inside `files/` are part of the template output.
|
||||
Everything under `files/` is part of the output tree.
|
||||
|
||||
Rendering rules:
|
||||
- Boilerplates renders every file under `files/`
|
||||
- files without template expressions pass through unchanged
|
||||
- output paths currently match the relative file paths under `files/`
|
||||
- template discovery ignores anything without `template.json`
|
||||
Rendering behavior:
|
||||
- Boilerplates walks every file under `files/`
|
||||
- files are rendered with the custom delimiter set
|
||||
- files without template expressions still pass through the render pipeline
|
||||
- output paths currently mirror the relative paths inside `files/`
|
||||
- rendered output is sanitized to normalize blank lines and trailing whitespace
|
||||
|
||||
## Delimiters
|
||||
|
||||
Templates use custom delimiters, not default Jinja syntax:
|
||||
Templates use custom delimiters rather than default Jinja syntax:
|
||||
|
||||
- variables: `<< value >>`
|
||||
- blocks: `<% if condition %>`
|
||||
@@ -165,17 +158,21 @@ Legacy `{{ }}`, `{% %}`, and `{# #}` delimiters are rejected.
|
||||
|
||||
## Includes and Imports
|
||||
|
||||
Includes and imports are resolved relative to the template's `files/` directory.
|
||||
|
||||
Example:
|
||||
Includes and imports resolve relative to the template's `files/` directory.
|
||||
|
||||
```jinja
|
||||
<% include 'partials/header.yaml' %>
|
||||
```
|
||||
|
||||
## Template Discovery
|
||||
## Discovery Rules
|
||||
|
||||
Templates are discovered from configured libraries. A directory is considered a template only when it contains `template.json`.
|
||||
Templates are discovered from configured libraries.
|
||||
|
||||
A directory is considered a valid template only when:
|
||||
- `template.json` exists
|
||||
- `files/` exists
|
||||
|
||||
In practice, Boilerplates discovers templates by module directory path such as `compose/<template>/` or `terraform/<template>/`.
|
||||
|
||||
Useful commands:
|
||||
|
||||
@@ -186,11 +183,11 @@ boilerplates compose show nginx
|
||||
```
|
||||
|
||||
Draft templates:
|
||||
- set `metadata.draft` to `true` to hide a template from normal discovery
|
||||
- set `metadata.draft` to `true` to hide the template from normal discovery
|
||||
|
||||
## Generation
|
||||
## Generation Workflow
|
||||
|
||||
Typical generation flow:
|
||||
Typical workflow:
|
||||
|
||||
```bash
|
||||
boilerplates compose show nginx
|
||||
@@ -204,6 +201,7 @@ Useful flags:
|
||||
- `--var` for direct CLI overrides
|
||||
- `--no-interactive` for non-interactive generation
|
||||
- `--dry-run` to preview generation without writing files
|
||||
- `--show-files` to print rendered files during dry-run
|
||||
|
||||
## Validation
|
||||
|
||||
@@ -219,11 +217,19 @@ Validate all templates in a module:
|
||||
boilerplates compose validate
|
||||
```
|
||||
|
||||
Validation covers:
|
||||
- manifest structure
|
||||
- variable declaration coverage
|
||||
- delimiter compatibility
|
||||
- renderability
|
||||
- optional semantic validators for module-specific output
|
||||
|
||||
## Best Practices
|
||||
|
||||
- keep manifests in `template.json`
|
||||
- keep all generated content under `files/`
|
||||
- keep every generated file under `files/`
|
||||
- declare every variable that appears in rendered content
|
||||
- use the custom delimiter set consistently
|
||||
- hardcode tested upstream application versions in rendered files unless a variable is truly required
|
||||
- hardcode tested upstream application versions in rendered files unless a variable is actually needed
|
||||
- use `metadata.version.name` for the user-facing label
|
||||
- use the remaining `metadata.version` fields to track upstream dependency context when useful
|
||||
- use the remaining `metadata.version` fields for upstream snapshot context when useful
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Variables
|
||||
|
||||
Variables define the configurable inputs available to a template. In the current runtime they live in `template.json` under `variables`, grouped into sections with `items`.
|
||||
Variables are defined per template in `template.json` under the top-level `variables` array. There is no separate schema reference layer anymore, so the manifest is the source of truth.
|
||||
|
||||
## Variable Layout
|
||||
## Runtime Shape
|
||||
|
||||
Example:
|
||||
Each entry in `variables` is a group. Each group contains `items`.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -24,7 +24,7 @@ Example:
|
||||
"name": "environment",
|
||||
"type": "enum",
|
||||
"title": "Environment",
|
||||
"default": "prod",
|
||||
"value": "stage",
|
||||
"config": {
|
||||
"options": ["prod", "stage", "dev"]
|
||||
}
|
||||
@@ -35,15 +35,16 @@ Example:
|
||||
}
|
||||
```
|
||||
|
||||
Each group requires:
|
||||
Group fields:
|
||||
- `name`
|
||||
- `title`
|
||||
- `description`
|
||||
- `toggle`
|
||||
- `needs`
|
||||
- `items`
|
||||
|
||||
Each item requires:
|
||||
Item fields:
|
||||
- `name`
|
||||
|
||||
Common item fields:
|
||||
- `type`
|
||||
- `title`
|
||||
- `description`
|
||||
@@ -54,11 +55,13 @@ Common item fields:
|
||||
- `extra`
|
||||
- `config`
|
||||
|
||||
`title` is used as the prompt label. If `description` is omitted, the runtime falls back to `title`.
|
||||
Runtime mapping notes:
|
||||
- `title` is used as the prompt label
|
||||
- `description` is used as the display/help text
|
||||
- if `description` is omitted, the runtime falls back to `title`
|
||||
|
||||
## Variable Types
|
||||
## Supported Types
|
||||
|
||||
Supported types:
|
||||
- `str`
|
||||
- `int`
|
||||
- `float`
|
||||
@@ -69,25 +72,37 @@ Supported types:
|
||||
- `hostname`
|
||||
- `secret`
|
||||
|
||||
### Secret Variables
|
||||
Type notes:
|
||||
- `bool` defaults to `false` when omitted
|
||||
- other types default to empty unless a `default` or `value` is provided
|
||||
- `secret` values are masked in prompts and display output
|
||||
|
||||
Use `type: "secret"` for values that should be masked in prompts and output.
|
||||
## `default` vs `value`
|
||||
|
||||
Use `default` for the normal manifest default.
|
||||
|
||||
Use `value` when the template should pin the runtime value directly. If both are present, `value` wins.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "api_token",
|
||||
"type": "secret",
|
||||
"title": "API token"
|
||||
"name": "environment",
|
||||
"type": "enum",
|
||||
"title": "Environment",
|
||||
"default": "prod",
|
||||
"value": "stage",
|
||||
"config": {
|
||||
"options": ["prod", "stage", "dev"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Config Object
|
||||
|
||||
Extra behavior is defined under `config`.
|
||||
Additional behavior is configured through `config`.
|
||||
|
||||
Supported config fields:
|
||||
Supported fields:
|
||||
- `placeholder`
|
||||
- `textarea`
|
||||
- `unit`
|
||||
@@ -143,7 +158,7 @@ Supported config fields:
|
||||
}
|
||||
```
|
||||
|
||||
### Autogenerated Secret Values
|
||||
### Autogenerated Secrets
|
||||
|
||||
`config.autogenerated` is supported only for `secret` variables.
|
||||
|
||||
@@ -180,22 +195,12 @@ Base64 example:
|
||||
}
|
||||
```
|
||||
|
||||
## Default Values and Overrides
|
||||
Rules:
|
||||
- autogenerated secrets cannot define `default`
|
||||
- base64 generation uses `bytes`, not `length`
|
||||
- `autogenerated` on non-secret variables is rejected
|
||||
|
||||
Value precedence is:
|
||||
|
||||
1. module runtime defaults
|
||||
2. template defaults from `template.json`
|
||||
3. saved user defaults in `config.yaml`
|
||||
4. `--var-file`
|
||||
5. `--var`
|
||||
|
||||
Useful notes:
|
||||
- `default` defines the standard value
|
||||
- `value` lets a template pin a value more directly in the manifest
|
||||
- autogenerated secrets cannot also define a default
|
||||
|
||||
## Dependencies
|
||||
## Dependencies with `needs`
|
||||
|
||||
Both groups and items support `needs`.
|
||||
|
||||
@@ -207,14 +212,18 @@ Examples:
|
||||
|
||||
Semantics:
|
||||
- semicolon means AND
|
||||
- comma means OR within the same comparison
|
||||
- `!=` means the current value must not match
|
||||
- comma means OR within a single comparison
|
||||
- `=` means the value must match
|
||||
- `!=` means the value must not match
|
||||
|
||||
## Toggles
|
||||
Behavior notes:
|
||||
- group `needs` controls whether the group is considered available
|
||||
- item `needs` controls whether the item is shown and validated
|
||||
- disabled boolean items are reset to `false` unless they were explicitly set via CLI
|
||||
|
||||
Groups may define `toggle`, but the toggle variable must exist as a boolean item in that same group.
|
||||
## Group Toggles
|
||||
|
||||
Example:
|
||||
Groups may define `toggle`, but the referenced toggle variable must exist as a boolean item in that same group.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -237,14 +246,25 @@ Example:
|
||||
}
|
||||
```
|
||||
|
||||
If a referenced toggle variable is not present, the runtime drops the toggle behavior.
|
||||
If the toggle variable is missing, the runtime ignores the toggle.
|
||||
|
||||
## Override Order
|
||||
|
||||
For a loaded template, values are applied in this order:
|
||||
|
||||
1. `default` / `value` from `template.json`
|
||||
2. saved defaults from the active config file
|
||||
3. `--var-file`
|
||||
4. `--var`
|
||||
5. interactive prompt answers
|
||||
|
||||
## Inspecting Variables
|
||||
|
||||
The fastest way to see the actual variables for a template is still:
|
||||
Use `show` against the template you care about:
|
||||
|
||||
```bash
|
||||
boilerplates compose show nginx
|
||||
boilerplates terraform show cloudflare-dns-record
|
||||
```
|
||||
|
||||
That reflects the template as the runtime sees it, including defaults, dependencies, and optional sections.
|
||||
That view reflects the actual runtime state for that template, including defaults, dependencies, toggle state, and file structure.
|
||||
|
||||
@@ -20,10 +20,6 @@ The current runtime supports:
|
||||
- [Libraries](Core-Concepts-Libraries) - official and custom libraries, discovery, priority, and config
|
||||
- [Defaults](Core-Concepts-Defaults) - saved default values and precedence
|
||||
|
||||
## Variable Discovery
|
||||
|
||||
- [Variables](Variables) - how to inspect the variables a template actually exposes
|
||||
|
||||
## Additional Docs
|
||||
|
||||
- [Repository README](https://github.com/ChristianLempa/boilerplates/blob/main/README.md)
|
||||
|
||||
@@ -283,11 +283,16 @@ Sync the default template library:
|
||||
boilerplates repo update
|
||||
```
|
||||
|
||||
This downloads all available templates to:
|
||||
This syncs the configured Git library checkouts under the active config directory's `libraries/` folder.
|
||||
|
||||
Global-config default:
|
||||
|
||||
```
|
||||
~/.config/boilerplates/libraries/
|
||||
```
|
||||
|
||||
If the CLI is using a local `./config.yaml`, the sync location becomes `./libraries/`.
|
||||
|
||||
### Shell Completion (Optional)
|
||||
|
||||
Enable tab completion for your shell:
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# Variables Reference
|
||||
|
||||
Boilerplates no longer publishes static schema-version variable reference pages.
|
||||
|
||||
The current runtime is template-driven:
|
||||
- modules provide baseline variable behavior
|
||||
- each template can override defaults and add template-specific variables
|
||||
- the exact variable set depends on the template you are inspecting
|
||||
|
||||
## How to Inspect Variables
|
||||
|
||||
Use the CLI against the template itself:
|
||||
|
||||
```bash
|
||||
boilerplates compose show nginx
|
||||
boilerplates terraform show cloudflare-dns-record
|
||||
boilerplates ansible show ubuntu-vm-core
|
||||
```
|
||||
|
||||
This shows:
|
||||
- template metadata
|
||||
- rendered version label from `metadata.version.name` when present
|
||||
- the template file tree
|
||||
- the actual variable groups and items exposed by that template
|
||||
|
||||
## Recommended Workflow
|
||||
|
||||
1. List templates for a module.
|
||||
2. Show the template you want to use.
|
||||
3. Review defaults, dependencies, and optional sections.
|
||||
4. Generate with `--output`, `--var-file`, and `--var` as needed.
|
||||
|
||||
## Why the Old Pages Were Removed
|
||||
|
||||
The older wiki pages were generated from schema-version snapshots and no longer matched the current `template.json` runtime. They were removed to avoid documenting variables that may not exist, may have changed shape, or may be template-specific.
|
||||
@@ -11,6 +11,3 @@
|
||||
- [Variables](Core-Concepts-Variables)
|
||||
- [Libraries](Core-Concepts-Libraries)
|
||||
- [Defaults](Core-Concepts-Defaults)
|
||||
|
||||
### Reference
|
||||
- [Variables](Variables)
|
||||
|
||||
@@ -6,8 +6,7 @@ Create reusable templates and turn them into configurable workloads for homelabs
|
||||
|
||||
Create reusable templates for infrastructure expertise like Docker, Kubernetes, Terraform, Ansible, Python, and more. Use the built-in *Jinja2-like* templating syntax with `<< >>` variables, `<% %>` blocks, and `<# #>` comments to keep configuration modular and conditional. Sync with Git in both directions or manage everything locally. Render templates, configure variables through a guided wizard, and wire up secrets. Copy them to remote servers and environments or any local directory.
|
||||
|
||||
*Don't want to start from scratch?*
|
||||
> Explore 100+ template presets for homelabs and self-hosted infrastructure: https://github.com/ChristianLempa/boilerplates-library
|
||||
✨ Explore 100+ template presets for homelabs and self-hosted infrastructure: https://github.com/ChristianLempa/boilerplates-library
|
||||
|
||||
## Boilerplates CLI
|
||||
|
||||
@@ -15,10 +14,9 @@ The Boilerplates CLI is the main interface for working with template libraries l
|
||||
|
||||
It combines template-defined variables and defaults, guided interactive prompts, CLI variable overrides, and git-backed template libraries into one workflow. In practice, that means you can keep reusable boilerplates in a repository and turn them into concrete, environment-specific configurations with a single command.
|
||||
|
||||
> **WARNING**
|
||||
> Boilerplates `0.2.0` introduced the new template format. Legacy `template.yaml` / `template.yml` manifests and `.j2` template files are no longer supported.
|
||||
>
|
||||
> New templates must use `template.json`, keep renderable content under `files/`, and use the custom *Jinja2*-like delimiters `<< >>`, `<% %>`, and `<# #>` instead of default *Jinja2* syntax.
|
||||
⚠️ Boilerplates `0.2.0` introduced the new template format. Legacy `template.yaml` / `template.yml` manifests and `.j2` template files are no longer supported.
|
||||
|
||||
ℹ️ New templates must use `template.json`, keep renderable content under `files/`, and use the custom *Jinja2*-like delimiters `<< >>`, `<% %>`, and `<# #>` instead of default *Jinja2* syntax.
|
||||
|
||||
### Installation
|
||||
|
||||
|
||||
@@ -203,7 +203,6 @@ class TemplateMetadata:
|
||||
library: str = "unknown"
|
||||
library_type: str = "git"
|
||||
draft: bool = False
|
||||
guide: str = ""
|
||||
icon: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __init__(
|
||||
@@ -226,7 +225,6 @@ class TemplateMetadata:
|
||||
self.library = library_name or "unknown"
|
||||
self.library_type = library_type
|
||||
self.draft = bool(metadata.get("draft", False))
|
||||
self.guide = str(metadata.get("guide", "")).rstrip("\n")
|
||||
self.icon = metadata.get("icon", {}) if isinstance(metadata.get("icon"), dict) else {}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user