#!/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 += "
" + " • ".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 \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()