Skip to content

Commit

Permalink
Generate plugin documentation from their sources
Browse files Browse the repository at this point in the history
This required a small refactoring of how field properties were stored.
Instead of treating them as something owned by CLI options, they have
their use outside of CLI area, and must exist in field metadata to be
consumable in general.

Now we have a template, a script, a section in docs. Fixing the content
would be the next step...
  • Loading branch information
happz committed Dec 5, 2023
1 parent 80b0d2c commit 1073ea4
Show file tree
Hide file tree
Showing 14 changed files with 304 additions and 65 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

/tmp/
docs/code/autodocs/*.rst
docs/plugins/discover.rst
docs/plugins/execute.rst
docs/plugins/finish.rst
docs/plugins/prepare.rst
docs/plugins/provision.rst
docs/plugins/report.rst
docs/plugins/test-checks.rst
docs/_build
docs/spec
docs/stories
Expand Down
26 changes: 22 additions & 4 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .

.PHONY: help generate-stories generate-autodocs clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
.PHONY: help generate-plugins plugins/*.rst generate-stories generate-autodocs clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext

clean:
rm -rf $(BUILDDIR) stories spec docs/autodocs/*.rst
Expand Down Expand Up @@ -52,7 +52,7 @@ REPODIR = ..
TMTDIR = $(REPODIR)/tmt
SCRIPTSDIR = scripts

generate: spec stories generate-lint-checks generate-test-checks generate-stories generate-autodocs ## Refresh all generated documentation sources
generate: spec stories generate-lint-checks generate-plugins generate-stories generate-autodocs ## Refresh all generated documentation sources

spec:
mkdir -p spec
Expand All @@ -63,15 +63,33 @@ stories:
spec/lint.rst: $(SCRIPTSDIR)/generate-lint-checks.py lint-checks.rst.j2 $(TMTDIR)/base.py
$(SCRIPTSDIR)/generate-lint-checks.py lint-checks.rst.j2 $@

spec/test-checks.rst: $(SCRIPTSDIR)/generate-test-checks.py test-checks.rst.j2 $(TMTDIR)/checks/*.py
plugins/discover.rst: $(SCRIPTSDIR)/generate-plugins.py plugins.rst.j2 $(TMTDIR)/steps/discover/*.py
$(SCRIPTSDIR)/generate-plugins.py discover plugins.rst.j2 $@

plugins/execute.rst: $(SCRIPTSDIR)/generate-plugins.py plugins.rst.j2 $(TMTDIR)/steps/execute/*.py
$(SCRIPTSDIR)/generate-plugins.py execute plugins.rst.j2 $@

plugins/finish.rst: $(SCRIPTSDIR)/generate-plugins.py plugins.rst.j2 $(TMTDIR)/steps/finish/*.py
$(SCRIPTSDIR)/generate-plugins.py finish plugins.rst.j2 $@

plugins/prepare.rst: $(SCRIPTSDIR)/generate-plugins.py plugins.rst.j2 $(TMTDIR)/steps/prepare/*.py
$(SCRIPTSDIR)/generate-plugins.py prepare plugins.rst.j2 $@

plugins/provision.rst: $(SCRIPTSDIR)/generate-plugins.py plugins.rst.j2 $(TMTDIR)/steps/provision/*.py
$(SCRIPTSDIR)/generate-plugins.py provision plugins.rst.j2 $@

plugins/report.rst: $(SCRIPTSDIR)/generate-plugins.py plugins.rst.j2 $(TMTDIR)/steps/report/*.py
$(SCRIPTSDIR)/generate-plugins.py report plugins.rst.j2 $@

plugins/test-checks.rst: $(SCRIPTSDIR)/generate-test-checks.py test-checks.rst.j2 $(TMTDIR)/checks/*.py
$(SCRIPTSDIR)/generate-test-checks.py test-checks.rst.j2 $@

generate-lint-checks: spec spec/lint.rst ## Generate documentation sources for lint checks

generate-stories: stories ## Generate documentation sources for stories
$(SCRIPTSDIR)/generate-stories.py story.rst.j2

generate-test-checks: spec spec/test-checks.rst ## Generate documentation sources for test checks
generate-plugins: plugins/discover.rst plugins/execute.rst plugins/finish.rst plugins/prepare.rst plugins/provision.rst plugins/report.rst plugins/test-checks.rst ## Generate documentation sources for plugins

generate-autodocs: ## Generate autodocs from source docstrings
cd ../ && sphinx-apidoc --force --implicit-namespaces --no-toc -o docs/code/autodocs tmt
Expand Down
6 changes: 3 additions & 3 deletions docs/code/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@

In order to get a quick start with the ``tmt`` source code you
might want look through the :ref:`classes` first to learn about
the overall structure of the code. The :ref:`plugins` can help if
you are planning to write a new plugin. To find detailed
the overall structure of the code. The :ref:`plugin_introduction`
can help if you are planning to write a new plugin. To find detailed
information about individual classes, modules and packages inspect
the documentation generated from sources linked below.

.. toctree::
:maxdepth: 2

Class Overview <classes>
Plugin Introduction <plugins>
Plugin Introduction <plugin-introduction>
tmt <autodocs/tmt>

.. toctree::
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.. _plugins:
.. _plugin_introduction:

===========================
Plugin Introduction
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Table of Contents
Overview <overview>
Guide <guide>
Specification <spec>
Plugins <plugins/index>
Examples <examples>
Stories <stories>
Questions <questions>
Expand Down
43 changes: 43 additions & 0 deletions docs/plugins.rst.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
:tocdepth: 0

.. _/plugins/{{ STEP }}:

{{ STEP | capitalize }} Plugins
{{ '=' * (8 + (STEP | length)) }}

{% for PLUGIN_ID in REGISTRY.iter_plugin_ids() %}
{% set method = REGISTRY.get_plugin(PLUGIN_ID) %}
{% set PLUGIN = method.class_ %}

.. _plugins/{{ STEP }}/{{ PLUGIN_ID | strip }}:

{{ PLUGIN_ID }}
{{ '^' * (PLUGIN_ID | length)}}

{% if PLUGIN.__doc__ %}
{{ PLUGIN.__doc__ | dedent | strip }}
{% endif %}

**Configuration**

{% for field in container_fields(PLUGIN._data_class) %}
{% if field.name in ('how', 'name', 'where') and field.internal != true %}
{% set _, option, _, metadata = container_field(PLUGIN._data_class, field.name) %}

{% if metadata.metavar %}
{{ option }}: ``{{ metadata.metavar }}``
{% elif metadata.default is boolean %}
{{ option }}: ``true|false``
{% else %}
{{ option }}:
{% endif %}
{% if metadata.help %}
{{ metadata.help | strip | indent(4, first=true) }}
{% endif %}
{% endif %}
{% endfor %}

{% if not loop.last %}
----
{% endif %}
{% endfor %}
17 changes: 17 additions & 0 deletions docs/plugins/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.. _plugins:

Plugins
=======

Here you will find documentation for plugins shipped with tmt.

.. toctree::
:maxdepth: 2

Discover <discover>
Provision <provision>
Prepare <prepare>
Execute <execute>
Finish <finish>
Report <report>
Test checks <test-checks>
72 changes: 72 additions & 0 deletions docs/scripts/generate-plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python3

import sys
import textwrap

import tmt.log
import tmt.plugins
import tmt.steps.discover
import tmt.steps.execute
import tmt.steps.finish
import tmt.steps.prepare
import tmt.steps.provision
import tmt.steps.report
import tmt.utils
from tmt.utils import Path, render_template_file

HELP = textwrap.dedent("""
Usage: generate-plugins.py <STEP-NAME> <TEMPLATE-PATH> <OUTPUT-PATH>
Generate pages for step plugins sources.
""").strip()


def main() -> None:
if len(sys.argv) != 4:
print(HELP)

sys.exit(1)

step_name = sys.argv[1]
template_filepath = Path(sys.argv[2])
output_filepath = Path(sys.argv[3])

# We will need a logger...
logger = tmt.log.Logger.create()
logger.add_console_handler()

# ... explore available plugins...
tmt.plugins.explore(logger)

if step_name == 'discover':
registry = tmt.steps.discover.DiscoverPlugin._supported_methods

elif step_name == 'execute':
registry = tmt.steps.execute.ExecutePlugin._supported_methods

elif step_name == 'finish':
registry = tmt.steps.finish.FinishPlugin._supported_methods

elif step_name == 'prepare':
registry = tmt.steps.prepare.PreparePlugin._supported_methods

elif step_name == 'provision':
registry = tmt.steps.provision.ProvisionPlugin._supported_methods

elif step_name == 'report':
registry = tmt.steps.report.ReportPlugin._supported_methods

else:
raise tmt.utils.GeneralError(f"Unhandled step name '{step_name}'.")

# ... and render the template.
output_filepath.write_text(render_template_file(
template_filepath,
STEP=step_name,
REGISTRY=registry,
container_fields=tmt.utils.container_fields,
container_field=tmt.utils.container_field))


if __name__ == '__main__':
main()
5 changes: 1 addition & 4 deletions docs/spec.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ Level 1: Tests
Metadata closely related to individual :ref:`/spec/tests` such
as the :ref:`/spec/tests/test` script, directory
:ref:`/spec/tests/path` or maximum :ref:`/spec/tests/duration`
which are stored directly with the test code. See
:ref:`/spec/test-checks` for the list of available test
:ref:`checks</spec/tests/check>`.
which are stored directly with the test code.

Level 2: Plans
:ref:`/spec/plans` are used to group relevant tests and enable
Expand All @@ -56,5 +54,4 @@ Level 3: Stories
spec/stories
spec/context
spec/hardware
spec/test-checks
spec/lint
6 changes: 3 additions & 3 deletions docs/test-checks.rst.j2
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
:tocdepth: 0

.. _/spec/test-checks:
.. _/plugins/test-checks:

Tests Checks
============
Expand All @@ -19,7 +19,7 @@ tmt.
{% for PLUGIN_ID in REGISTRY.iter_plugin_ids() %}
{% set PLUGIN = REGISTRY.get_plugin(PLUGIN_ID) %}

.. _spec/test-checks/{{ PLUGIN_ID | strip }}:
.. _plugins/test-checks/{{ PLUGIN_ID | strip }}:

{{ PLUGIN_ID }}
{{ '^' * (PLUGIN_ID | length)}}
Expand All @@ -37,7 +37,7 @@ tmt.
{% if metadata.metavar %}
{{ option }}: ``{{ metadata.metavar }}``
{% elif metadata.default is boolean %}
{{ option }}: ``true | false``
{{ option }}: ``true|false``
{% else %}
{{ option }}: ...
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion spec/tests/check.fmf
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ description: |
panic detection, core dump collection or collection of system
logs.

See :ref:`/spec/test-checks` for the list of available checks.
See :ref:`/plugins/test-checks` for the list of available checks.

example:
- |
Expand Down
1 change: 1 addition & 0 deletions tmt/checks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class Check(
how: str
enabled: bool = field(
default=True,
is_flag=True,
help='Whether the check is enabled or not.')

@cached_property
Expand Down
12 changes: 8 additions & 4 deletions tmt/steps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,14 @@ class StepData(
# TODO: we can easily add lists of keys for various verbosity levels...
_KEYS_SHOW_ORDER = ['name', 'how']

name: str
how: str
order: int = tmt.utils.DEFAULT_PLUGIN_ORDER
summary: Optional[str] = None
name: str = field(help='The name of the step phase.')
how: str = field()
order: int = field(
default=tmt.utils.DEFAULT_PLUGIN_ORDER,
help='Order in which the phase should be handled.')
summary: Optional[str] = field(
default=None,
help='Concise summary describing purpose of the phase.')

def to_spec(self) -> _RawStepData:
""" Convert to a form suitable for saving in a specification file """
Expand Down
Loading

0 comments on commit 1073ea4

Please sign in to comment.