Browse code

Collections docs generation (#59761)

* Build documentation for Ansible-2.10 (formerly known as ACD).

Builds plugin docs from collections whose source is on galaxy

The new command downloads collections from galaxy, then finds the
plugins inside of them to get the documentation for those plugins.

* Update the python syntax checks
* docs builds can now require python 3.6+.

* Move plugin formatter code out to an external tool, antsibull-docs.
Collection owners want to be able to extract docs for their own
websites as well.
* The jinja2 filters, tests, and other support code have moved to antsibull
* Remove document_plugins as that has now been integrated into antsibull-docs

* Cleanup and bugfix to other build script code:
* The Commands class needed to have its metaclass set for abstractmethod
to work correctly
* Fix lint issues in some command plugins

* Add the docs/docsite/rst/collections to .gitignore as
everything in that directory will be generated so we don't want any of
it saved in the git repository
* gitignore the build dir and remove edit docs link on module pages

* Add docs/rst/collections as a directory to remove on make clean
* Split the collections docs from the main docs

* remove version and edit on github
* remove version banner for just collections
* clarify examples need collection keyword defined

* Remove references to plugin documentation locations that no longer exist.
* Perhaps the pages in plugins/*.rst should be deprecated
altogether and their content moved?
* If not, perhaps we want to rephrase and link into the collection
documentation?
* Or perhaps we want to link to the plugins which are present in
collections/ansible/builtin?

* Remove PYTHONPATH from the build-ansible calls
One of the design goals of the build-ansible.py script was for it to
automatically set its library path to include the checkout of ansible
and the library of code to implement itself. Because it automatically
includes the checkout of ansible, we don't need to set PYTHONPATH in
the Makefile any longer.

* Create a command to only build ansible-base plugin docs
* When building docs for devel, only build the ansible-base docs for
now. This is because antsibull needs support for building a "devel
tree" of docs. This can be changed once that is implemented
* When building docs for the sanity tests, only build the ansible-base
plugin docs for now. Those are the docs which are in this repo so
that seems appropriate for now.

Toshio Kuratomi authored on 2020/07/18 05:07:35
Showing 38 changed files
... ...
@@ -37,6 +37,8 @@ docs/docsite/rst/cli/ansible.rst
37 37
 docs/docsite/rst/dev_guide/collections_galaxy_meta.rst
38 38
 docs/docsite/rst/dev_guide/testing/sanity/index.rst.new
39 39
 docs/docsite/rst/modules/*.rst
40
+docs/docsite/rst/collections/*.rst
41
+docs/docsite/rst/collections/*/*.rst
40 42
 docs/docsite/rst/playbooks_directives.rst
41 43
 docs/docsite/rst/plugins_by_category.rst
42 44
 docs/docsite/rst/plugins/*/*.rst
... ...
@@ -277,7 +277,7 @@ linkcheckdocs:
277 277
 .PHONY: generate_rst
278 278
 generate_rst: lib/ansible/cli/*.py
279 279
 	mkdir -p ./docs/man/man1/ ; \
280
-	PYTHONPATH=./lib $(GENERATE_CLI) --template-file=docs/templates/man.j2 --output-dir=docs/man/man1/ --output-format man lib/ansible/cli/*.py
280
+	$(GENERATE_CLI) --template-file=docs/templates/man.j2 --output-dir=docs/man/man1/ --output-format man lib/ansible/cli/*.py
281 281
 
282 282
 
283 283
 docs: generate_rst
... ...
@@ -1,6 +1,6 @@
1 1
 OS := $(shell uname -s)
2 2
 SITELIB = $(shell python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"):
3
-PLUGIN_FORMATTER=../../hacking/build-ansible.py document-plugins
3
+PLUGIN_FORMATTER=../../hacking/build-ansible.py docs-build
4 4
 TESTING_FORMATTER=../bin/testing_formatter.sh
5 5
 KEYWORD_DUMPER=../../hacking/build-ansible.py document-keywords
6 6
 CONFIG_DUMPER=../../hacking/build-ansible.py document-config
... ...
@@ -12,23 +12,35 @@ else
12 12
 CPUS ?= $(shell nproc)
13 13
 endif
14 14
 
15
-# Sets the build output directory if it's not already specified
15
+# Sets the build output directory for the main docsite if it's not already specified
16 16
 ifndef BUILDDIR
17 17
 	BUILDDIR = _build
18 18
 endif
19 19
 
20
-MODULE_ARGS=
20
+# Backwards compat for separate VARS
21
+PLUGIN_ARGS=
21 22
 ifdef MODULES
22
-	MODULE_ARGS = -l $(MODULES)
23
+ifndef PLUGINS
24
+	PLUGIN_ARGS = -l $(MODULES)
25
+else
26
+	PLUGIN_ARGS = -l $(MODULES),$(PLUGINS)
23 27
 endif
24
-
25
-PLUGIN_ARGS=
28
+else
26 29
 ifdef PLUGINS
27 30
 	PLUGIN_ARGS = -l $(PLUGINS)
28 31
 endif
32
+endif
33
+
29 34
 
30 35
 DOC_PLUGINS ?= become cache callback cliconf connection httpapi inventory lookup netconf shell strategy vars
31 36
 
37
+PYTHON=python
38
+# fetch version from project release.py as single source-of-truth
39
+VERSION := $(shell $(PYTHON) ../../packaging/release/versionhelper/version_helper.py --raw || echo error)
40
+ifeq ($(findstring error,$(VERSION)), error)
41
+$(error "version_helper failed")
42
+endif
43
+
32 44
 assertrst:
33 45
 ifndef rst
34 46
 	$(error specify document or pattern with rst=somefile.rst)
... ...
@@ -38,17 +50,24 @@ all: docs
38 38
 
39 39
 docs: htmldocs
40 40
 
41
-generate_rst: collections_meta config cli keywords modules plugins testing
41
+generate_rst: collections_meta config cli keywords plugins testing
42
+base_generate_rst: collections_meta config cli keywords base_plugins testing
42 43
 
43 44
 htmldocs: generate_rst
44 45
 	CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx html
45 46
 
47
+base_htmldocs: base_generate_rst
48
+	CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx html
49
+
46 50
 singlehtmldocs: generate_rst
47 51
 	CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx singlehtml
48 52
 
53
+base_singlehtmldocs: base_generate_rst
54
+	CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx singlehtml
55
+
49 56
 linkcheckdocs: generate_rst
50 57
 		CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx linkcheck
51
-		
58
+
52 59
 webdocs: docs
53 60
 
54 61
 #TODO: leaving htmlout removal for those having older versions, should eventually be removed also
... ...
@@ -58,7 +77,7 @@ clean:
58 58
 	-rm -rf $(BUILDDIR)/html
59 59
 	-rm -rf htmlout
60 60
 	-rm -rf module_docs
61
-	-rm -rf _build
61
+	-rm -rf $(BUILDDIR)
62 62
 	-rm -f .buildinfo
63 63
 	-rm -f objects.inv
64 64
 	-rm -rf *.doctrees
... ...
@@ -70,43 +89,44 @@ clean:
70 70
 	find . -type f \( -name "*~" -or -name "#*" \) -delete
71 71
 	find . -type f \( -name "*.swp" \) -delete
72 72
 	@echo "Cleaning up generated rst"
73
-	rm -f rst/modules/*_by_category.rst
74
-	rm -f rst/modules/list_of_*.rst
75
-	rm -f rst/modules/*_maintained.rst
76
-	rm -f rst/modules/*_module.rst
77
-	rm -f rst/modules/*_plugin.rst
78 73
 	rm -f rst/playbooks_directives.rst
79
-	rm -f rst/plugins/*/*.rst
80 74
 	rm -f rst/reference_appendices/config.rst
81 75
 	rm -f rst/reference_appendices/playbooks_keywords.rst
82 76
 	rm -f rst/dev_guide/collections_galaxy_meta.rst
83 77
 	rm -f rst/cli/*.rst
78
+	rm -rf rst/collections/*
79
+	@echo "Cleaning up legacy generated rst locations"
80
+	rm -rf rst/modules
81
+	rm -f rst/plugins/*/*.rst
84 82
 
85 83
 .PHONY: docs clean
86 84
 
87 85
 collections_meta: ../templates/collections_galaxy_meta.rst.j2
88
-	PYTHONPATH=../../lib $(COLLECTION_DUMPER) --template-file=../templates/collections_galaxy_meta.rst.j2 --output-dir=rst/dev_guide/ ../../lib/ansible/galaxy/data/collections_galaxy_meta.yml
86
+	$(COLLECTION_DUMPER) --template-file=../templates/collections_galaxy_meta.rst.j2 --output-dir=rst/dev_guide/ ../../lib/ansible/galaxy/data/collections_galaxy_meta.yml
89 87
 
90 88
 # TODO: make generate_man output dir cli option
91 89
 cli:
92 90
 	mkdir -p rst/cli
93
-	PYTHONPATH=../../lib $(GENERATE_CLI) --template-file=../templates/cli_rst.j2 --output-dir=rst/cli/ --output-format rst ../../lib/ansible/cli/*.py
91
+	$(GENERATE_CLI) --template-file=../templates/cli_rst.j2 --output-dir=rst/cli/ --output-format rst ../../lib/ansible/cli/*.py
94 92
 
95 93
 keywords: ../templates/playbooks_keywords.rst.j2
96
-	PYTHONPATH=../../lib $(KEYWORD_DUMPER) --template-dir=../templates --output-dir=rst/reference_appendices/ ./keyword_desc.yml
94
+	$(KEYWORD_DUMPER) --template-dir=../templates --output-dir=rst/reference_appendices/ ./keyword_desc.yml
97 95
 
98 96
 config: ../templates/config.rst.j2
99
-	PYTHONPATH=../../lib $(CONFIG_DUMPER) --template-file=../templates/config.rst.j2 --output-dir=rst/reference_appendices/ ../../lib/ansible/config/base.yml
100
-
101
-modules: ../templates/plugin.rst.j2
102
-	PYTHONPATH=../../lib $(PLUGIN_FORMATTER) -t rst --template-dir=../templates --module-dir=../../lib/ansible/modules -o rst/modules/ $(MODULE_ARGS)
103
-
104
-plugins: ../templates/plugin.rst.j2
105
-	@echo "looping over doc plugins"
106
-	for plugin in $(DOC_PLUGINS); \
107
-	do \
108
-		PYTHONPATH=../../lib $(PLUGIN_FORMATTER) -t rst --plugin-type $$plugin --template-dir=../templates --module-dir=../../lib/ansible/plugins/$$plugin -o rst $(PLUGIN_ARGS); \
109
-	done
97
+	$(CONFIG_DUMPER) --template-file=../templates/config.rst.j2 --output-dir=rst/reference_appendices/ ../../lib/ansible/config/base.yml
98
+
99
+# For now, if we're building on devel, just build base docs.  In the future we'll want to build docs that
100
+# are the latest versions on galaxy (using a different antsibull-docs subcommand)
101
+plugins:
102
+	if expr "$(VERSION)" : '.*[.]dev[0-9]\+$$' &> /dev/null; then \
103
+		$(PLUGIN_FORMATTER) base -o rst $(PLUGIN_ARGS);\
104
+	else \
105
+		$(PLUGIN_FORMATTER) full -o rst $(PLUGIN_ARGS);\
106
+	fi
107
+
108
+# This only builds the plugin docs included with ansible-base
109
+base_plugins:
110
+	$(PLUGIN_FORMATTER) base -o rst $(PLUGIN_ARGS);\
110 111
 
111 112
 testing:
112 113
 	$(TESTING_FORMATTER)
... ...
@@ -10,26 +10,27 @@
10 10
       element.appendChild(para);
11 11
       document.write('</div>');
12 12
     }
13
-
14
-    // Create a banner if we're not the latest version
15
-    current_url = window.location.href;
16
-    if ((current_url.search("latest") > -1) || (current_url.search("/{{ latest_version }}/") > -1)) {
17
-     // no banner for latest release
18
-    } else if (current_url.search("devel") > -1) {
19
-      document.write('<div id="banner_id" class="admonition caution">');
20
-      para = document.createElement('p');
21
-      banner_text=document.createTextNode("You are reading the *devel* version of the Ansible documentation - most module documentation is currently missing as the modules have moved to collections. Until docs catches up to this change, use the version selection to the left if you want module documentation or the latest stable release version. The *devel* version is not guaranteed stable.");
22
-      para.appendChild(banner_text);
23
-      element = document.getElementById('banner_id');
24
-      element.appendChild(para);
25
-      document.write('</div>');
26
-    } else {
27
-      document.write('<div id="banner_id" class="admonition caution">');
28
-      para = document.createElement('p');
29
-      banner_text=document.createTextNode("You are reading an older version of the Ansible documentation. Use the version selection to the left if you want the latest stable released version.");
30
-      para.appendChild(banner_text);
31
-      element = document.getElementById('banner_id');
32
-      element.appendChild(para);
33
-      document.write('</div>');
34
-    }
13
+    {% if (not READTHEDOCS) and (available_versions is defined) %}
14
+      // Create a banner if we're not the latest version
15
+      current_url = window.location.href;
16
+      if ((current_url.search("latest") > -1) || (current_url.search("/{{ latest_version }}/") > -1)) {
17
+       // no banner for latest release
18
+      } else if (current_url.search("devel") > -1) {
19
+        document.write('<div id="banner_id" class="admonition caution">');
20
+        para = document.createElement('p');
21
+        banner_text=document.createTextNode("You are reading the *devel* version of the Ansible documentation - this version is not guaranteed stable. Use the version selection to the left if you want the latest stable released version.");
22
+        para.appendChild(banner_text);
23
+        element = document.getElementById('banner_id');
24
+        element.appendChild(para);
25
+        document.write('</div>');
26
+      } else {
27
+        document.write('<div id="banner_id" class="admonition caution">');
28
+        para = document.createElement('p');
29
+        banner_text=document.createTextNode("You are reading an older version of the Ansible documentation. Use the version selection to the left if you want the latest stable released version.");
30
+        para.appendChild(banner_text);
31
+        element = document.getElementById('banner_id');
32
+        element.appendChild(para);
33
+        document.write('</div>');
34
+      }
35
+    {% endif %}
35 36
   </script>
... ...
@@ -1,7 +1,7 @@
1 1
 <!--- Based on https://github.com/rtfd/sphinx_rtd_theme/pull/438/files -->
2 2
 {# Creates dropdown version selection in the top-left navigation. #}
3 3
 <div class="version">
4
-  {% if not READTHEDOCS %}
4
+  {% if (not READTHEDOCS) and (available_versions is defined) %}
5 5
     <div class="version-dropdown">
6 6
       <select class="version-list" id="version-list" onchange="javascript:location.href = this.value;">
7 7
         <script> x = document.getElementById("version-list"); </script>
8 8
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+# We also need an example of modules hosted in Automation Hub
1
+# We'll likely move to data hosted in botmeta instead of a standalone file but
2
+# we'll need all of these same details.
3
+module:
4
+    purefa_user:
5
+        source: 'https://galaxy.ansible.com/'
6
+        fqcn: 'purestorage.flasharray'
7
+    purefa_vg:
8
+        source: 'https://galaxy.ansible.com/'
9
+        fqcn: 'purestorage.flasharray'
10
+    gcp_compute_firewall_info:
11
+        source: 'https://galaxy.ansible.com/'
12
+        fqcn: 'google.cloud'
13
+module_utils:
14
+    purefa:
15
+        source: 'https://galaxy.ansible.com/'
16
+        fqcn: 'purestorage.flasharray'
... ...
@@ -6,3 +6,4 @@ sphinx==2.1.2
6 6
 sphinx-notfound-page
7 7
 Pygments >= 2.4.0
8 8
 straight.plugin # Needed for hacking/build-ansible.py which is the backend build script
9
+antsibull >= 0.15.0
... ...
@@ -280,6 +280,10 @@ autoclass_content = 'both'
280 280
 intersphinx_mapping = {'python': ('https://docs.python.org/2/', (None, '../python2.inv')),
281 281
                        'python3': ('https://docs.python.org/3/', (None, '../python3.inv')),
282 282
                        'jinja2': ('http://jinja.palletsprojects.com/', (None, '../jinja2.inv')),
283
+                       'collections': ('https://docs.ansible.com/collections/',
284
+                                       (None, '../collections.inv',
285
+                                        'http://docs.testing.ansible.com/collections/objects.inv',
286
+                                        '../_collections_build/html/objects.inv')),
283 287
                        'ansible_2_9': ('https://docs.ansible.com/ansible/2.9/', (None, '../ansible_2_9.inv')),
284 288
                        'ansible_2_8': ('https://docs.ansible.com/ansible/2.8/', (None, '../ansible_2_8.inv')),
285 289
                        'ansible_2_7': ('https://docs.ansible.com/ansible/2.7/', (None, '../ansible_2_7.inv')),
... ...
@@ -74,7 +74,7 @@ Ansible releases a new major release of Ansible approximately three to four time
74 74
    :maxdepth: 1
75 75
    :caption: Reference & Appendices
76 76
 
77
-   ../modules/modules_by_category
77
+   collections/index
78 78
    reference_appendices/playbooks_keywords
79 79
    reference_appendices/common_return_values
80 80
    reference_appendices/config
... ...
@@ -10,7 +10,7 @@ Ansible Network modules extend the benefits of simple, powerful, agentless autom
10 10
 
11 11
 If you're new to Ansible, or new to using Ansible for network management, start with :ref:`network_getting_started`. If you are already familiar with network automation with Ansible, see :ref:`network_advanced`.
12 12
 
13
-For documentation on using a particular network module, consult the :ref:`list of all network modules<network_modules>`. Some network modules are maintained by the Ansible community - here's a list of :ref:`network modules maintained by the Ansible Network Team<network_supported>`.
13
+For documentation on using a particular network module, consult the :ref:`list of all network modules<network_modules>`. Network modules for various hardware are supported by different teams including the hardware vendors themselves, volunteers from the Ansible community, and the Ansible Network Team.
14 14
 
15 15
 .. toctree::
16 16
    :maxdepth: 3
... ...
@@ -47,11 +47,6 @@ Plugin List
47 47
 You can use ``ansible-doc -t become -l`` to see the list of available plugins.
48 48
 Use ``ansible-doc -t become <plugin name>`` to see specific documentation and examples.
49 49
 
50
-.. toctree:: :maxdepth: 1
51
-    :glob:
52
-
53
-    become/*
54
-
55 50
 .. seealso::
56 51
 
57 52
    :ref:`about_playbooks`
... ...
@@ -118,11 +118,6 @@ Plugin List
118 118
 You can use ``ansible-doc -t cache -l`` to see the list of available plugins.
119 119
 Use ``ansible-doc -t cache <plugin name>`` to see specific documentation and examples.
120 120
 
121
-.. toctree:: :maxdepth: 1
122
-    :glob:
123
-
124
-    cache/*
125
-
126 121
 .. seealso::
127 122
 
128 123
    :ref:`action_plugins`
... ...
@@ -79,12 +79,6 @@ Plugin list
79 79
 You can use ``ansible-doc -t callback -l`` to see the list of available plugins.
80 80
 Use ``ansible-doc -t callback <plugin name>`` to see specific documents and examples.
81 81
 
82
-.. toctree:: :maxdepth: 1
83
-    :glob:
84
-
85
-    callback/*
86
-
87
-
88 82
 .. seealso::
89 83
 
90 84
    :ref:`action_plugins`
... ...
@@ -58,12 +58,6 @@ You can use ``ansible-doc -t connection -l`` to see the list of available plugin
58 58
 Use ``ansible-doc -t connection <plugin name>`` to see detailed documentation and examples.
59 59
 
60 60
 
61
-.. toctree:: :maxdepth: 1
62
-    :glob:
63
-
64
-    connection/*
65
-
66
-
67 61
 .. seealso::
68 62
 
69 63
    :ref:`Working with Playbooks<working_with_playbooks>`
... ...
@@ -162,11 +162,6 @@ Plugin List
162 162
 You can use ``ansible-doc -t inventory -l`` to see the list of available plugins.
163 163
 Use ``ansible-doc -t inventory <plugin name>`` to see plugin-specific documentation and examples.
164 164
 
165
-.. toctree:: :maxdepth: 1
166
-    :glob:
167
-
168
-    inventory/*
169
-
170 165
 .. seealso::
171 166
 
172 167
    :ref:`about_playbooks`
... ...
@@ -138,11 +138,6 @@ Plugin list
138 138
 You can use ``ansible-doc -t lookup -l`` to see the list of available plugins. Use ``ansible-doc -t lookup <plugin name>`` to see specific documents and examples.
139 139
 
140 140
 
141
-.. toctree:: :maxdepth: 1
142
-    :glob:
143
-
144
-    lookup/*
145
-
146 141
 .. seealso::
147 142
 
148 143
    :ref:`about_playbooks`
... ...
@@ -33,11 +33,6 @@ In this case, you will also want to update the :ref:`ansible_shell_executable <a
33 33
 You can further control the settings for each plugin via other configuration options
34 34
 detailed in the plugin themselves (linked below).
35 35
 
36
-.. toctree:: :maxdepth: 1
37
-    :glob:
38
-
39
-    shell/*
40
-
41 36
 .. seealso::
42 37
 
43 38
    :ref:`about_playbooks`
... ...
@@ -59,11 +59,6 @@ You can use ``ansible-doc -t strategy -l`` to see the list of available plugins.
59 59
 Use ``ansible-doc -t strategy <plugin name>`` to see plugin-specific specific documentation and examples.
60 60
 
61 61
 
62
-.. toctree:: :maxdepth: 1
63
-    :glob:
64
-
65
-    strategy/*
66
-
67 62
 .. seealso::
68 63
 
69 64
    :ref:`about_playbooks`
... ...
@@ -57,11 +57,6 @@ You can use ``ansible-doc -t vars -l`` to see the list of available plugins.
57 57
 Use ``ansible-doc -t vars <plugin name>`` to see specific plugin-specific documentation and examples.
58 58
 
59 59
 
60
-.. toctree:: :maxdepth: 1
61
-    :glob:
62
-
63
-    vars/*
64
-
65 60
 .. seealso::
66 61
 
67 62
    :ref:`action_plugins`
... ...
@@ -286,7 +286,7 @@ For more information, see `this systemd issue
286 286
 Become and network automation
287 287
 =============================
288 288
 
289
-As of version 2.6, Ansible supports ``become`` for privilege escalation (entering ``enable`` mode or privileged EXEC mode) on all :ref:`Ansible-maintained platforms<network_supported>` that support ``enable`` mode. Using ``become`` replaces the ``authorize`` and ``auth_pass`` options in a ``provider`` dictionary.
289
+As of version 2.6, Ansible supports ``become`` for privilege escalation (entering ``enable`` mode or privileged EXEC mode) on all Ansible-maintained network platforms that support ``enable`` mode. Using ``become`` replaces the ``authorize`` and ``auth_pass`` options in a ``provider`` dictionary.
290 290
 
291 291
 You must set the connection type to either ``connection: network_cli`` or ``connection: httpapi`` to use ``become`` for privilege escalation on network devices. Check the :ref:`platform_options` and :ref:`network_modules` documentation for details.
292 292
 
... ...
@@ -5,7 +5,8 @@
5 5
 Using collections
6 6
 *****************
7 7
 
8
-Collections are a distribution format for Ansible content that can include playbooks, roles, modules, and plugins.
8
+Collections are a distribution format for Ansible content that can include playbooks, roles, modules, and plugins. As modules move from the core Ansible repository into collections, the module documentation will move to the `collections documentation page <https://docs.ansible.com/collections/>`_
9
+
9 10
 You can install and use collections through `Ansible Galaxy <https://galaxy.ansible.com>`_.
10 11
 
11 12
 * For details on how to *develop* collections see :ref:`developing_collections`.
... ...
@@ -7,9 +7,8 @@ Working With Modules
7 7
    :maxdepth: 1
8 8
 
9 9
    modules_intro
10
-   ../reference_appendices/common_return_values
11 10
    modules_support
12
-   ../modules/modules_by_category
11
+   ../reference_appendices/common_return_values
13 12
 
14 13
 
15 14
 Ansible ships with a number of modules (called the 'module library')
16 15
deleted file mode 100644
... ...
@@ -1,48 +0,0 @@
1
-{# avoids rST "isn't included in any toctree" errors for module docs #}
2
-:orphan:
3
-
4
-{% if title %}
5
-.. _@{ title.lower() + '_' + plugin_type + 's' }@:
6
-{% else %}
7
-.. _@{ plugin_type + 's' }@:
8
-{% endif %}
9
-
10
-{% if title %}
11
-@{ title }@ @{ plugin_type + 's' }@
12
-@{ '`' * title | length }@````````
13
-{% else %}
14
-@{ plugin_type + 's' }@
15
-```````
16
-{% endif %}
17
-
18
-{% if blurb %}
19
-@{ blurb }@
20
-
21
-{% endif %}
22
-
23
-{% if category['_modules'] %}
24
-
25
-{% for module in category['_modules'] | sort %}
26
-  * :ref:`@{ module }@_@{ plugin_type }@`{% if module_info[module]['deprecated'] %} **(D)**{% endif%} 
27
-{% endfor %}
28
-{% endif %}
29
-
30
-{% for name, info in subcategories.items() | sort %}
31
-
32
-.. _@{ name.lower() + '_' + title.lower() + '_' + plugin_type + 's' }@:
33
-
34
-@{ name.title() }@
35
-@{ '-' * name | length }@
36
-
37
-
38
-
39
-{% for module in info['_modules'] | sort %}
40
-  * :ref:`@{ module }@_@{ plugin_type }@`{% if module_info[module]['deprecated'] %} **(D)**{% endif%} 
41
-{% endfor %}
42
-
43
-{% endfor %}
44
-
45
-.. note::
46
-    - **(D)**: This marks a module as deprecated, which means a module is kept for backwards compatibility but usage is discouraged.
47
-      The module documentation details page may explain more about this rationale.
48
-
49 1
deleted file mode 100644
... ...
@@ -1,36 +0,0 @@
1
-.. _@{ title.lower() + '_' + plugin_type + 's' }@:
2
-
3
-@{ title }@ @{ plugin_type }@
4
-@{ '`' * title | length }@````````
5
-
6
-{% if blurb %}
7
-@{ blurb }@
8
-
9
-{% endif %}
10
-.. toctree:: :maxdepth: 1
11
-{% if category['_modules'] %}
12
-
13
-{% for module in category['_modules'] | sort %}
14
-  @{ module }@{% if module_info[module]['deprecated'] %} **(D)**{% endif%}{% if module_info[module]['doc']['short_description'] %} -- @{ module_info[module]['doc']['short_description'] }@{% endif %} <plugins/@{ module_info[module]['primary_category'] }@/@{ module }@>
15
-{% endfor %}
16
-{% endif %}
17
-
18
-{% for name, info in subcategories.items() | sort %}
19
-
20
-.. _@{ name.lower() + '_' + title.lower() + '_' + plugin_type + 's' }@:
21
-
22
-@{ name.title() }@
23
-@{ '-' * name | length }@
24
-
25
-.. toctree:: :maxdepth: 1
26
-
27
-{% for module in info['_modules'] | sort %}
28
-  :ref:`@{ module }@_@{ plugin_type }@`{% if module_info[module]['deprecated'] %} **(D)**{% endif%} -- @{ module_info[module]['doc']['short_description'] }@
29
-{% endfor %}
30
-
31
-{% endfor %}
32
-
33
-.. note::
34
-    - **(D)**: This marks a module as deprecated, which means a module is kept for backwards compatibility but usage is discouraged.
35
-      The module documentation details page may explain more about this rationale.
36
-
37 1
deleted file mode 100644
... ...
@@ -1,45 +0,0 @@
1
-.. _@{ slug }@:
2
-
3
-{# avoids rST "isn't included in any toctree" errors for module index docs #}
4
-:orphan:
5
-
6
-**************************@{ '*' * maintainers | length }@
7
-Modules Maintained by the @{ maintainers }@
8
-**************************@{ '*' * maintainers | length }@
9
-
10
-.. contents::
11
-   :local:
12
-
13
-{% for category, data in subcategories.items() | sort %}
14
-
15
-{% if category.lower() %}
16
-.. _@{ category.lower() + '_' + slug.lower() + '_categories' }@:
17
-{% else %}
18
-.. _@{ slug.lower() + '_categories' }@:
19
-{% endif %}
20
-
21
-@{ category.title() }@
22
-@{ '=' * category | length }@
23
-
24
-{% for name, info in data.items() | sort %}
25
-
26
-{% if name.lower() %}
27
-.. _@{ name.lower() + '_' + category + '_' + slug.lower() + '_' + plugin_type + 's' }@:
28
-{% else %}
29
-.. _@{ slug.lower() + '_' + category }@:
30
-{% endif %}
31
-
32
-@{ name.title() }@
33
-@{ '-' * name | length }@
34
-
35
-{% for module in info['_modules'] | sort %}
36
-  * :ref:`@{ module }@_@{plugin_type}@`{% if module_info[module]['deprecated'] %} **(D)** {% endif%} 
37
-{% endfor %}
38
-
39
-{% endfor %}
40
-
41
-{% endfor %}
42
-
43
-.. note::
44
-    - **(D)**: This marks a module as deprecated, which means a module is kept for backwards compatibility but usage is discouraged.
45
-      The module documentation details page may explain more about this rationale.
46 1
deleted file mode 100644
... ...
@@ -1,442 +0,0 @@
1
-:source: @{ source }@
2
-
3
-{# avoids rST "isn't included in any toctree" errors for module docs #}
4
-{% if plugin_type == 'module' %}
5
-:orphan:
6
-{% endif %}
7
-
8
-.. _@{ module }@_@{ plugin_type }@:
9
-{% for alias in aliases %}
10
-.. _@{ alias }@_@{ plugin_type }@:
11
-{% endfor %}
12
-
13
-{% if short_description %}
14
-{%   set title = module + ' -- ' + short_description | rst_ify %}
15
-{% else %}
16
-{%   set title = module %}
17
-{% endif %}
18
-
19
-@{ title }@
20
-@{ '+' * title|length }@
21
-
22
-{% if version_added is defined and version_added != '' -%}
23
-.. versionadded:: @{ version_added | default('') }@
24
-{% endif %}
25
-
26
-.. contents::
27
-   :local:
28
-   :depth: 1
29
-
30
-{# ------------------------------------------
31
- #
32
- # Please note: this looks like a core dump
33
- # but it isn't one.
34
- #
35
- --------------------------------------------#}
36
-{% if deprecated is defined -%}
37
-
38
-
39
-DEPRECATED
40
-{# use unknown here? skip the fields? #}
41
-:Removed in Ansible: version: @{ deprecated['removed_in'] | default('') | string | rst_ify }@
42
-:Why: @{ deprecated['why'] | default('') | rst_ify }@
43
-:Alternative: @{ deprecated['alternative'] | default('') | rst_ify }@
44
-
45
-
46
-{% endif %}
47
-
48
-Synopsis
49
-{% if description -%}
50
-
51
-{%   for desc in description %}
52
-- @{ desc | rst_ify }@
53
-{%   endfor %}
54
-
55
-{% endif %}
56
-
57
-{% if aliases is defined -%}
58
-Aliases: @{ ','.join(aliases) }@
59
-{% endif %}
60
-
61
-{% if requirements -%}
62
-
63
-Requirements
64
-{%   if plugin_type == 'module' %}
65
-The below requirements are needed on the host that executes this @{ plugin_type }@.
66
-{%   else %}
67
-The below requirements are needed on the local master node that executes this @{ plugin_type }@.
68
-{%   endif %}
69
-
70
-{%   for req in requirements %}
71
-- @{ req | rst_ify }@
72
-{%   endfor %}
73
-
74
-{% endif %}
75
-
76
-{% if options -%}
77
-
78
-Parameters
79
-
80
-.. raw:: html
81
-
82
-    <table  border=0 cellpadding=0 class="documentation-table">
83
-        {# Pre-compute the nesting depth to allocate columns -#}
84
-        @{ to_kludge_ns('maxdepth', 1) -}@
85
-        {% for key, value in options|dictsort recursive -%}
86
-            @{ to_kludge_ns('maxdepth', [loop.depth, from_kludge_ns('maxdepth')] | max) -}@
87
-            {% if value.suboptions -%}
88
-                {% if value.suboptions.items -%}
89
-                    @{ loop(value.suboptions.items()) -}@
90
-                {% elif value.suboptions[0].items -%}
91
-                    @{ loop(value.suboptions[0].items()) -}@
92
-                {% endif -%}
93
-            {% endif -%}
94
-        {% endfor -%}
95
-        {# Header of the documentation -#}
96
-        <tr>
97
-            <th colspan="@{ from_kludge_ns('maxdepth') }@">Parameter</th>
98
-            <th>Choices/<font color="blue">Defaults</font></th>
99
-            {% if plugin_type != 'module' %}
100
-                <th>Configuration</th>
101
-            {% endif %}
102
-            <th width="100%">Comments</th>
103
-        </tr>
104
-        {% for key, value in options|dictsort recursive %}
105
-            <tr>
106
-                {# indentation based on nesting level #}
107
-                {% for i in range(1, loop.depth) %}
108
-                    <td class="elbow-placeholder"></td>
109
-                {% endfor %}
110
-                {# parameter name with required and/or introduced label #}
111
-                <td colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@">
112
-                    <div class="ansibleOptionAnchor" id="parameter-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}"></div>
113
-                    <b>@{ key }@</b>
114
-                    <a class="ansibleOptionLink" href="#parameter-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}" title="Permalink to this option"></a>
115
-                    <div style="font-size: small">
116
-                        <span style="color: purple">@{ value.type | documented_type }@</span>
117
-                        {% if value.get('elements') %} / <span style="color: purple">elements=@{ value.elements | documented_type }@</span>{% endif %}
118
-                        {% if value.get('required', False) %} / <span style="color: red">required</span>{% endif %}
119
-                    </div>
120
-                    {% if value.version_added %}<div style="font-style: italic; font-size: small; color: darkgreen">added in @{value.version_added}@</div>{% endif %}
121
-                </td>
122
-                {# default / choices #}
123
-                <td>
124
-                    {# Turn boolean values in 'yes' and 'no' values #}
125
-                    {% if value.default is sameas true %}
126
-                        {% set _x = value.update({'default': 'yes'}) %}
127
-                    {% elif value.default is sameas false %}
128
-                        {% set _x = value.update({'default': 'no'}) %}
129
-                    {% endif %}
130
-                    {% if value.type == 'bool' %}
131
-                        {% set _x = value.update({'choices': ['no', 'yes']}) %}
132
-                    {% endif %}
133
-                    {# Show possible choices and highlight details #}
134
-                    {% if value.choices %}
135
-                        <ul style="margin: 0; padding: 0"><b>Choices:</b>
136
-                            {% for choice in value.choices %}
137
-                                {# Turn boolean values in 'yes' and 'no' values #}
138
-                                {% if choice is sameas true %}
139
-                                    {% set choice = 'yes' %}
140
-                                {% elif choice is sameas false %}
141
-                                    {% set choice = 'no' %}
142
-                                {% endif %}
143
-                                {% if (value.default is not list and value.default == choice) or (value.default is list and choice in value.default) %}
144
-                                    <li><div style="color: blue"><b>@{ choice | escape }@</b>&nbsp;&larr;</div></li>
145
-                                {% else %}
146
-                                    <li>@{ choice | escape }@</li>
147
-                                {% endif %}
148
-                            {% endfor %}
149
-                        </ul>
150
-                    {% endif %}
151
-                    {# Show default value, when multiple choice or no choices #}
152
-                    {% if value.default is defined and value.default not in value.choices %}
153
-                        <b>Default:</b><br/><div style="color: blue">@{ value.default | tojson | escape }@</div>
154
-                    {% endif %}
155
-                </td>
156
-                {# configuration #}
157
-                {% if plugin_type != 'module' %}
158
-                    <td>
159
-                        {% if 'ini' in value %}
160
-                            <div> ini entries:
161
-                                {% for ini in value.ini %}
162
-                                    <p>[@{ ini.section }@]<br>@{ ini.key }@ = @{ value.default | default('VALUE') }@</p>
163
-                                {% endfor %}
164
-                            </div>
165
-                        {% endif %}
166
-                        {% if 'env' in value %}
167
-                            {% for env in value.env %}
168
-                                <div>env:@{ env.name }@</div>
169
-                            {% endfor %}
170
-                        {% endif %}
171
-                        {% if 'vars' in value %}
172
-                            {% for myvar in value.vars %}
173
-                                <div>var: @{ myvar.name }@</div>
174
-                            {% endfor %}
175
-                        {% endif %}
176
-                    </td>
177
-                {% endif %}
178
-                {# description #}
179
-                <td>
180
-                    {% for desc in value.description %}
181
-                        <div>@{ desc | replace('\n', '\n    ') | html_ify }@</div>
182
-                    {% endfor %}
183
-                    {% if 'aliases' in value and value.aliases %}
184
-                        <div style="font-size: small; color: darkgreen"><br/>aliases: @{ value.aliases|join(', ') }@</div>
185
-                    {% endif %}
186
-                </td>
187
-            </tr>
188
-            {% if value.suboptions %}
189
-                {% if value.suboptions.items %}
190
-                    @{ loop(value.suboptions|dictsort) }@
191
-                {% elif value.suboptions[0].items %}
192
-                    @{ loop(value.suboptions[0]|dictsort) }@
193
-                {% endif %}
194
-            {% endif %}
195
-        {% endfor %}
196
-    </table>
197
-    <br/>
198
-
199
-{% endif %}
200
-
201
-{% if notes -%}
202
-Notes
203
-
204
-.. note::
205
-{%   for note in notes %}
206
-   - @{ note | rst_ify }@
207
-{%   endfor %}
208
-
209
-{% endif %}
210
-
211
-{% if seealso -%}
212
-See Also
213
-
214
-.. seealso::
215
-
216
-{% for item in seealso %}
217
-{%   if item.module is defined and item.description is defined %}
218
-   :ref:`@{ item.module }@_module`
219
-       @{ item.description | rst_ify }@
220
-{%   elif item.module is defined %}
221
-   :ref:`@{ item.module }@_module`
222
-      The official documentation on the **@{ item.module }@** module.
223
-{%   elif item.name is defined and item.link is defined and item.description is defined %}
224
-   `@{ item.name }@ <@{ item.link }@>`_
225
-       @{ item.description | rst_ify }@
226
-{%   elif item.ref is defined and item.description is defined %}
227
-   :ref:`@{ item.ref }@`
228
-       @{ item.description | rst_ify }@
229
-{%   endif %}
230
-{% endfor %}
231
-
232
-{% endif %}
233
-
234
-{% if examples or plainexamples -%}
235
-
236
-Examples
237
-
238
-.. code-block:: yaml+jinja
239
-
240
-{%   for example in examples %}
241
-{%     if example['description'] %}@{ example['description'] | indent(4, True) }@{% endif %}
242
-@{ example['code'] | escape | indent(4, True) }@
243
-{%   endfor %}
244
-{%   if plainexamples %}@{ plainexamples | indent(4, True) }@{% endif %}
245
-
246
-{% endif %}
247
-
248
-{% if not returnfacts and returndocs and returndocs.ansible_facts is defined %}
249
-{%   set returnfacts = returndocs.ansible_facts.contains %}
250
-{%   set _x = returndocs.pop('ansible_facts', None) %}
251
-{% endif %}
252
-
253
-{% if returnfacts -%}
254
-
255
-Returned Facts
256
-Facts returned by this module are added/updated in the ``hostvars`` host facts and can be referenced by name just like any other host fact. They do not need to be registered in order to use them.
257
-
258
-.. raw:: html
259
-
260
-    <table border=0 cellpadding=0 class="documentation-table">
261
-        {# Pre-compute the nesting depth to allocate columns #}
262
-        @{ to_kludge_ns('maxdepth', 1) -}@
263
-        {% for key, value in returnfacts|dictsort recursive %}
264
-            @{ to_kludge_ns('maxdepth', [loop.depth, from_kludge_ns('maxdepth')] | max) -}@
265
-            {% if value.contains -%}
266
-                {% if value.contains.items -%}
267
-                    @{ loop(value.contains.items()) -}@
268
-                {% elif value.contains[0].items -%}
269
-                    @{ loop(value.contains[0].items()) -}@
270
-                {% endif -%}
271
-            {% endif -%}
272
-        {% endfor -%}
273
-        <tr>
274
-            <th colspan="@{ from_kludge_ns('maxdepth') }@">Fact</th>
275
-            <th>Returned</th>
276
-            <th width="100%">Description</th>
277
-        </tr>
278
-        {% for key, value in returnfacts|dictsort recursive %}
279
-            <tr>
280
-                {% for i in range(1, loop.depth) %}
281
-                    <td class="elbow-placeholder"></td>
282
-                {% endfor %}
283
-                <td colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@" colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@">
284
-                    <div class="ansibleOptionAnchor" id="return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}"></div>
285
-                    <b>@{ key }@</b>
286
-                    <a class="ansibleOptionLink" href="#return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}" title="Permalink to this fact"></a>
287
-                    <div style="font-size: small">
288
-                      <span style="color: purple">@{ value.type | documented_type }@</span>
289
-                      {% if value.elements %} / <span style="color: purple">elements=@{ value.elements | documented_type }@</span>{% endif %}
290
-                    </div>
291
-                    {% if value.version_added %}<div style="font-style: italic; font-size: small; color: darkgreen">added in @{value.version_added}@</div>{% endif %}
292
-                </td>
293
-                <td>@{ value.returned | html_ify }@</td>
294
-                <td>
295
-                    {% if value.description is string %}
296
-                        <div>@{ value.description | html_ify }@
297
-                        </div>
298
-                    {% else %}
299
-                        {% for desc in value.description %}
300
-                            <div>@{ desc | html_ify }@
301
-                            </div>
302
-                        {% endfor %}
303
-                    {% endif %}
304
-                    <br/>
305
-                    {% if value.sample is defined and value.sample %}
306
-                        <div style="font-size: smaller"><b>Sample:</b></div>
307
-                        {# TODO: The sample should be escaped, using | escape or | htmlify, but both mess things up beyond repair with dicts #}
308
-                        <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n    ') | html_ify }@</div>
309
-                    {% endif %}
310
-                </td>
311
-            </tr>
312
-            {# ---------------------------------------------------------
313
-             # sadly we cannot blindly iterate through the child dicts,
314
-             # since in some documentations,
315
-             # lists are used instead of dicts. This handles both types
316
-             # ---------------------------------------------------------#}
317
-            {% if value.contains %}
318
-                {% if value.contains.items %}
319
-                    @{ loop(value.contains|dictsort) }@
320
-                {% elif value.contains[0].items %}
321
-                    @{ loop(value.contains[0]|dictsort) }@
322
-                {% endif %}
323
-            {% endif %}
324
-        {% endfor %}
325
-    </table>
326
-    <br/><br/>
327
-
328
-{% endif %}
329
-
330
-{% if returndocs -%}
331
-
332
-Return Values
333
-Common return values are documented :ref:`here <common_return_values>`, the following are the fields unique to this @{ plugin_type }@:
334
-
335
-.. raw:: html
336
-
337
-    <table border=0 cellpadding=0 class="documentation-table">
338
-        @{ to_kludge_ns('maxdepth', 1) -}@
339
-        {% for key, value in returndocs|dictsort recursive -%}
340
-            @{ to_kludge_ns('maxdepth', [loop.depth, from_kludge_ns('maxdepth')] | max) -}@
341
-            {% if value.contains -%}
342
-                {% if value.contains.items -%}
343
-                    @{ loop(value.contains.items()) -}@
344
-                {% elif value.contains[0].items -%}
345
-                    @{ loop(value.contains[0].items()) -}@
346
-                {% endif -%}
347
-            {% endif -%}
348
-        {% endfor -%}
349
-        <tr>
350
-            <th colspan="@{ from_kludge_ns('maxdepth') }@">Key</th>
351
-            <th>Returned</th>
352
-            <th width="100%">Description</th>
353
-        </tr>
354
-        {% for key, value in returndocs|dictsort recursive %}
355
-            <tr>
356
-                {% for i in range(1, loop.depth) %}
357
-                    <td class="elbow-placeholder">&nbsp;</td>
358
-                {% endfor %}
359
-                <td colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@">
360
-                    <div class="ansibleOptionAnchor" id="return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}"></div>
361
-                    <b>@{ key }@</b>
362
-                    <a class="ansibleOptionLink" href="#return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}" title="Permalink to this return value"></a>
363
-                    <div style="font-size: small">
364
-                      <span style="color: purple">@{ value.type | documented_type }@</span>
365
-                      {% if value.elements %} / <span style="color: purple">elements=@{ value.elements | documented_type }@</span>{% endif %}
366
-                    </div>
367
-                    {% if value.version_added %}<div style="font-style: italic; font-size: small; color: darkgreen">added in @{value.version_added}@</div>{% endif %}
368
-                </td>
369
-                <td>@{ value.returned | html_ify }@</td>
370
-                <td>
371
-                    {% if value.description is string %}
372
-                        <div>@{ value.description | html_ify |indent(4) | trim}@</div>
373
-                    {% else %}
374
-                        {% for desc in value.description %}
375
-                            <div>@{ desc | html_ify |indent(4) | trim}@</div>
376
-                        {% endfor %}
377
-                    {% endif %}
378
-                    <br/>
379
-                    {% if value.sample is defined and value.sample %}
380
-                        <div style="font-size: smaller"><b>Sample:</b></div>
381
-                        {# TODO: The sample should be escaped, using |escape or |htmlify, but both mess things up beyond repair with dicts #}
382
-                        <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n    ') | html_ify }@</div>
383
-                    {% endif %}
384
-                </td>
385
-            </tr>
386
-            {# ---------------------------------------------------------
387
-             # sadly we cannot blindly iterate through the child dicts,
388
-             # since in some documentations,
389
-             # lists are used instead of dicts. This handles both types
390
-             # ---------------------------------------------------------#}
391
-            {% if value.contains %}
392
-                {% if value.contains.items %}
393
-                    @{ loop(value.contains|dictsort) }@
394
-                {% elif value.contains[0].items %}
395
-                    @{ loop(value.contains[0]|dictsort) }@
396
-                {% endif %}
397
-            {% endif %}
398
-        {% endfor %}
399
-    </table>
400
-    <br/><br/>
401
-
402
-{% endif %}
403
-
404
-Status
405
-
406
-{% if deprecated %}
407
-
408
-- This @{ plugin_type }@ will be removed in version @{ deprecated['removed_in'] | default('') | string | rst_ify }@. *[deprecated]*
409
-- For more information see `DEPRECATED`_.
410
-
411
-{% endif %}
412
-
413
-{% if author is defined -%}
414
-Authors
415
-~~~~~~~
416
-
417
-{%   for author_name in author %}
418
-- @{ author_name }@
419
-{%   endfor %}
420
-
421
-{% endif %}
422
-
423
-.. hint::
424
-{%   if plugin_type == 'module' %}
425
-    If you notice any issues in this documentation, you can `edit this document <https://github.com/ansible/ansible/edit/devel/lib/ansible/modules/@{ source }@?description=%23%23%23%23%23%20SUMMARY%0A%3C!---%20Your%20description%20here%20--%3E%0A%0A%0A%23%23%23%23%23%20ISSUE%20TYPE%0A-%20Docs%20Pull%20Request%0A%0A%2Blabel:%20docsite_pr>`_ to improve it.
426
-{% else %}
427
-    If you notice any issues in this documentation, you can `edit this document <https://github.com/ansible/ansible/edit/devel/lib/ansible/plugins/@{ plugin_type }@/@{ source }@?description=%23%23%23%23%23%20SUMMARY%0A%3C!---%20Your%20description%20here%20--%3E%0A%0A%0A%23%23%23%23%23%20ISSUE%20TYPE%0A-%20Docs%20Pull%20Request%0A%0A%2Blabel:%20docsite_pr>`_ to improve it.
428
-
429
-
430
-.. hint::
431
-    Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
432
-{% endif %}
433 1
deleted file mode 100644
... ...
@@ -1,18 +0,0 @@
1
-:source: @{ source }@
2
-
3
-{# avoids rST "isn't included in any toctree" errors for module docs #}
4
-:orphan:
5
-
6
-.. _@{ module }@_@{ plugin_type }@_alias_@{ alias }@:
7
-
8
-{% if short_description %}
9
-{%   set title = alias + ' -- ' + short_description | rst_ify %}
10
-{% else %}
11
-{%   set title = alias %}
12
-{% endif %}
13
-
14
-@{ title }@
15
-@{ '+' * title|length }@
16
-
17
-This is an alias for :ref:`@{ module }@ <@{ module }@_@{ plugin_type }@>`.
18
-This name has been **deprecated**. Please update your tasks to use the new name ``@{ module }@`` instead.
19 1
deleted file mode 100644
... ...
@@ -1,9 +0,0 @@
1
-Plugin Index
2
-============
3
-
4
-
5
-.. toctree:: :maxdepth: 1
6
-
7
-{% for name in categories %}
8
-   list_of_@{ name }@_plugins
9
-{% endfor %}
10 1
deleted file mode 100644
... ...
@@ -1,15 +0,0 @@
1
-.. _@{ slug }@:
2
-
3
-Plugins Maintained by the @{ maintainers }@
4
-``````````````````````````@{ '`' * maintainers | length }@
5
-
6
-.. toctree:: :maxdepth: 1
7
-
8
-{% for module in modules | sort %}
9
-  @{ module }@{% if module_info[module]['deprecated'] %} **(D)**{% endif %} - @{ module_info[module]['doc']['short_description'] }@ <plugins/@{ module_info[module]['primary_category'] }@/@{ module }@>
10
-{% endfor %}
11
-
12
-.. note::
13
-    - **(D)**: This marks a plugin as deprecated, which means a plugin is kept for backwards compatibility but usage is discouraged.
14
-      The plugin documentation details page may explain more about this rationale.
15
-
... ...
@@ -22,15 +22,25 @@ except ImportError:
22 22
 
23 23
 
24 24
 def build_lib_path(this_script=__file__):
25
-    """Return path to the common build library directory"""
25
+    """Return path to the common build library directory."""
26 26
     hacking_dir = os.path.dirname(this_script)
27 27
     libdir = os.path.abspath(os.path.join(hacking_dir, 'build_library'))
28 28
 
29 29
     return libdir
30 30
 
31 31
 
32
+def ansible_lib_path(this_script=__file__):
33
+    """Return path to the common build library directory."""
34
+    hacking_dir = os.path.dirname(this_script)
35
+    libdir = os.path.abspath(os.path.join(hacking_dir, '..', 'lib'))
36
+
37
+    return libdir
38
+
39
+
40
+sys.path.insert(0, ansible_lib_path())
32 41
 sys.path.insert(0, build_lib_path())
33 42
 
43
+
34 44
 from build_ansible import commands, errors
35 45
 
36 46
 
... ...
@@ -47,14 +57,15 @@ def create_arg_parser(program_name):
47 47
 
48 48
 def main():
49 49
     """
50
-    Main entrypoint of the script
50
+    Start our run.
51 51
 
52 52
     "It all starts here"
53 53
     """
54 54
     subcommands = load('build_ansible.command_plugins', subclasses=commands.Command)
55 55
 
56 56
     arg_parser = create_arg_parser(os.path.basename(sys.argv[0]))
57
-    arg_parser.add_argument('--debug', dest='debug', required=False, default=False, action='store_true',
57
+    arg_parser.add_argument('--debug', dest='debug', required=False, default=False,
58
+                            action='store_true',
58 59
                             help='Show tracebacks and other debugging information')
59 60
     subparsers = arg_parser.add_subparsers(title='Subcommands', dest='command',
60 61
                                            help='for help use build-ansible.py SUBCOMMANDS -h')
... ...
@@ -11,14 +11,13 @@ import os.path
11 11
 import pathlib
12 12
 
13 13
 import yaml
14
-from jinja2 import Environment, FileSystemLoader
15 14
 from ansible.module_utils.six import string_types
16 15
 from ansible.module_utils._text import to_bytes
16
+from antsibull.jinja2.environment import doc_environment
17 17
 
18 18
 # Pylint doesn't understand Python3 namespace modules.
19 19
 from ..change_detection import update_file_if_different  # pylint: disable=relative-beyond-top-level
20 20
 from ..commands import Command  # pylint: disable=relative-beyond-top-level
21
-from ..jinja2.filters import documented_type, rst_ify  # pylint: disable=relative-beyond-top-level
22 21
 
23 22
 
24 23
 DEFAULT_TEMPLATE_FILE = 'collections_galaxy_meta.rst.j2'
... ...
@@ -61,12 +60,7 @@ class DocumentCollectionMeta(Command):
61 61
 
62 62
         normalize_options(options)
63 63
 
64
-        env = Environment(loader=FileSystemLoader(template_dir),
65
-                          variable_start_string="@{",
66
-                          variable_end_string="}@",
67
-                          trim_blocks=True)
68
-        env.filters['documented_type'] = documented_type
69
-        env.filters['rst_ify'] = rst_ify
64
+        env = doc_environment(template_dir)
70 65
 
71 66
         template = env.get_template(template_file)
72 67
         output_name = os.path.join(output_dir, template_file.replace('.j2', ''))
73 68
new file mode 100644
... ...
@@ -0,0 +1,164 @@
0
+# coding: utf-8
1
+# Copyright: (c) 2020, Ansible Project
2
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3
+
4
+# Make coding more python3-ish
5
+from __future__ import absolute_import, division, print_function
6
+
7
+import glob
8
+import os
9
+import os.path
10
+import pathlib
11
+import shutil
12
+from tempfile import TemporaryDirectory
13
+
14
+import yaml
15
+
16
+from ansible.release import __version__ as ansible_base__version__
17
+
18
+# Pylint doesn't understand Python3 namespace modules.
19
+# pylint: disable=relative-beyond-top-level
20
+from ..commands import Command
21
+# pylint: enable=relative-beyond-top-level
22
+
23
+
24
+__metaclass__ = type
25
+
26
+
27
+DEFAULT_TOP_DIR = pathlib.Path(__file__).parents[4]
28
+DEFAULT_OUTPUT_DIR = pathlib.Path(__file__).parents[4] / 'docs/docsite'
29
+
30
+
31
+#
32
+# Subcommand base
33
+#
34
+
35
+def generate_base_docs(args):
36
+    """Regenerate the documentation for all plugins listed in the plugin_to_collection_file."""
37
+    # imports here so that they don't cause unnecessary deps for all of the plugins
38
+    from antsibull.cli import antsibull_docs
39
+
40
+    with TemporaryDirectory() as tmp_dir:
41
+        #
42
+        # Construct a deps file with our version of ansible_base in it
43
+        #
44
+        modified_deps_file = os.path.join(tmp_dir, 'ansible.deps')
45
+
46
+        # The _acd_version doesn't matter
47
+        deps_file_contents = {'_acd_version': ansible_base__version__,
48
+                              '_ansible_base_version': ansible_base__version__}
49
+
50
+        with open(modified_deps_file, 'w') as f:
51
+            f.write(yaml.dump(deps_file_contents))
52
+
53
+        # Generate the plugin rst
54
+        antsibull_docs.run(['antsibull-docs', 'stable', '--deps-file', modified_deps_file,
55
+                            '--ansible-base-cache', str(args.top_dir),
56
+                            '--dest-dir', args.output_dir])
57
+
58
+        # If we make this more than just a driver for antsibull:
59
+        # Run other rst generation
60
+        # Run sphinx build
61
+
62
+
63
+#
64
+# Subcommand full
65
+#
66
+
67
+def generate_full_docs(args):
68
+    """Regenerate the documentation for all plugins listed in the plugin_to_collection_file."""
69
+    # imports here so that they don't cause unnecessary deps for all of the plugins
70
+    import sh
71
+    from antsibull.cli import antsibull_docs
72
+    from packaging.version import Version
73
+
74
+    ansible_base_ver = Version(ansible_base__version__)
75
+    ansible_base_major_ver = '{0}.{1}'.format(ansible_base_ver.major, ansible_base_ver.minor)
76
+
77
+    with TemporaryDirectory() as tmp_dir:
78
+        sh.git(['clone', 'https://github.com/ansible-community/ansible-build-data'], _cwd=tmp_dir)
79
+        deps_files = glob.glob(os.path.join(tmp_dir, 'ansible-build-data',
80
+                                            ansible_base_major_ver, '*.deps'))
81
+        if not deps_files:
82
+            raise Exception('No deps files exist for version {0}'.format(ansible_base_major_ver))
83
+
84
+        # Find the latest version of the deps file for this version
85
+        latest = None
86
+        latest_ver = Version('0')
87
+        for filename in deps_files:
88
+            with open(filename, 'r') as f:
89
+                deps_data = yaml.safe_load(f.read())
90
+            new_version = Version(deps_data['_ansible_base_version'])
91
+            if new_version > latest_ver:
92
+                latest_ver = new_version
93
+                latest = filename
94
+
95
+        # Make a copy of the deps file so that we can set the ansible-base version to use
96
+        modified_deps_file = os.path.join(tmp_dir, 'ansible.deps')
97
+        shutil.copyfile(latest, modified_deps_file)
98
+
99
+        # Put our version of ansible-base into the deps file
100
+        with open(modified_deps_file, 'r') as f:
101
+            deps_data = yaml.safe_load(f.read())
102
+
103
+        deps_data['_ansible_base_version'] = ansible_base__version__
104
+
105
+        with open(modified_deps_file, 'w') as f:
106
+            f.write(yaml.dump(deps_data))
107
+
108
+        # Generate the plugin rst
109
+        antsibull_docs.run(['antsibull-docs', 'stable', '--deps-file', modified_deps_file,
110
+                            '--ansible-base-cache', str(args.top_dir),
111
+                            '--dest-dir', args.output_dir])
112
+
113
+        # If we make this more than just a driver for antsibull:
114
+        # Run other rst generation
115
+        # Run sphinx build
116
+
117
+
118
+class CollectionPluginDocs(Command):
119
+    name = 'docs-build'
120
+    _ACTION_HELP = """Action to perform.
121
+        full: Regenerate the rst for the full ansible website.
122
+        base: Regenerate the rst for plugins in ansible-base and then build the website.
123
+        named: Regenerate the rst for the named plugins and then build the website.
124
+    """
125
+
126
+    @classmethod
127
+    def init_parser(cls, add_parser):
128
+        parser = add_parser(cls.name,
129
+                            description='Generate documentation for plugins in collections.'
130
+                            ' Plugins in collections will have a stub file in the normal plugin'
131
+                            ' documentation location that says the module is in a collection and'
132
+                            ' point to generated plugin documentation under the collections/'
133
+                            ' hierarchy.')
134
+        parser.add_argument('action', action='store', choices=('full', 'base', 'named'),
135
+                            default='full', help=cls._ACTION_HELP)
136
+        parser.add_argument("-o", "--output-dir", action="store", dest="output_dir",
137
+                            default=DEFAULT_OUTPUT_DIR,
138
+                            help="Output directory for generated doc files")
139
+        parser.add_argument("-t", "--top-dir", action="store", dest="top_dir",
140
+                            default=DEFAULT_TOP_DIR,
141
+                            help="Toplevel directory of this ansible-base checkout or expanded"
142
+                            " tarball.")
143
+        parser.add_argument("-l", "--limit-to-modules", '--limit-to', action="store",
144
+                            dest="limit_to", default=None,
145
+                            help="Limit building module documentation to comma-separated list of"
146
+                            " plugins. Specify non-existing plugin name for no plugins.")
147
+
148
+    @staticmethod
149
+    def main(args):
150
+        # normalize CLI args
151
+
152
+        if not args.output_dir:
153
+            args.output_dir = os.path.abspath(str(DEFAULT_OUTPUT_DIR))
154
+
155
+        if args.action == 'full':
156
+            return generate_full_docs(args)
157
+
158
+        if args.action == 'base':
159
+            return generate_base_docs(args)
160
+        # args.action == 'named' (Invalid actions are caught by argparse)
161
+        raise NotImplementedError('Building docs for specific files is not yet implemented')
162
+
163
+        # return 0
0 164
deleted file mode 100644
... ...
@@ -1,807 +0,0 @@
1
-# Copyright: (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
2
-# Copyright: (c) 2012-2014, Michael DeHaan <michael@ansible.com> and others
3
-# Copyright: (c) 2017, Ansible Project
4
-
5
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
-
7
-from __future__ import absolute_import, division, print_function
8
-__metaclass__ = type
9
-
10
-
11
-import datetime
12
-import glob
13
-import json
14
-import os
15
-import re
16
-import sys
17
-import warnings
18
-from collections import defaultdict
19
-from copy import deepcopy
20
-from distutils.version import LooseVersion
21
-from functools import partial
22
-from pprint import PrettyPrinter
23
-
24
-try:
25
-    from html import escape as html_escape
26
-except ImportError:
27
-    # Python-3.2 or later
28
-    import cgi
29
-
30
-    def html_escape(text, quote=True):
31
-        return cgi.escape(text, quote)
32
-
33
-import jinja2
34
-import yaml
35
-from jinja2 import Environment, FileSystemLoader
36
-
37
-from ansible.errors import AnsibleError
38
-from ansible.module_utils._text import to_bytes
39
-from ansible.module_utils.common.collections import is_sequence
40
-from ansible.module_utils.parsing.convert_bool import boolean
41
-from ansible.module_utils.six import iteritems, string_types
42
-from ansible.plugins.loader import fragment_loader
43
-from ansible.utils import plugin_docs
44
-from ansible.utils.display import Display
45
-
46
-# Pylint doesn't understand Python3 namespace modules.
47
-from ..change_detection import update_file_if_different  # pylint: disable=relative-beyond-top-level
48
-from ..commands import Command  # pylint: disable=relative-beyond-top-level
49
-from ..jinja2.filters import do_max, documented_type, html_ify, rst_fmt, rst_ify, rst_xline  # pylint: disable=relative-beyond-top-level
50
-
51
-
52
-#####################################################################################
53
-# constants and paths
54
-
55
-# if a module is added in a version of Ansible older than this, don't print the version added information
56
-# in the module documentation because everyone is assumed to be running something newer than this already.
57
-TOO_OLD_TO_BE_NOTABLE = 2.4
58
-
59
-# Get parent directory of the directory this script lives in
60
-MODULEDIR = os.path.abspath(os.path.join(
61
-    os.path.dirname(os.path.realpath(__file__)), os.pardir, 'lib', 'ansible', 'modules'
62
-))
63
-
64
-# The name of the DOCUMENTATION template
65
-EXAMPLE_YAML = os.path.abspath(os.path.join(
66
-    os.path.dirname(os.path.realpath(__file__)), os.pardir, 'examples', 'DOCUMENTATION.yml'
67
-))
68
-
69
-DEPRECATED = b" (D)"
70
-
71
-pp = PrettyPrinter()
72
-display = Display()
73
-
74
-
75
-# kludge_ns gives us a kludgey way to set variables inside of loops that need to be visible outside
76
-# the loop.  We can get rid of this when we no longer need to build docs with less than Jinja-2.10
77
-# http://jinja.pocoo.org/docs/2.10/templates/#assignments
78
-# With Jinja-2.10 we can use jinja2's namespace feature, restoring the namespace template portion
79
-# of: fa5c0282a4816c4dd48e80b983ffc1e14506a1f5
80
-NS_MAP = {}
81
-
82
-
83
-def to_kludge_ns(key, value):
84
-    NS_MAP[key] = value
85
-    return ""
86
-
87
-
88
-def from_kludge_ns(key):
89
-    return NS_MAP[key]
90
-
91
-
92
-test_list = partial(is_sequence, include_strings=False)
93
-
94
-
95
-def normalize_options(value):
96
-    """Normalize boolean option value."""
97
-
98
-    if value.get('type') == 'bool' and 'default' in value:
99
-        try:
100
-            value['default'] = boolean(value['default'], strict=True)
101
-        except TypeError:
102
-            pass
103
-    return value
104
-
105
-
106
-def write_data(text, output_dir, outputname, module=None):
107
-    ''' dumps module output to a file or the screen, as requested '''
108
-
109
-    if output_dir is not None:
110
-        if module:
111
-            outputname = outputname % module
112
-
113
-        if not os.path.exists(output_dir):
114
-            os.makedirs(output_dir)
115
-        fname = os.path.join(output_dir, outputname)
116
-        fname = fname.replace(".py", "")
117
-
118
-        try:
119
-            updated = update_file_if_different(fname, to_bytes(text))
120
-        except Exception as e:
121
-            display.display("while rendering %s, an error occured: %s" % (module, e))
122
-            raise
123
-        if updated:
124
-            display.display("rendering: %s" % module)
125
-    else:
126
-        print(text)
127
-
128
-
129
-IS_STDOUT_TTY = sys.stdout.isatty()
130
-
131
-
132
-def show_progress(progress):
133
-    '''Show a little process indicator.'''
134
-    if IS_STDOUT_TTY:
135
-        sys.stdout.write('\r%s\r' % ("-/|\\"[progress % 4]))
136
-        sys.stdout.flush()
137
-
138
-
139
-def get_plugin_info(module_dir, limit_to=None, verbose=False):
140
-    '''
141
-    Returns information about plugins and the categories that they belong to
142
-
143
-    :arg module_dir: file system path to the top of the plugin directory
144
-    :kwarg limit_to: If given, this is a list of plugin names to
145
-        generate information for.  All other plugins will be ignored.
146
-    :returns: Tuple of two dicts containing module_info, categories, and
147
-        aliases and a set listing deprecated modules:
148
-
149
-        :module_info: mapping of module names to information about them.  The fields of the dict are:
150
-
151
-            :path: filesystem path to the module
152
-            :deprecated: boolean.  True means the module is deprecated otherwise not.
153
-            :aliases: set of aliases to this module name
154
-            :metadata: The modules metadata (as recorded in the module)
155
-            :doc: The documentation structure for the module
156
-            :seealso: The list of dictionaries with references to related subjects
157
-            :examples: The module's examples
158
-            :returndocs: The module's returndocs
159
-
160
-        :categories: maps category names to a dict.  The dict contains at
161
-            least one key, '_modules' which contains a list of module names in
162
-            that category.  Any other keys in the dict are subcategories with
163
-            the same structure.
164
-
165
-    '''
166
-
167
-    categories = dict()
168
-    module_info = defaultdict(dict)
169
-
170
-    # * windows powershell modules have documentation stubs in python docstring
171
-    #   format (they are not executed) so skip the ps1 format files
172
-    # * One glob level for every module level that we're going to traverse
173
-    files = (
174
-        glob.glob("%s/*.py" % module_dir) +
175
-        glob.glob("%s/*/*.py" % module_dir) +
176
-        glob.glob("%s/*/*/*.py" % module_dir) +
177
-        glob.glob("%s/*/*/*/*.py" % module_dir)
178
-    )
179
-
180
-    module_index = 0
181
-    for module_path in files:
182
-        # Do not list __init__.py files
183
-        if module_path.endswith('__init__.py'):
184
-            continue
185
-
186
-        # Do not list blacklisted modules
187
-        module = os.path.splitext(os.path.basename(module_path))[0]
188
-        if module in plugin_docs.BLACKLIST['MODULE'] or module == 'base':
189
-            continue
190
-
191
-        # If requested, limit module documentation building only to passed-in
192
-        # modules.
193
-        if limit_to is not None and module.lower() not in limit_to:
194
-            continue
195
-
196
-        deprecated = False
197
-        if module.startswith("_"):
198
-            if os.path.islink(module_path):
199
-                # Handle aliases
200
-                source = os.path.splitext(os.path.basename(os.path.realpath(module_path)))[0]
201
-                module = module.replace("_", "", 1)
202
-                if source.startswith("_"):
203
-                    source = source.replace("_", "", 1)
204
-                aliases = module_info[source].get('aliases', set())
205
-                aliases.add(module)
206
-                aliases_deprecated = module_info[source].get('aliases_deprecated', set())
207
-                aliases_deprecated.add(module)
208
-                # In case we just created this via get()'s fallback
209
-                module_info[source]['aliases'] = aliases
210
-                module_info[source]['aliases_deprecated'] = aliases_deprecated
211
-                continue
212
-            else:
213
-                # Handle deprecations
214
-                module = module.replace("_", "", 1)
215
-                deprecated = True
216
-
217
-        #
218
-        # Regular module to process
219
-        #
220
-
221
-        module_index += 1
222
-        show_progress(module_index)
223
-
224
-        # use ansible core library to parse out doc metadata YAML and plaintext examples
225
-        doc, examples, returndocs, metadata = plugin_docs.get_docstring(
226
-            module_path, fragment_loader, verbose=verbose, collection_name='ansible.builtin')
227
-
228
-        if metadata and 'removed' in metadata.get('status', []):
229
-            continue
230
-
231
-        category = categories
232
-
233
-        # Start at the second directory because we don't want the "vendor"
234
-        mod_path_only = os.path.dirname(module_path[len(module_dir):])
235
-
236
-        # Find the subcategory for each module
237
-        relative_dir = mod_path_only.split('/')[1]
238
-        sub_category = mod_path_only[len(relative_dir) + 2:]
239
-
240
-        primary_category = ''
241
-        module_categories = []
242
-        # build up the categories that this module belongs to
243
-        for new_cat in mod_path_only.split('/')[1:]:
244
-            if new_cat not in category:
245
-                category[new_cat] = dict()
246
-                category[new_cat]['_modules'] = []
247
-            module_categories.append(new_cat)
248
-            category = category[new_cat]
249
-
250
-        category['_modules'].append(module)
251
-
252
-        # the category we will use in links (so list_of_all_plugins can point to plugins/action_plugins/*'
253
-        if module_categories:
254
-            primary_category = module_categories[0]
255
-
256
-        if not doc:
257
-            display.error("*** ERROR: DOCUMENTATION section missing for %s. ***" % module_path)
258
-            continue
259
-
260
-        if 'options' in doc and doc['options'] is None:
261
-            display.error("*** ERROR: DOCUMENTATION.options must be a dictionary/hash when used. ***")
262
-            pos = getattr(doc, "ansible_pos", None)
263
-            if pos is not None:
264
-                display.error("Module position: %s, %d, %d" % doc.ansible_pos)
265
-            doc['options'] = dict()
266
-
267
-        for key, opt in doc.get('options', {}).items():
268
-            doc['options'][key] = normalize_options(opt)
269
-
270
-        # save all the information
271
-        module_info[module] = {'path': module_path,
272
-                               'source': os.path.relpath(module_path, module_dir),
273
-                               'deprecated': deprecated,
274
-                               'aliases': module_info[module].get('aliases', set()),
275
-                               'aliases_deprecated': module_info[module].get('aliases_deprecated', set()),
276
-                               'metadata': metadata,
277
-                               'doc': doc,
278
-                               'examples': examples,
279
-                               'returndocs': returndocs,
280
-                               'categories': module_categories,
281
-                               'primary_category': primary_category,
282
-                               'sub_category': sub_category,
283
-                               }
284
-
285
-    # keep module tests out of becoming module docs
286
-    if 'test' in categories:
287
-        del categories['test']
288
-
289
-    return module_info, categories
290
-
291
-
292
-def jinja2_environment(template_dir, typ, plugin_type):
293
-
294
-    env = Environment(loader=FileSystemLoader(template_dir),
295
-                      variable_start_string="@{",
296
-                      variable_end_string="}@",
297
-                      trim_blocks=True)
298
-    env.globals['xline'] = rst_xline
299
-
300
-    # Can be removed (and template switched to use namespace) when we no longer need to build
301
-    # with <Jinja-2.10
302
-    env.globals['to_kludge_ns'] = to_kludge_ns
303
-    env.globals['from_kludge_ns'] = from_kludge_ns
304
-    if 'max' not in env.filters:
305
-        # Jinja < 2.10
306
-        env.filters['max'] = do_max
307
-
308
-    if 'tojson' not in env.filters:
309
-        # Jinja < 2.9
310
-        env.filters['tojson'] = json.dumps
311
-
312
-    templates = {}
313
-    if typ == 'rst':
314
-        env.filters['rst_ify'] = rst_ify
315
-        env.filters['html_ify'] = html_ify
316
-        env.filters['fmt'] = rst_fmt
317
-        env.filters['xline'] = rst_xline
318
-        env.filters['documented_type'] = documented_type
319
-        env.tests['list'] = test_list
320
-        templates['plugin'] = env.get_template('plugin.rst.j2')
321
-        templates['plugin_deprecation_stub'] = env.get_template('plugin_deprecation_stub.rst.j2')
322
-
323
-        if plugin_type == 'module':
324
-            name = 'modules'
325
-        else:
326
-            name = 'plugins'
327
-
328
-        templates['category_list'] = env.get_template('%s_by_category.rst.j2' % name)
329
-        templates['support_list'] = env.get_template('%s_by_support.rst.j2' % name)
330
-        templates['list_of_CATEGORY_modules'] = env.get_template('list_of_CATEGORY_%s.rst.j2' % name)
331
-    else:
332
-        raise Exception("Unsupported format type: %s" % typ)
333
-
334
-    return templates
335
-
336
-
337
-def process_version_added(version_added):
338
-    if not isinstance(version_added, string_types):
339
-        return version_added
340
-    if ':' not in version_added:
341
-        return version_added
342
-    # Strip tag from version_added. It suffices to do this here since
343
-    # this is only used for ansible-base, and there the only valid tag
344
-    # is `ansible.builtin:`.
345
-    return version_added[version_added.index(':') + 1:]
346
-
347
-
348
-def too_old(added):
349
-    if not added:
350
-        return False
351
-    try:
352
-        added_tokens = str(added).split(".")
353
-        readded = added_tokens[0] + "." + added_tokens[1]
354
-        added_float = float(readded)
355
-    except ValueError as e:
356
-        warnings.warn("Could not parse %s: %s" % (added, str(e)))
357
-        return False
358
-    return added_float < TOO_OLD_TO_BE_NOTABLE
359
-
360
-
361
-def process_options(module, options, full_key=None):
362
-    option_names = []
363
-    if full_key is None:
364
-        full_key = []
365
-
366
-    if options:
367
-        for (k, v) in iteritems(options):
368
-            # Make sure that "full key" is contained
369
-            full_key_k = full_key + [k]
370
-            v['full_key'] = full_key_k
371
-
372
-            # Error out if there's no description
373
-            if 'description' not in v:
374
-                raise AnsibleError("Missing required description for parameter '%s' in '%s' " % (k, module))
375
-
376
-            # Make sure description is a list of lines for later formatting
377
-            if isinstance(v['description'], string_types):
378
-                v['description'] = [v['description']]
379
-            elif not isinstance(v['description'], (list, tuple)):
380
-                raise AnsibleError("Invalid type for options['%s']['description']."
381
-                                   " Must be string or list of strings.  Got %s" %
382
-                                   (k, type(v['description'])))
383
-
384
-            # Error out if required isn't a boolean (people have been putting
385
-            # information on when something is required in here.  Those need
386
-            # to go in the description instead).
387
-            required_value = v.get('required', False)
388
-            if not isinstance(required_value, bool):
389
-                raise AnsibleError("Invalid required value '%s' for parameter '%s' in '%s' (must be truthy)" % (required_value, k, module))
390
-
391
-            # Strip old version_added information for options
392
-            if 'version_added' in v:
393
-                v['version_added'] = process_version_added(v['version_added'])
394
-                if too_old(v['version_added']):
395
-                    del v['version_added']
396
-
397
-            if 'suboptions' in v and v['suboptions']:
398
-                if isinstance(v['suboptions'], dict):
399
-                    process_options(module, v['suboptions'], full_key=full_key_k)
400
-                elif isinstance(v['suboptions'][0], dict):
401
-                    process_options(module, v['suboptions'][0], full_key=full_key_k)
402
-
403
-            option_names.append(k)
404
-
405
-    option_names.sort()
406
-
407
-    return option_names
408
-
409
-
410
-def process_returndocs(returndocs, full_key=None):
411
-    if full_key is None:
412
-        full_key = []
413
-
414
-    if returndocs:
415
-        for (k, v) in iteritems(returndocs):
416
-            # Make sure that "full key" is contained
417
-            full_key_k = full_key + [k]
418
-            v['full_key'] = full_key_k
419
-
420
-            # Strip old version_added information for options
421
-            if 'version_added' in v:
422
-                v['version_added'] = process_version_added(v['version_added'])
423
-                if too_old(v['version_added']):
424
-                    del v['version_added']
425
-
426
-            # Process suboptions
427
-            suboptions = v.get('contains')
428
-            if suboptions:
429
-                if isinstance(suboptions, dict):
430
-                    process_returndocs(suboptions, full_key=full_key_k)
431
-                elif is_sequence(suboptions):
432
-                    process_returndocs(suboptions[0], full_key=full_key_k)
433
-
434
-
435
-def process_plugins(module_map, templates, outputname, output_dir, ansible_version, plugin_type):
436
-    for module_index, module in enumerate(module_map):
437
-
438
-        show_progress(module_index)
439
-
440
-        fname = module_map[module]['path']
441
-        display.vvvvv(pp.pformat(('process_plugins info: ', module_map[module])))
442
-
443
-        # crash if module is missing documentation and not explicitly hidden from docs index
444
-        if module_map[module]['doc'] is None:
445
-            display.error("%s MISSING DOCUMENTATION" % (fname,))
446
-            _doc = {plugin_type: module,
447
-                    'version_added': '2.4',
448
-                    'filename': fname}
449
-            module_map[module]['doc'] = _doc
450
-            # continue
451
-
452
-        # Going to reference this heavily so make a short name to reference it by
453
-        doc = module_map[module]['doc']
454
-        display.vvvvv(pp.pformat(('process_plugins doc: ', doc)))
455
-
456
-        # add some defaults for plugins that dont have most of the info
457
-        doc['module'] = doc.get('module', module)
458
-        doc['version_added'] = process_version_added(doc.get('version_added', 'historical'))
459
-
460
-        doc['plugin_type'] = plugin_type
461
-
462
-        if module_map[module]['deprecated'] and 'deprecated' not in doc:
463
-            display.warning("%s PLUGIN MISSING DEPRECATION DOCUMENTATION: %s" % (fname, 'deprecated'))
464
-
465
-        required_fields = ('short_description',)
466
-        for field in required_fields:
467
-            if field not in doc:
468
-                display.warning("%s PLUGIN MISSING field '%s'" % (fname, field))
469
-
470
-        not_nullable_fields = ('short_description',)
471
-        for field in not_nullable_fields:
472
-            if field in doc and doc[field] in (None, ''):
473
-                print("%s: WARNING: MODULE field '%s' DOCUMENTATION is null/empty value=%s" % (fname, field, doc[field]))
474
-
475
-        if 'description' in doc:
476
-            if isinstance(doc['description'], string_types):
477
-                doc['description'] = [doc['description']]
478
-            elif not isinstance(doc['description'], (list, tuple)):
479
-                raise AnsibleError("Description must be a string or list of strings.  Got %s"
480
-                                   % type(doc['description']))
481
-        else:
482
-            doc['description'] = []
483
-
484
-        if 'version_added' not in doc:
485
-            # Will never happen, since it has been explicitly inserted above.
486
-            raise AnsibleError("*** ERROR: missing version_added in: %s ***\n" % module)
487
-
488
-        #
489
-        # The present template gets everything from doc so we spend most of this
490
-        # function moving data into doc for the template to reference
491
-        #
492
-
493
-        if module_map[module]['aliases']:
494
-            doc['aliases'] = module_map[module]['aliases']
495
-
496
-        # don't show version added information if it's too old to be called out
497
-        added = 0
498
-        if doc['version_added'] == 'historical':
499
-            del doc['version_added']
500
-        else:
501
-            added = doc['version_added']
502
-
503
-        # Strip old version_added for the module
504
-        if too_old(added):
505
-            del doc['version_added']
506
-
507
-        doc['option_keys'] = process_options(module, doc.get('options'))
508
-        doc['filename'] = fname
509
-        doc['source'] = module_map[module]['source']
510
-        doc['docuri'] = doc['module'].replace('_', '-')
511
-        doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d')
512
-        doc['ansible_version'] = ansible_version
513
-
514
-        # check the 'deprecated' field in doc. We expect a dict potentially with 'why', 'version', and 'alternative' fields
515
-        # examples = module_map[module]['examples']
516
-        # print('\n\n%s: type of examples: %s\n' % (module, type(examples)))
517
-        # if examples and not isinstance(examples, (str, unicode, list)):
518
-        #    raise TypeError('module %s examples is wrong type (%s): %s' % (module, type(examples), examples))
519
-
520
-        # use 'examples' for 'plainexamples' if 'examples' is a string
521
-        if isinstance(module_map[module]['examples'], string_types):
522
-            doc['plainexamples'] = module_map[module]['examples']  # plain text
523
-        else:
524
-            doc['plainexamples'] = ''
525
-
526
-        doc['metadata'] = module_map[module]['metadata']
527
-
528
-        display.vvvvv(pp.pformat(module_map[module]))
529
-        if module_map[module]['returndocs']:
530
-            doc['returndocs'] = module_map[module]['returndocs']
531
-            process_returndocs(doc['returndocs'])
532
-        else:
533
-            doc['returndocs'] = None
534
-
535
-        doc['author'] = doc.get('author', ['UNKNOWN'])
536
-        if isinstance(doc['author'], string_types):
537
-            doc['author'] = [doc['author']]
538
-
539
-        display.v('about to template %s' % module)
540
-        display.vvvvv(pp.pformat(doc))
541
-        try:
542
-            text = templates['plugin'].render(doc)
543
-        except Exception as e:
544
-            display.warning(msg="Could not parse %s due to %s" % (module, e))
545
-            continue
546
-
547
-        if LooseVersion(jinja2.__version__) < LooseVersion('2.10'):
548
-            # jinja2 < 2.10's indent filter indents blank lines.  Cleanup
549
-            text = re.sub(' +\n', '\n', text)
550
-
551
-        write_data(text, output_dir, outputname, module)
552
-
553
-        # Create deprecation stub pages for deprecated aliases
554
-        if module_map[module]['aliases']:
555
-            for alias in module_map[module]['aliases']:
556
-                if alias in module_map[module]['aliases_deprecated']:
557
-                    doc['alias'] = alias
558
-
559
-                    display.v('about to template %s (deprecation alias %s)' % (module, alias))
560
-                    display.vvvvv(pp.pformat(doc))
561
-                    try:
562
-                        text = templates['plugin_deprecation_stub'].render(doc)
563
-                    except Exception as e:
564
-                        display.warning(msg="Could not parse %s (deprecation alias %s) due to %s" % (module, alias, e))
565
-                        continue
566
-
567
-                    if LooseVersion(jinja2.__version__) < LooseVersion('2.10'):
568
-                        # jinja2 < 2.10's indent filter indents blank lines.  Cleanup
569
-                        text = re.sub(' +\n', '\n', text)
570
-
571
-                    write_data(text, output_dir, outputname, alias)
572
-
573
-
574
-def process_categories(plugin_info, categories, templates, output_dir, output_name, plugin_type):
575
-    # For some reason, this line is changing plugin_info:
576
-    # text = templates['list_of_CATEGORY_modules'].render(template_data)
577
-    # To avoid that, make a deepcopy of the data.
578
-    # We should track that down and fix it at some point in the future.
579
-    plugin_info = deepcopy(plugin_info)
580
-    for category in sorted(categories.keys()):
581
-        module_map = categories[category]
582
-        category_filename = output_name % category
583
-
584
-        display.display("*** recording category %s in %s ***" % (category, category_filename))
585
-
586
-        # start a new category file
587
-
588
-        category_name = category.replace("_", " ")
589
-        category_title = category_name.title()
590
-
591
-        subcategories = dict((k, v) for k, v in module_map.items() if k != '_modules')
592
-        template_data = {'title': category_title,
593
-                         'category_name': category_name,
594
-                         'category': module_map,
595
-                         'subcategories': subcategories,
596
-                         'module_info': plugin_info,
597
-                         'plugin_type': plugin_type
598
-                         }
599
-
600
-        text = templates['list_of_CATEGORY_modules'].render(template_data)
601
-        write_data(text, output_dir, category_filename)
602
-
603
-
604
-def process_support_levels(plugin_info, categories, templates, output_dir, plugin_type):
605
-    supported_by = {'Ansible Core Team': {'slug': 'core_supported',
606
-                                          'modules': [],
607
-                                          'output': 'core_maintained.rst',
608
-                                          'blurb': "These are :doc:`modules maintained by the"
609
-                                                   " Ansible Core Team<core_maintained>` and will always ship"
610
-                                                   " with Ansible itself."},
611
-                    'Ansible Network Team': {'slug': 'network_supported',
612
-                                             'modules': [],
613
-                                             'output': 'network_maintained.rst',
614
-                                             'blurb': "These are :doc:`modules maintained by the"
615
-                                                      " Ansible Network Team<network_maintained>` in"
616
-                                                      " a relationship similar to how the Ansible Core Team"
617
-                                                      " maintains the Core modules."},
618
-                    'Ansible Partners': {'slug': 'certified_supported',
619
-                                         'modules': [],
620
-                                         'output': 'partner_maintained.rst',
621
-                                         'blurb': """
622
-Some examples of :doc:`Certified Modules<partner_maintained>` are those submitted by other
623
-companies. Maintainers of these types of modules must watch for any issues reported or pull requests
624
-raised against the module.
625
-
626
-The Ansible Core Team will review all modules becoming certified.  Core committers will review
627
-proposed changes to existing Certified Modules once the community maintainers of the module have
628
-approved the changes. Core committers will also ensure that any issues that arise due to Ansible
629
-engine changes will be remediated.  Also, it is strongly recommended (but not presently required)
630
-for these types of modules to have unit tests.
631
-
632
-These modules are currently shipped with Ansible, but might be shipped separately in the future.
633
-"""},
634
-                    'Ansible Community': {'slug': 'community_supported',
635
-                                          'modules': [],
636
-                                          'output': 'community_maintained.rst',
637
-                                          'blurb': """
638
-These are :doc:`modules maintained by the Ansible Community<community_maintained>`.  They **are
639
-not** supported by the Ansible Core Team or by companies/partners associated to the module.
640
-
641
-They are still fully usable, but the response rate to issues is purely up to the community.  Best
642
-effort support will be provided but is not covered under any support contracts.
643
-
644
-These modules are currently shipped with Ansible, but will most likely be shipped separately in the future.
645
-                                          """},
646
-                    }
647
-
648
-    # only gen support pages for modules for now, need to split and namespace templates and generated docs
649
-    if plugin_type == 'plugins':
650
-        return
651
-    # Separate the modules by support_level
652
-    for module, info in plugin_info.items():
653
-        if not info.get('metadata', None):
654
-            display.warning('no metadata for %s' % module)
655
-            continue
656
-        if info['metadata']['supported_by'] == 'core':
657
-            supported_by['Ansible Core Team']['modules'].append(module)
658
-        elif info['metadata']['supported_by'] == 'network':
659
-            supported_by['Ansible Network Team']['modules'].append(module)
660
-        elif info['metadata']['supported_by'] == 'certified':
661
-            supported_by['Ansible Partners']['modules'].append(module)
662
-        elif info['metadata']['supported_by'] == 'community':
663
-            supported_by['Ansible Community']['modules'].append(module)
664
-        else:
665
-            raise AnsibleError('Unknown supported_by value: %s' % info['metadata']['supported_by'])
666
-
667
-    # Render the module lists based on category and subcategory
668
-    for maintainers, data in supported_by.items():
669
-        subcategories = {}
670
-        subcategories[''] = {}
671
-        for module in data['modules']:
672
-            new_cat = plugin_info[module]['sub_category']
673
-            category = plugin_info[module]['primary_category']
674
-            if category not in subcategories:
675
-                subcategories[category] = {}
676
-                subcategories[category][''] = {}
677
-                subcategories[category]['']['_modules'] = []
678
-            if new_cat not in subcategories[category]:
679
-                subcategories[category][new_cat] = {}
680
-                subcategories[category][new_cat]['_modules'] = []
681
-            subcategories[category][new_cat]['_modules'].append(module)
682
-
683
-        template_data = {'maintainers': maintainers,
684
-                         'subcategories': subcategories,
685
-                         'modules': data['modules'],
686
-                         'slug': data['slug'],
687
-                         'module_info': plugin_info,
688
-                         'plugin_type': plugin_type
689
-                         }
690
-        text = templates['support_list'].render(template_data)
691
-        write_data(text, output_dir, data['output'])
692
-
693
-
694
-def validate_options(options):
695
-    ''' validate option parser options '''
696
-
697
-    if not options.module_dir:
698
-        sys.exit("--module-dir is required")
699
-    if not os.path.exists(options.module_dir):
700
-        sys.exit("--module-dir does not exist: %s" % options.module_dir)
701
-    if not options.template_dir:
702
-        sys.exit("--template-dir must be specified")
703
-
704
-
705
-class DocumentPlugins(Command):
706
-    name = 'document-plugins'
707
-
708
-    @classmethod
709
-    def init_parser(cls, add_parser):
710
-        parser = add_parser(cls.name, description='Generate module documentation from metadata')
711
-
712
-        parser.add_argument("-A", "--ansible-version", action="store", dest="ansible_version",
713
-                            default="unknown", help="Ansible version number")
714
-        parser.add_argument("-M", "--module-dir", action="store", dest="module_dir",
715
-                            default=MODULEDIR, help="Ansible library path")
716
-        parser.add_argument("-P", "--plugin-type", action="store", dest="plugin_type",
717
-                            default='module', help="The type of plugin (module, lookup, etc)")
718
-        parser.add_argument("-T", "--template-dir", action="append", dest="template_dir",
719
-                            help="directory containing Jinja2 templates")
720
-        parser.add_argument("-t", "--type", action='store', dest='type', choices=['rst'],
721
-                            default='rst', help="Document type")
722
-        parser.add_argument("-o", "--output-dir", action="store", dest="output_dir", default=None,
723
-                            help="Output directory for module files")
724
-        parser.add_argument("-I", "--includes-file", action="store", dest="includes_file",
725
-                            default=None, help="Create a file containing list of processed modules")
726
-        parser.add_argument("-l", "--limit-to-modules", '--limit-to', action="store",
727
-                            dest="limit_to", default=None, help="Limit building module documentation"
728
-                            " to comma-separated list of plugins. Specify non-existing plugin name"
729
-                            " for no plugins.")
730
-        parser.add_argument('-V', action='version', help='Show version number and exit')
731
-        parser.add_argument('-v', '--verbose', dest='verbosity', default=0, action="count",
732
-                            help="verbose mode (increase number of 'v's for more)")
733
-
734
-    @staticmethod
735
-    def main(args):
736
-        if not args.template_dir:
737
-            args.template_dir = ["hacking/templates"]
738
-        validate_options(args)
739
-        display.verbosity = args.verbosity
740
-        plugin_type = args.plugin_type
741
-
742
-        display.display("Evaluating %s files..." % plugin_type)
743
-
744
-        # prep templating
745
-        templates = jinja2_environment(args.template_dir, args.type, plugin_type)
746
-
747
-        # set file/directory structure
748
-        if plugin_type == 'module':
749
-            # trim trailing s off of plugin_type for plugin_type=='modules'. ie 'copy_module.rst'
750
-            outputname = '%s_' + '%s.rst' % plugin_type
751
-            output_dir = args.output_dir
752
-        else:
753
-            # for plugins, just use 'ssh.rst' vs 'ssh_module.rst'
754
-            outputname = '%s.rst'
755
-            output_dir = '%s/plugins/%s' % (args.output_dir, plugin_type)
756
-
757
-        display.vv('output name: %s' % outputname)
758
-        display.vv('output dir: %s' % output_dir)
759
-
760
-        # Convert passed-in limit_to to None or list of modules.
761
-        if args.limit_to is not None:
762
-            args.limit_to = [s.lower() for s in args.limit_to.split(",")]
763
-
764
-        plugin_info, categories = get_plugin_info(args.module_dir, limit_to=args.limit_to, verbose=(args.verbosity > 0))
765
-
766
-        categories['all'] = {'_modules': plugin_info.keys()}
767
-
768
-        if display.verbosity >= 3:
769
-            display.vvv(pp.pformat(categories))
770
-        if display.verbosity >= 5:
771
-            display.vvvvv(pp.pformat(plugin_info))
772
-
773
-        # Transform the data
774
-        if args.type == 'rst':
775
-            display.v('Generating rst')
776
-            for key, record in plugin_info.items():
777
-                display.vv(key)
778
-                if display.verbosity >= 5:
779
-                    display.vvvvv(pp.pformat(('record', record)))
780
-                if record.get('doc', None):
781
-                    short_desc = record['doc']['short_description'].rstrip('.')
782
-                    if short_desc is None:
783
-                        display.warning('short_description for %s is None' % key)
784
-                        short_desc = ''
785
-                    record['doc']['short_description'] = rst_ify(short_desc)
786
-
787
-        if plugin_type == 'module':
788
-            display.v('Generating Categories')
789
-            # Write module master category list
790
-            category_list_text = templates['category_list'].render(categories=sorted(categories.keys()))
791
-            category_index_name = '%ss_by_category.rst' % plugin_type
792
-            write_data(category_list_text, output_dir, category_index_name)
793
-
794
-        # Render all the individual plugin pages
795
-        display.v('Generating plugin pages')
796
-        process_plugins(plugin_info, templates, outputname, output_dir, args.ansible_version, plugin_type)
797
-
798
-        # Render all the categories for modules
799
-        if plugin_type == 'module':
800
-            display.v('Generating Category lists')
801
-            category_list_name_template = 'list_of_%s_' + '%ss.rst' % plugin_type
802
-            process_categories(plugin_info, categories, templates, output_dir, category_list_name_template, plugin_type)
803
-
804
-            # Render all the categories for modules
805
-            process_support_levels(plugin_info, categories, templates, output_dir, plugin_type)
806
-
807
-        return 0
808 1
deleted file mode 100644
809 2
deleted file mode 100644
... ...
@@ -1,100 +0,0 @@
1
-# Copyright: (c) 2019, Ansible Project
2
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3
-
4
-# Make coding more python3-ish
5
-from __future__ import (absolute_import, division, print_function)
6
-__metaclass__ = type
7
-
8
-import re
9
-
10
-try:
11
-    from html import escape as html_escape
12
-except ImportError:
13
-    # Python-3.2 or later
14
-    import cgi
15
-
16
-    def html_escape(text, quote=True):
17
-        return cgi.escape(text, quote)
18
-
19
-from jinja2.runtime import Undefined
20
-
21
-from ansible.errors import AnsibleError
22
-from ansible.module_utils._text import to_text
23
-from ansible.module_utils.six import string_types
24
-
25
-
26
-_ITALIC = re.compile(r"I\(([^)]+)\)")
27
-_BOLD = re.compile(r"B\(([^)]+)\)")
28
-_MODULE = re.compile(r"M\(([^)]+)\)")
29
-_URL = re.compile(r"U\(([^)]+)\)")
30
-_LINK = re.compile(r"L\(([^)]+), *([^)]+)\)")
31
-_CONST = re.compile(r"C\(([^)]+)\)")
32
-_RULER = re.compile(r"HORIZONTALLINE")
33
-
34
-
35
-def html_ify(text):
36
-    ''' convert symbols like I(this is in italics) to valid HTML '''
37
-
38
-    if not isinstance(text, string_types):
39
-        text = to_text(text)
40
-
41
-    t = html_escape(text)
42
-    t = _ITALIC.sub(r"<em>\1</em>", t)
43
-    t = _BOLD.sub(r"<b>\1</b>", t)
44
-    t = _MODULE.sub(r"<span class='module'>\1</span>", t)
45
-    t = _URL.sub(r"<a href='\1'>\1</a>", t)
46
-    t = _LINK.sub(r"<a href='\2'>\1</a>", t)
47
-    t = _CONST.sub(r"<code>\1</code>", t)
48
-    t = _RULER.sub(r"<hr/>", t)
49
-
50
-    return t.strip()
51
-
52
-
53
-def documented_type(text):
54
-    ''' Convert any python type to a type for documentation '''
55
-
56
-    if isinstance(text, Undefined):
57
-        return '-'
58
-    if text == 'str':
59
-        return 'string'
60
-    if text == 'bool':
61
-        return 'boolean'
62
-    if text == 'int':
63
-        return 'integer'
64
-    if text == 'dict':
65
-        return 'dictionary'
66
-    return text
67
-
68
-
69
-# The max filter was added in Jinja2-2.10.  Until we can require that version, use this
70
-def do_max(seq):
71
-    return max(seq)
72
-
73
-
74
-def rst_ify(text):
75
-    ''' convert symbols like I(this is in italics) to valid restructured text '''
76
-
77
-    try:
78
-        t = _ITALIC.sub(r"*\1*", text)
79
-        t = _BOLD.sub(r"**\1**", t)
80
-        t = _MODULE.sub(r":ref:`\1 <\1_module>`", t)
81
-        t = _LINK.sub(r"`\1 <\2>`_", t)
82
-        t = _URL.sub(r"\1", t)
83
-        t = _CONST.sub(r"``\1``", t)
84
-        t = _RULER.sub(r"------------", t)
85
-    except Exception as e:
86
-        raise AnsibleError("Could not process (%s) : %s" % (text, e))
87
-
88
-    return t
89
-
90
-
91
-def rst_fmt(text, fmt):
92
-    ''' helper for Jinja2 to do format strings '''
93
-
94
-    return fmt % (text)
95
-
96
-
97
-def rst_xline(width, char="="):
98
-    ''' return a restructured text line of a given length '''
99
-
100
-    return char * width
... ...
@@ -11,7 +11,7 @@ import sys
11 11
 def main():
