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/+/797048
Upstream-wallaby-rdo: https://review.rdoproject.org/r/c/openstack/tripleo-validations-distgit/+/34056
Co-Authored-By: Kevin Carter <kecarter@redhat.com>
Signed-off-by: Gael Chamoulaud (Strider) <gchamoul@redhat.com>
Change-Id: I88ac702a9eedc53fdc88a2dbb5422b94322b68b4
(cherry picked from commit 933d98402f)
This commit is contained in:
committed by
Gael Chamoulaud
parent
e00f174607
commit
45847646c1
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user