Allow comments in variable files to be rendered in docs
This change will allow all comments in our variable files to be rendered normally within our documentation. This will allow folks reading our documentation to benefit from information we may put in the various files. This patch adds ruamel.yaml as new dependency for building documentation and we won't need to include a README.md file into the documentation role index anymore. Depends-On: https://review.opendev.org/c/openstack/requirements/+/795270 Upstream-rpm-master: https://review.rdoproject.org/r/c/openstack/tripleo-validations-distgit/+/34031 Co-Authored-By: Kevin Carter <kecarter@redhat.com> Signed-off-by: Gael Chamoulaud (Strider) <gchamoul@redhat.com> Change-Id: I88ac702a9eedc53fdc88a2dbb5422b94322b68b4
This commit is contained in:
parent
86bdd14bc1
commit
933d98402f
@ -4,4 +4,5 @@ openstackdocstheme>=2.2.2 # Apache-2.0
|
||||
reno>=3.1.0 # Apache-2.0
|
||||
doc8>=0.8.0 # Apache-2.0
|
||||
bashate>=0.6.0 # Apache-2.0
|
||||
ruamel.yaml>=0.15.5 # MIT
|
||||
six>=1.11.0 # MIT
|
||||
|
@ -14,7 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import importlib.util
|
||||
import imp
|
||||
import os
|
||||
|
||||
from docutils import core
|
||||
@ -23,7 +23,48 @@ from docutils.parsers.rst import Directive
|
||||
from docutils.parsers import rst
|
||||
from docutils.writers.html4css1 import Writer
|
||||
|
||||
from sphinx import addnodes
|
||||
|
||||
import yaml
|
||||
from ruamel.yaml import YAML as RYAML
|
||||
|
||||
try:
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
except ImportError:
|
||||
import StringIO
|
||||
|
||||
|
||||
class DocYaml(RYAML):
|
||||
def _license_filter(self, data):
|
||||
"""This will filter out our boilerplate license heading in return data.
|
||||
|
||||
The filter is used to allow documentation we're creating in variable
|
||||
files to be rendered more beautifully.
|
||||
"""
|
||||
lines = list()
|
||||
mark = True
|
||||
for line in data.splitlines():
|
||||
if '# Copyright' in line:
|
||||
mark = False
|
||||
if mark:
|
||||
lines.append(line)
|
||||
if '# under the License' in line:
|
||||
mark = True
|
||||
return '\n'.join(lines)
|
||||
|
||||
def dump(self, data, stream=None, **kw):
|
||||
if not stream:
|
||||
stream = StringIO()
|
||||
try:
|
||||
RYAML.dump(self, data, stream, **kw)
|
||||
return self._license_filter(stream.getvalue().strip())
|
||||
finally:
|
||||
stream.close()
|
||||
|
||||
|
||||
DOCYAML = DocYaml()
|
||||
DOCYAML.default_flow_style = False
|
||||
|
||||
|
||||
class AnsibleAutoPluginDirective(Directive):
|
||||
@ -93,16 +134,11 @@ class AnsibleAutoPluginDirective(Directive):
|
||||
|
||||
@staticmethod
|
||||
def load_module(filename):
|
||||
module_spec = importlib.util.spec_from_file_location(
|
||||
'__ansible_module__', filename
|
||||
)
|
||||
module = importlib.util.module_from_spec(module_spec)
|
||||
module_spec.loader.exec_module(module)
|
||||
return module
|
||||
return imp.load_source('__ansible_module__', filename)
|
||||
|
||||
@staticmethod
|
||||
def build_documentation(module):
|
||||
docs = yaml.safe_load(module.DOCUMENTATION)
|
||||
docs = DOCYAML.load(module.DOCUMENTATION)
|
||||
doc_data = dict()
|
||||
doc_data['docs'] = docs['description']
|
||||
doc_data['author'] = docs.get('author', list())
|
||||
@ -111,12 +147,10 @@ class AnsibleAutoPluginDirective(Directive):
|
||||
|
||||
@staticmethod
|
||||
def build_examples(module):
|
||||
examples = yaml.safe_load(module.EXAMPLES)
|
||||
examples = DOCYAML.load(module.EXAMPLES)
|
||||
return_examples = list()
|
||||
for example in examples:
|
||||
return_examples.append(
|
||||
yaml.safe_dump([example], default_flow_style=False)
|
||||
)
|
||||
return_examples.append(DOCYAML.dump([example]))
|
||||
return return_examples
|
||||
|
||||
def _raw_html_block(self, data):
|
||||
@ -140,10 +174,7 @@ class AnsibleAutoPluginDirective(Directive):
|
||||
def _literal_block(data, language='yaml', dump_data=True):
|
||||
if dump_data:
|
||||
literal = nodes.literal_block(
|
||||
text=yaml.safe_dump(
|
||||
data,
|
||||
default_flow_style=False
|
||||
)
|
||||
text=DOCYAML.dump(data)
|
||||
)
|
||||
else:
|
||||
literal = nodes.literal_block(text=data)
|
||||
@ -174,92 +205,110 @@ class AnsibleAutoPluginDirective(Directive):
|
||||
|
||||
def _run_role(self, role):
|
||||
section = self._section_block(
|
||||
title='Role Documentation',
|
||||
title="Role Documentation",
|
||||
text='Welcome to the "{}" role documentation.'.format(
|
||||
os.path.basename(role)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
molecule_defaults = None
|
||||
abspath_role = os.path.dirname(os.path.abspath(role))
|
||||
molecule_shared_file = os.path.join(os.path.dirname(abspath_role),
|
||||
".config/molecule/config.yml")
|
||||
molecule_shared_file = os.path.join(
|
||||
os.path.dirname(abspath_role), ".config/molecule/config.yml"
|
||||
)
|
||||
|
||||
if os.path.exists(molecule_shared_file):
|
||||
with open(molecule_shared_file) as msf:
|
||||
molecule_defaults = yaml.safe_load(msf.read())
|
||||
molecule_defaults = DOCYAML.load(msf.read())
|
||||
|
||||
defaults_file = os.path.join(role, 'defaults', 'main.yml')
|
||||
defaults_file = os.path.join(role, "defaults", "main.yml")
|
||||
if os.path.exists(defaults_file):
|
||||
with open(defaults_file) as f:
|
||||
role_defaults = yaml.safe_load(f.read())
|
||||
role_defaults = DOCYAML.load(f.read())
|
||||
section.append(
|
||||
self._yaml_section(
|
||||
to_yaml_data=role_defaults,
|
||||
section_title='Role Defaults',
|
||||
section_text='This section highlights all of the defaults'
|
||||
' and variables set within the "{}"'
|
||||
' role.'.format(
|
||||
os.path.basename(role)
|
||||
)
|
||||
section_title="Role Defaults",
|
||||
section_text="This section highlights all of the defaults"
|
||||
' and variables set within the "{}"'
|
||||
" role.".format(os.path.basename(role)),
|
||||
)
|
||||
)
|
||||
|
||||
vars_path = os.path.join(role, 'vars')
|
||||
vars_path = os.path.join(role, "vars")
|
||||
if os.path.exists(vars_path):
|
||||
for v_file in os.listdir(vars_path):
|
||||
vars_file = os.path.join(vars_path, v_file)
|
||||
with open(vars_file) as f:
|
||||
vars_values = yaml.safe_load(f.read())
|
||||
vars_values = DOCYAML.load(f.read())
|
||||
section.append(
|
||||
self._yaml_section(
|
||||
to_yaml_data=vars_values,
|
||||
section_title='Role Variables: {}'.format(v_file)
|
||||
section_title="Role Variables: {}".format(v_file),
|
||||
)
|
||||
)
|
||||
|
||||
test_list = nodes.field_list()
|
||||
test_section = self._section_block(
|
||||
title='Molecule Scenarios',
|
||||
title="Molecule Scenarios",
|
||||
text='Molecule is being used to test the "{}" role. The'
|
||||
' following section highlights the drivers in service'
|
||||
' and provides an example playbook showing how the role'
|
||||
' is leveraged.'.format(
|
||||
os.path.basename(role)
|
||||
)
|
||||
" following section highlights the drivers in service"
|
||||
" and provides an example playbook showing how the role"
|
||||
" is leveraged.".format(os.path.basename(role)),
|
||||
)
|
||||
molecule_path = os.path.join(role, 'molecule')
|
||||
|
||||
molecule_path = os.path.join(role, "molecule")
|
||||
if os.path.exists(molecule_path):
|
||||
for test in os.listdir(molecule_path):
|
||||
molecule_section = self._section_block(
|
||||
title='Scenario: {}'.format(test)
|
||||
title="Scenario: {}".format(test)
|
||||
)
|
||||
molecule_file = os.path.join(
|
||||
molecule_path,
|
||||
test,
|
||||
'molecule.yml'
|
||||
)
|
||||
with open(molecule_file) as f:
|
||||
molecule_conf = yaml.safe_load(f.read())
|
||||
molecule_file = os.path.join(molecule_path, test, "molecule.yml")
|
||||
if not os.path.exists(molecule_file):
|
||||
continue
|
||||
|
||||
with open(molecule_file) as f:
|
||||
molecule_conf = DOCYAML.load(f.read())
|
||||
|
||||
# if molecule.yml file from the scenarios, we get the
|
||||
# information from the molecule shared configuration file.
|
||||
if not molecule_conf:
|
||||
molecule_conf = molecule_defaults
|
||||
|
||||
molecule_section.append(
|
||||
self._yaml_section(
|
||||
to_yaml_data=molecule_conf,
|
||||
section_title='Example {} configuration'.format(test)
|
||||
)
|
||||
)
|
||||
|
||||
default_playbook = [molecule_path, test, 'converge.yml']
|
||||
|
||||
provisioner_data = None
|
||||
# Now that we use a shared molecule configuration file, the
|
||||
# molecule.yml file in the role scenarios could be empty or
|
||||
# contains only overriding keys.
|
||||
if molecule_conf:
|
||||
provisioner_data = molecule_conf.get('provisioner')
|
||||
else:
|
||||
provisioner_data = molecule_defaults.get('provisioner')
|
||||
driver_data = molecule_conf.get('driver',
|
||||
molecule_defaults.get('driver'))
|
||||
|
||||
if driver_data:
|
||||
molecule_section.append(
|
||||
nodes.field_name(text="Driver: {}".format(driver_data["name"]))
|
||||
)
|
||||
|
||||
options = driver_data.get("options")
|
||||
if options:
|
||||
molecule_section.append(
|
||||
self._yaml_section(
|
||||
to_yaml_data=options, section_title="Molecule Options"
|
||||
)
|
||||
)
|
||||
|
||||
platforms_data = molecule_conf.get('platforms',
|
||||
molecule_defaults.get('platforms'))
|
||||
|
||||
if platforms_data:
|
||||
molecule_section.append(
|
||||
self._yaml_section(
|
||||
to_yaml_data=platforms_data,
|
||||
section_title="Molecule Platform(s)",
|
||||
)
|
||||
)
|
||||
|
||||
default_playbook = [molecule_path, test, "converge.yml"]
|
||||
|
||||
provisioner_data = molecule_conf.get('provisioner',
|
||||
molecule_defaults.get('provisioner'))
|
||||
|
||||
if provisioner_data:
|
||||
inventory = provisioner_data.get('inventory')
|
||||
@ -267,9 +316,10 @@ class AnsibleAutoPluginDirective(Directive):
|
||||
molecule_section.append(
|
||||
self._yaml_section(
|
||||
to_yaml_data=inventory,
|
||||
section_title='Molecule Inventory'
|
||||
section_title="Molecule Inventory",
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
converge = provisioner_data['playbooks']['converge']
|
||||
default_playbook = default_playbook[:-1] + [converge]
|
||||
@ -277,36 +327,35 @@ class AnsibleAutoPluginDirective(Directive):
|
||||
pass
|
||||
|
||||
molecule_playbook_path = os.path.join(*default_playbook)
|
||||
|
||||
with open(molecule_playbook_path) as f:
|
||||
molecule_playbook = yaml.safe_load(f.read())
|
||||
molecule_playbook = DOCYAML.load(f.read())
|
||||
molecule_section.append(
|
||||
self._yaml_section(
|
||||
to_yaml_data=molecule_playbook,
|
||||
section_title='Example {} playbook'.format(test)
|
||||
section_title="Example {} playbook".format(test),
|
||||
)
|
||||
)
|
||||
test_section.append(molecule_section)
|
||||
test_list.append(molecule_section)
|
||||
else:
|
||||
test_section.append(test_list)
|
||||
section.append(test_section)
|
||||
|
||||
self.run_returns.append(section)
|
||||
|
||||
# Document any libraries nested within the role
|
||||
library_path = os.path.join(role, 'library')
|
||||
library_path = os.path.join(role, "library")
|
||||
if os.path.exists(library_path):
|
||||
self.options['documentation'] = True
|
||||
self.options['examples'] = True
|
||||
for lib in os.listdir(library_path):
|
||||
if lib.endswith('.py'):
|
||||
if lib.endswith(".py"):
|
||||
self._run_module(
|
||||
module=self.load_module(
|
||||
filename=os.path.join(
|
||||
library_path,
|
||||
lib
|
||||
)
|
||||
filename=os.path.join(library_path, lib)
|
||||
),
|
||||
module_title='Embedded module: {}'.format(lib),
|
||||
example_title='Examples for embedded module'
|
||||
module_title="Embedded module: {}".format(lib),
|
||||
example_title="Examples for embedded module",
|
||||
)
|
||||
|
||||
def _run_module(self, module, module_title="Module Documentation",
|
||||
|
Loading…
Reference in New Issue
Block a user