12 12
     base_dir = os.getcwd() + os.path.sep
13 13
     docs_dir = os.path.abspath('docs/docsite')
14
-    cmd = ['make', 'singlehtmldocs']
14
+    cmd = ['make', 'base_singlehtmldocs']
15 15
 
16 16
     sphinx = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=docs_dir)
17 17
     stdout, stderr = sphinx.communicate()
... ...
@@ -3,3 +3,4 @@ pyyaml
3 3
 sphinx
4 4
 sphinx-notfound-page
5 5
 straight.plugin
6
+antsibull
... ...
@@ -19,9 +19,6 @@ hacking/build_library/build_ansible/command_plugins/dump_keywords.py compile-3.5
19 19
 hacking/build_library/build_ansible/command_plugins/generate_man.py compile-2.6!skip # docs build only, 3.6+ required
20 20
 hacking/build_library/build_ansible/command_plugins/generate_man.py compile-2.7!skip # docs build only, 3.6+ required
21 21
 hacking/build_library/build_ansible/command_plugins/generate_man.py compile-3.5!skip # docs build only, 3.6+ required
22
-hacking/build_library/build_ansible/command_plugins/plugin_formatter.py compile-2.6!skip # docs build only, 3.6+ required
23
-hacking/build_library/build_ansible/command_plugins/plugin_formatter.py compile-2.7!skip # docs build only, 3.6+ required
24
-hacking/build_library/build_ansible/command_plugins/plugin_formatter.py compile-3.5!skip # docs build only, 3.6+ required
25 22
 hacking/build_library/build_ansible/command_plugins/porting_guide.py compile-2.6!skip # release process only, 3.6+ required
26 23
 hacking/build_library/build_ansible/command_plugins/porting_guide.py compile-2.7!skip # release process only, 3.6+ required
27 24
 hacking/build_library/build_ansible/command_plugins/porting_guide.py compile-3.5!skip # release process only, 3.6+ required