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

Add collection version to plugin pages and collection plugin index #200

Merged
merged 14 commits into from
Nov 30, 2020
3 changes: 2 additions & 1 deletion antsibull/cli/doc_commands/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from ... import app_context
from ...augment_docs import augment_docs
from ...compat import asyncio_run
from ...docs_parsing import AnsibleCollectionMetadata
from ...docs_parsing.fqcn import get_fqcn_parts, is_fqcn
from ...jinja2.environment import doc_environment
from ...logging import log
Expand Down Expand Up @@ -103,7 +104,7 @@ def generate_docs() -> int:
error_tmpl = env.get_template('plugin-error.rst.j2')

asyncio_run(write_rst(
'.'.join([namespace, collection]), plugin, plugin_type,
'.'.join([namespace, collection]), AnsibleCollectionMetadata.empty(), plugin, plugin_type,
plugin_info, errors, plugin_tmpl, error_tmpl, '',
path_override=output_path))
flog.debug('Finished writing plugin docs')
Expand Down
17 changes: 11 additions & 6 deletions antsibull/cli/doc_commands/stable.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,12 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner],
"""

# Get the info from the plugins
plugin_info = asyncio_run(get_ansible_plugin_info(
plugin_info, collection_metadata = asyncio_run(get_ansible_plugin_info(
venv, collection_dir, collection_names=collection_names))
flog.notice('Finished parsing info from plugins')
flog.notice('Finished parsing info from plugins and collections')
# flog.fields(plugin_info=plugin_info).debug('Plugin data')
# flog.fields(
# collection_metadata=collection_metadata).debug('Collection metadata')

"""
# Turn these into some sort of decorator that will choose to dump or load the values
Expand Down Expand Up @@ -311,21 +313,24 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner],
"""

plugin_contents = get_plugin_contents(plugin_info, nonfatal_errors)
collection_info = get_collection_contents(plugin_contents)
collection_to_plugin_info = get_collection_contents(plugin_contents)
flog.debug('Finished getting collection data')

# Only build top-level index if requested
if create_indexes:
asyncio_run(output_collection_index(collection_info, dest_dir))
asyncio_run(output_collection_index(collection_to_plugin_info, dest_dir))
flog.notice('Finished writing collection index')
asyncio_run(output_plugin_indexes(plugin_contents, dest_dir))
flog.notice('Finished writing plugin indexes')

asyncio_run(output_indexes(collection_info, dest_dir, squash_hierarchy=squash_hierarchy))
asyncio_run(output_indexes(collection_to_plugin_info, dest_dir,
collection_metadata=collection_metadata,
squash_hierarchy=squash_hierarchy))
flog.notice('Finished writing indexes')

asyncio_run(output_all_plugin_rst(collection_info, plugin_info,
asyncio_run(output_all_plugin_rst(collection_to_plugin_info, plugin_info,
nonfatal_errors, dest_dir,
collection_metadata=collection_metadata,
squash_hierarchy=squash_hierarchy))
flog.debug('Finished writing plugin docs')

Expand Down
2 changes: 1 addition & 1 deletion antsibull/data/docsite/plugin.rst.j2
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
the same module name.
{% else %}
.. note::
This plugin is part of the `@{collection}@ collection <https://galaxy.ansible.com/@{collection | replace('.', '/', 1)}@>`_.
This plugin is part of the `@{collection}@ collection <https://galaxy.ansible.com/@{collection | replace('.', '/', 1)}@>`_{% if collection_version %} (version @{ collection_version }@){% endif %}.

To install it use: :code:`ansible-galaxy collection install @{collection}@`.

Expand Down
17 changes: 12 additions & 5 deletions antsibull/data/docsite/plugins_by_collection.rst.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,29 @@

.. _plugins_in_@{collection_name}@:

Plugin Index
============
@{collection_name.title()}@
@{ '=' * (collection_name | length) }@

These are the plugins in the @{collection_name}@ collection
{% if collection_version %}
Collection version @{ collection_version }@
{% endif %}

.. toctree::
:maxdepth: 1

Plugin Index
------------

These are the plugins in the @{collection_name}@ collection

{% for category, plugins in plugin_maps.items() | sort %}

{% if category == 'module' %}
Modules
-------
~~~~~~~
{% else %}
@{ category | capitalize }@ Plugins
@{ '-' * ((category | length) + 8) }@
@{ '~' * ((category | length) + 8) }@
{% endif %}

{% for name, desc in plugins.items() | sort %}
Expand Down
16 changes: 16 additions & 0 deletions antsibull/docs_parsing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,19 @@ def _get_environment(collection_dir: t.Optional[str]) -> t.Dict[str, str]:
if env_var in os.environ:
env[env_var] = os.environ[env_var]
return env


class AnsibleCollectionMetadata:
path: str
version: t.Optional[str]

def __init__(self, path: str, version: t.Optional[str]):
self.path = path
self.version = version

def __repr__(self):
return 'AnsibleCollectionMetadata({0}, {1})'.format(repr(self.path), repr(self.version))

@classmethod
def empty(cls, path='.'):
return cls(path=path, version=None)
68 changes: 60 additions & 8 deletions antsibull/docs_parsing/ansible_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import asyncio
import json
import sys
import os
import traceback
import typing as t
from concurrent.futures import ThreadPoolExecutor
Expand All @@ -18,7 +19,7 @@
from ..logging import log
from ..vendored.json_utils import _filter_non_json_lines
from .fqcn import get_fqcn_parts
from . import _get_environment, ParsingError
from . import _get_environment, ParsingError, AnsibleCollectionMetadata

if t.TYPE_CHECKING:
from ..venv import VenvRunner, FakeVenvRunner
Expand Down Expand Up @@ -158,10 +159,56 @@ async def _get_plugin_info(plugin_type: str, ansible_doc: 'sh.Command',
return results


def get_collection_metadata(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
env: t.Dict[str, str],
collection_names: t.Optional[t.List[str]] = None,
) -> t.Dict[str, AnsibleCollectionMetadata]:
collection_metadata = {}

# Obtain ansible.builtin version
if collection_names is None or 'ansible.builtin' in collection_names:
venv_ansible = venv.get_command('ansible')
ansible_version_cmd = venv_ansible('--version', _env=env)
raw_result = ansible_version_cmd.stdout.decode('utf-8', errors='surrogateescape')
path: t.Optional[str] = None
version: t.Optional[str] = None
for line in raw_result.splitlines():
if line.strip().startswith('ansible python module location'):
path = line.split('=', 2)[1].strip()
if line.startswith('ansible '):
version = line[len('ansible '):]
collection_metadata['ansible.builtin'] = AnsibleCollectionMetadata(
path=path, version=version)

# Obtain collection versions
venv_ansible_galaxy = venv.get_command('ansible-galaxy')
ansible_collection_list_cmd = venv_ansible_galaxy('collection', 'list', _env=env)
raw_result = ansible_collection_list_cmd.stdout.decode('utf-8', errors='surrogateescape')
current_base_path = None
for line in raw_result.splitlines():
parts = line.split()
if len(parts) >= 2:
if parts[0] == '#':
current_base_path = parts[1]
else:
collection_name = parts[0]
version = parts[1]
if '.' in collection_name:
if collection_names is None or collection_name in collection_names:
namespace, name = collection_name.split('.', 2)
collection_metadata[collection_name] = AnsibleCollectionMetadata(
path=os.path.join(current_base_path, namespace, name),
version=None if version == '*' else version)

return collection_metadata


async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
collection_dir: t.Optional[str],
collection_names: t.Optional[t.List[str]] = None
) -> t.Dict[str, t.Dict[str, t.Any]]:
) -> t.Tuple[
t.Mapping[str, t.Mapping[str, t.Any]],
t.Mapping[str, t.Any]]:
"""
Retrieve information about all of the Ansible Plugins.

