From cfd708aba1d9818094191f304b2414b47785b382 Mon Sep 17 00:00:00 2001 From: "Gael Chamoulaud (Strider)" Date: Wed, 23 Jun 2021 11:31:39 +0200 Subject: [PATCH] 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. Co-Authored-By: Kevin Carter Change-Id: I82f68c1d78605e5b48df3d16461e4875d34890d0 Signed-off-by: Gael Chamoulaud (Strider) --- doc/requirements.txt | 1 + doc/source/_exts/ansible-autodoc.py | 176 +++++++++++++++------------- 2 files changed, 98 insertions(+), 79 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 5a46ed7..d4d963a 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -6,4 +6,5 @@ sphinxcontrib-apidoc>=0.2.0 # BSD License sphinxcontrib-svg2pdfconverter>=1.1.1 # BSD License 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 diff --git a/doc/source/_exts/ansible-autodoc.py b/doc/source/_exts/ansible-autodoc.py index 87e8cfb..0ed8f90 100644 --- a/doc/source/_exts/ansible-autodoc.py +++ b/doc/source/_exts/ansible-autodoc.py @@ -26,6 +26,7 @@ from docutils.writers.html4css1 import Writer from sphinx import addnodes import yaml +from ruamel.yaml import YAML as RYAML try: import io @@ -34,17 +35,14 @@ except ImportError: import StringIO -class DocYaml: - """ - Wrapper clas around calls to yaml lib. - """ +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 = [] + lines = list() mark = True for line in data.splitlines(): if '# Copyright' in line: @@ -59,15 +57,14 @@ class DocYaml: if not stream: stream = StringIO() try: - yaml.dump(data=data, stream=stream, Dumper=yaml.Dumper, **kw) + RYAML.dump(self, data, stream, **kw) return self._license_filter(stream.getvalue().strip()) finally: stream.close() - def load(self, data): - return yaml.load(data, Loader=yaml.Loader) DOCYAML = DocYaml() +DOCYAML.default_flow_style = False class AnsibleAutoPluginDirective(Directive): @@ -208,28 +205,37 @@ class AnsibleAutoPluginDirective(Directive): def _run_role(self, role): section = self._section_block( - title='Role Documentation', - text='Welcome to the "{}" role documentation.'.format( + title="Role Documentation", + text="Welcome to the '{}' role documentation.".format( os.path.basename(role) - ) + ), ) - defaults_file = os.path.join(role, 'defaults', 'main.yml') + + 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" + ) + + if os.path.exists(molecule_shared_file): + with open(molecule_shared_file) as msf: + molecule_defaults = DOCYAML.load(msf.read()) + + defaults_file = os.path.join(role, "defaults", "main.yml") if os.path.exists(defaults_file): with open(defaults_file) as f: 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) @@ -238,81 +244,96 @@ class AnsibleAutoPluginDirective(Directive): 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', - 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) - ) + 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)), ) - 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) - ) - molecule_file = os.path.join( - molecule_path, - test, - 'molecule.yml' + title="Scenario: {}".format(test) ) + 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_conf: - driver_data = molecule_conf.get('driver') - if driver_data: + + # 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 + + # 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. + 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( - nodes.field_name( - text='Driver: {}'.format( - driver_data['name'] - ) + self._yaml_section( + to_yaml_data=options, section_title="Molecule Options" ) ) - 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')) - provisioner_data = molecule_conf.get('provisioner') - if provisioner_data: - inventory = provisioner_data.get('inventory') - if inventory: - molecule_section.append( - self._yaml_section( - to_yaml_data=inventory, - section_title='Molecule Inventory' - ) - ) - - molecule_playbook_path = os.path.join( - molecule_path, - test, - 'converge.yml' - ) - if not os.path.exists(molecule_playbook_path): - molecule_playbook_path = os.path.join( - molecule_path, - test, - 'playbook.yml' + 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') + if inventory: + molecule_section.append( + self._yaml_section( + to_yaml_data=inventory, + section_title="Molecule Inventory", + ) + ) + + try: + converge = provisioner_data['playbooks']['converge'] + default_playbook = default_playbook[:-1] + [converge] + except KeyError: + pass + + molecule_playbook_path = os.path.join(*default_playbook) + with open(molecule_playbook_path) as f: 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_list.append(molecule_section) @@ -323,21 +344,18 @@ class AnsibleAutoPluginDirective(Directive): 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",