Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate plugin documentation from their sources #2549

Merged
merged 1 commit into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
28 changes: 24 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 code/autodocs/*.rst
Expand Down Expand Up @@ -53,7 +53,9 @@ TMTDIR = $(REPODIR)/tmt
SCRIPTSDIR = scripts
TEMPLATESDIR = templates

generate: spec stories generate-lint-checks generate-test-checks generate-stories generate-autodocs ## Refresh all generated documentation sources
PLUGINS_TEMPLATE := $(TEMPLATESDIR)/plugins.rst.j2

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

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

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

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

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

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

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

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

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

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

generate-stories: stories $(TEMPLATESDIR)/story.rst.j2 ## Generate documentation sources for stories
$(SCRIPTSDIR)/generate-stories.py $(TEMPLATESDIR)/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
24 changes: 24 additions & 0 deletions docs/plugins/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.. _plugins:

Plugins
=======

Here you will find documentation for plugins shipped with tmt.

.. warning::

Please, be aware that the documentation below is a work in progress. We are
working on fixing it, adding missing bits and generally making it better.
Also, it was originaly used for command line help only, therefore the
formatting is often suboptional.

.. 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
62 changes: 62 additions & 0 deletions docs/templates/plugins.rst.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
: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)}}

{#
TODO: once we start getting reviewed and polished plugins, drop the warning
for those that would be done and ready. Probably with some temporary list
to which we would add their names.
#}
.. warning::

Please, be aware that the documentation below is a work in progress. We are
working on fixing it, adding missing bits and generally making it better.
Also, it was originaly used for command line help only, therefore the
formatting is often suboptional.

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

**Configuration**

{% for field in container_fields(PLUGIN._data_class) %}
{% if (
field.name not in ('how', 'name', 'where', '_OPTIONLESS_FIELDS')
and field.internal != true
and (
not PLUGIN._data_class._OPTIONLESS_FIELDS
or field.name not in PLUGIN._data_class._OPTIONLESS_FIELDS
)
) %}
{% 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 %}
6 changes: 3 additions & 3 deletions docs/templates/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 @@ -251,10 +251,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
Loading