Big-Link-Man/scripts/generate_cli_docs.py

239 lines
8.8 KiB
Python

#!/usr/bin/env python3
"""
Generate comprehensive CLI documentation from Click commands
"""
import sys
from pathlib import Path
# Add project root to Python path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
# Import after path setup
from src.cli.commands import app
import click
def format_option(option):
"""Format a Click option for documentation"""
names = []
if option.opts:
names.extend(option.opts)
if option.secondary_opts:
names.extend(option.secondary_opts)
name_str = ", ".join(f"`{n}`" for n in names)
# Get option type info
type_info = ""
if hasattr(option, 'type') and option.type:
if isinstance(option.type, click.Choice):
choices = ", ".join(f"`{c}`" for c in option.type.choices)
type_info = f"Choice: {choices}"
elif isinstance(option.type, click.Path):
type_info = "Path"
if hasattr(option.type, 'exists') and option.type.exists:
type_info += " (must exist)"
elif hasattr(option.type, '__name__'):
type_info = option.type.__name__
else:
type_info = str(option.type)
# Get default value
default_info = ""
if option.is_flag:
default_info = "Flag (boolean)"
elif option.default is not None and not callable(option.default):
# Filter out Click's Sentinel.UNSET
default_val = str(option.default)
if 'Sentinel' not in default_val and 'UNSET' not in default_val:
default_info = f"Default: `{option.default}`"
# Required indicator
required = option.required
return {
'name': name_str,
'help': option.help or "",
'type': type_info,
'default': default_info,
'required': required
}
def format_command(cmd):
"""Format a Click command for documentation"""
if not isinstance(cmd, click.Command):
return None
doc = {
'name': cmd.name,
'help': cmd.get_short_help_str() or cmd.help or "",
'description': cmd.help or "",
'options': []
}
# Get all options
for param in cmd.params:
if isinstance(param, click.Option):
doc['options'].append(format_option(param))
return doc
def generate_docs():
"""Generate comprehensive CLI documentation"""
commands = []
for name, cmd in app.commands.items():
cmd_doc = format_command(cmd)
if cmd_doc:
commands.append(cmd_doc)
# Sort commands alphabetically
commands.sort(key=lambda x: x['name'])
# Group commands by category
categories = {
'System': ['config', 'health', 'models'],
'User Management': ['add-user', 'delete-user', 'list-users'],
'Site Management': ['provision-site', 'attach-domain', 'list-sites', 'get-site', 'remove-site', 'sync-sites'],
'Project Management': ['ingest-cora', 'ingest-simple', 'list-projects'],
'Content Generation': ['generate-batch'],
'Deployment': ['deploy-batch', 'verify-deployment'],
'Link Export': ['get-links']
}
# Build markdown
md_lines = []
md_lines.append("# CLI Command Reference")
md_lines.append("")
md_lines.append("Comprehensive documentation for all CLI commands.")
md_lines.append("")
md_lines.append("## Table of Contents")
md_lines.append("")
for category in categories.keys():
md_lines.append(f"- [{category}](#{category.lower().replace(' ', '-')})")
md_lines.append("")
md_lines.append("---")
md_lines.append("")
# Generate documentation for each category
for category, command_names in categories.items():
md_lines.append(f"## {category}")
md_lines.append("")
for cmd_doc in commands:
if cmd_doc['name'] in command_names:
md_lines.append(f"### `{cmd_doc['name']}`")
md_lines.append("")
if cmd_doc['description']:
md_lines.append(cmd_doc['description'])
md_lines.append("")
if cmd_doc['options']:
md_lines.append("**Options:**")
md_lines.append("")
for opt in cmd_doc['options']:
parts = [opt['name']]
if opt['required']:
parts.append("**(required)**")
md_lines.append(f"- {' '.join(parts)}")
details = []
if opt['type']:
details.append(f"Type: {opt['type']}")
if opt['default'] and 'Sentinel' not in opt['default']:
details.append(opt['default'])
if opt['help']:
details.append(opt['help'])
if details:
md_lines.append(f" - {' | '.join(details)}")
md_lines.append("")
else:
md_lines.append("No options required.")
md_lines.append("")
md_lines.append("**Example:**")
md_lines.append("")
md_lines.append("```bash")
example_cmd = f"uv run python main.py {cmd_doc['name']}"
# Build example with required options and common optional ones
example_parts = []
for opt in cmd_doc['options']:
opt_name = opt['name'].replace('`', '').split(',')[0].strip()
if opt['required']:
# Add required options with example values
if '--username' in opt_name or '--admin-user' in opt_name:
example_parts.append("--username admin")
elif '--password' in opt_name or '--admin-password' in opt_name:
example_parts.append("--password yourpass")
elif '--file' in opt_name or '-f' in opt_name:
example_parts.append("--file path/to/file.xlsx")
elif '--job-file' in opt_name or '-j' in opt_name:
example_parts.append("--job-file jobs/example.json")
elif '--project-id' in opt_name or '-p' in opt_name:
example_parts.append("--project-id 1")
elif '--batch-id' in opt_name or '-b' in opt_name:
example_parts.append("--batch-id 1")
elif '--domain' in opt_name:
example_parts.append("--domain www.example.com")
elif '--name' in opt_name:
example_parts.append("--name \"My Project\"")
elif '--tier' in opt_name or '-t' in opt_name:
example_parts.append("--tier 1")
elif '--storage-name' in opt_name:
example_parts.append("--storage-name my-storage-zone")
elif '--region' in opt_name:
example_parts.append("--region DE")
else:
example_parts.append(f"{opt_name} <value>")
elif not opt['required'] and '--debug' in opt_name:
# Include common flags in examples
example_parts.append("--debug")
if example_parts:
example_cmd += " " + " ".join(example_parts)
md_lines.append(example_cmd)
md_lines.append("```")
md_lines.append("")
md_lines.append("---")
md_lines.append("")
# Add any commands not in categories
uncategorized = [c for c in commands if not any(c['name'] in names for names in categories.values())]
if uncategorized:
md_lines.append("## Other Commands")
md_lines.append("")
for cmd_doc in uncategorized:
md_lines.append(f"### `{cmd_doc['name']}`")
md_lines.append("")
if cmd_doc['description']:
md_lines.append(cmd_doc['description'])
md_lines.append("")
return "\n".join(md_lines)
if __name__ == "__main__":
docs = generate_docs()
output_file = Path(__file__).parent.parent / "docs" / "CLI_COMMAND_REFERENCE.md"
output_file.parent.mkdir(exist_ok=True)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(docs)
print(f"CLI documentation generated: {output_file}")
print(f"Total commands documented: {len(app.commands)}")