computer-playbook/sphinx/roles_overview.py

99 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import glob
import yaml
from docutils import nodes
from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from sphinx.util import logging
logger = logging.getLogger(__name__)
class RolesOverviewDirective(Directive):
"""
A directive to embed a roles overview as reStructuredText.
It scans the roles directory (i.e. every folder under "roles") for a
"meta/main.yml" file, reads the roles galaxy tags (from galaxy_info.galaxy_tags)
and description (from galaxy_info.description), and outputs a listing grouped
by each tag. Roles without galaxy tags are grouped under "uncategorized".
"""
has_content = False
def run(self):
env = self.state.document.settings.env
srcdir = env.srcdir
roles_dir = os.path.join(srcdir, 'roles')
if not os.path.isdir(roles_dir):
logger.warning(f"Roles directory not found: {roles_dir}")
error_node = self.state.document.reporter.error(
"Roles directory not found.", line=self.lineno)
return [error_node]
# Dictionary mapping tags to role entries.
categories = {}
for role_path in glob.glob(os.path.join(roles_dir, '*')):
if os.path.isdir(role_path):
meta_path = os.path.join(role_path, 'meta', 'main.yml')
if os.path.exists(meta_path):
try:
with open(meta_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
except Exception as e:
logger.warning(f"Error reading YAML file {meta_path}: {e}")
continue
role_name = os.path.basename(role_path)
# Try to get galaxy_tags from galaxy_info. If none, use "uncategorized".
galaxy_info = data.get('galaxy_info', {})
tags = galaxy_info.get('galaxy_tags', [])
if not tags:
tags = ['uncategorized']
role_description = galaxy_info.get('description', '')
role_entry = {
'name': role_name,
'description': role_description,
'link': f'roles/{role_name}/README.md',
'tags': tags,
}
# Add this role to every tag it belongs to.
for tag in tags:
categories.setdefault(tag, []).append(role_entry)
else:
logger.warning(f"meta/main.yml not found for role {role_path}")
logger.info("For role %s, galaxy_info: %s", role_name, galaxy_info)
# Sort categories and roles alphabetically.
sorted_categories = sorted(categories.items(), key=lambda x: x[0].lower())
for tag, roles in sorted_categories:
roles.sort(key=lambda r: r['name'].lower())
# Build reStructuredText content.
lines = []
for tag, roles in sorted_categories:
lines.append(f".. rubric:: {tag}")
lines.append("")
for role in roles:
# Render the role name as a hyperlink in reStructuredText.
lines.append(f"* `{role['name']} <{role['link']}>`_")
# Insert a line break before the description.
if role['description']:
lines.append("")
lines.append(f" {role['description']}")
lines.append("")
lines.append("")
rst_content = "\n".join(lines)
# Use a ViewList for nested_parse.
rst_lines = ViewList()
for line in rst_content.splitlines():
rst_lines.append(line, '<roles-overview>')
container = nodes.container()
self.state.nested_parse(rst_lines, self.content_offset, container)
return [container]
def setup(app):
app.add_directive("roles-overview", RolesOverviewDirective)
return {'version': '0.1', 'parallel_read_safe': True}