Expand All @@ -171,12 +218,14 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
search path for Ansible.
:arg collection_names: Optional list of collections. If specified, will only collect
information for plugins in these collections.
:returns: A nested directory structure that looks like::
:returns: An tuple. The first component is a nested directory structure that looks like:

plugin_type:
plugin_name: # Includes namespace and collection.
{information from ansible-doc --json. See the ansible-doc documentation for more
info.}
plugin_type:
plugin_name: # Includes namespace and collection.
{information from ansible-doc --json. See the ansible-doc documentation
for more info.}

The second component is a Mapping of collection names to metadata.
"""
flog = mlog.fields(func='get_ansible_plugin_info')
flog.debug('Enter')
Expand Down Expand Up @@ -246,5 +295,8 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
# done so, we want to then fail by raising one of the exceptions.
raise ParsingError('Parsing of plugins failed')

flog.debug('Retrieving collection metadata')
collection_metadata = get_collection_metadata(venv, env, collection_names)

flog.debug('Leave')
return plugin_map
return (plugin_map, collection_metadata)
26 changes: 17 additions & 9 deletions antsibull/docs_parsing/ansible_internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ..logging import log
from ..utils.get_pkg_data import get_antsibull_data
from ..vendored.json_utils import _filter_non_json_lines
from . import _get_environment
from . import _get_environment, AnsibleCollectionMetadata

if t.TYPE_CHECKING:
from ..venv import VenvRunner, FakeVenvRunner
Expand All @@ -23,7 +23,9 @@
async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
collection_dir: t.Optional[str],
collection_names: t.Optional[t.List[str]] = None
) -> t.Dict[str, t.Dict[str, t.Any]]:
) -> t.Tuple[
t.Mapping[str, t.Mapping[str, t.Any]],
t.Mapping[str, t.Any]]:
"""
Retrieve information about all of the Ansible Plugins.

Expand All @@ -33,12 +35,14 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
search path for Ansible.
:arg collection_names: Optional list of collections. If specified, will only collect
information for plugins in these collections.
:returns: A nested directory structure that looks like::
:returns: An tuple. The first component is a nested directory structure that looks like:

plugin_type:
plugin_name: # Includes namespace and collection.
{information from ansible-doc --json. See the ansible-doc documentation for more
info.}
plugin_type:
plugin_name: # Includes namespace and collection.
{information from ansible-doc --json. See the ansible-doc documentation
for more info.}

The second component is a Mapping of collection names to metadata.
"""
flog = mlog.fields(func='get_ansible_plugin_info')
flog.debug('Enter')
Expand Down Expand Up @@ -72,7 +76,11 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
plugin_log.fields(error=plugin_data['error']).error(
'Error while extracting documentation. Will not document this plugin.')

# TODO: use result['collections']
collection_metadata = {}
for collection_name, collection_data in result['collections'].items():
collection_metadata[collection_name] = AnsibleCollectionMetadata(
path=collection_data['path'],
version=collection_data.get('version'))

flog.debug('Leave')
return plugin_map
return (plugin_map, collection_metadata)
17 changes: 11 additions & 6 deletions antsibull/docs_parsing/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
collection_dir: t.Optional[str],
collection_names: t.Optional[t.List[str]] = None
) -> t.Dict[str, t.Dict[str, t.Any]]:
) -> t.Tuple[
t.Mapping[str, t.Mapping[str, t.Any]],
t.Mapping[str, t.Any]]:
"""
Retrieve information about all of the Ansible Plugins.

Expand All @@ -30,12 +32,15 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'],
search path for Ansible.
:arg collection_names: Optional list of collections. If specified, will only collect
information for plugins in these collections.
:returns: A nested directory structure that looks like::
:returns: An tuple. The first component is a nested directory structure that looks like:

plugin_type:
plugin_name: # Includes namespace and collection.
{information from ansible-doc --json. See the ansible-doc documentation
for more info.}

The second component is a Mapping of collection names to metadata.

plugin_type:
plugin_name: # Includes namespace and collection.
{information from ansible-doc --json. See the ansible-doc documentation for more
info.}
"""
lib_ctx = app_context.lib_ctx.get()

Expand Down
Loading