Skip to content

Commit 4af4844

Browse files
authored
Fix lint-collection-docs --plugin-docs subcommand (#47)
* Fix collection copier. * Prevent errors to be dumped twice. * Also use --plugin-docs in tests. * Improve file name guessing. * Add changelog fragment.
1 parent 667ad1c commit 4af4844

File tree

4 files changed

+81
-49
lines changed

4 files changed

+81
-49
lines changed

.github/workflows/antsibull-docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ jobs:
8080

8181
- name: Lint collection docs
8282
run: |
83-
poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/community/docker
83+
poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/community/docker --plugin-docs
8484
poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/community/crypto
8585
poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/sensu/sensu_go
8686
working-directory: antsibull-docs
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bugfixes:
2+
- "Make ``lint-collection-docs --plugin-docs`` subcommand actually work (https://github.com/ansible-community/antsibull-docs/pull/47)."

src/antsibull_docs/lint_plugin_docs.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@
3737
)
3838
from .jinja2.environment import doc_environment
3939
from .utils.collection_name_transformer import CollectionNameTransformer
40-
from .write_docs import create_plugin_rst
40+
from .write_docs import (
41+
create_plugin_rst,
42+
guess_relative_filename,
43+
has_broken_docs,
44+
)
4145
from .rstcheck import check_rst_content
4246

4347

@@ -48,7 +52,7 @@ def __init__(self):
4852
self.dir = None
4953

5054
def __enter__(self):
51-
if self.dir is None:
55+
if self.dir is not None:
5256
raise AssertionError('Collection copier already initialized')
5357
self.dir = os.path.realpath(tempfile.mkdtemp(prefix='antsibull-docs-'))
5458
return self
@@ -107,17 +111,6 @@ def _lint_collection_plugin_docs(collections_dir: str, collection_name: str,
107111
collection_to_plugin_info = get_collection_contents(plugin_contents)
108112
for collection in collection_metadata:
109113
collection_to_plugin_info[collection] # pylint:disable=pointless-statement
110-
# Collect non-fatal errors
111-
result = []
112-
for plugin_type, plugins in sorted(nonfatal_errors.items()):
113-
for plugin_name, errors in sorted(plugins.items()):
114-
for error in errors:
115-
result.append((
116-
os.path.join(original_path_to_collection, 'plugins', plugin_type, plugin_name),
117-
0,
118-
0,
119-
error,
120-
))
121114
# Compose RST files and check for errors
122115
# Setup the jinja environment
123116
env = doc_environment(
@@ -129,21 +122,38 @@ def _lint_collection_plugin_docs(collections_dir: str, collection_name: str,
129122
role_tmpl = env.get_template('role.rst.j2')
130123
error_tmpl = env.get_template('plugin-error.rst.j2')
131124

125+
result = []
132126
for collection_name_, plugins_by_type in collection_to_plugin_info.items():
133127
for plugin_type, plugins_dict in plugins_by_type.items():
134128
plugin_type_tmpl = plugin_tmpl
135129
if plugin_type == 'role':
136130
plugin_type_tmpl = role_tmpl
137131
for plugin_short_name, dummy_ in plugins_dict.items():
138132
plugin_name = '.'.join((collection_name_, plugin_short_name))
133+
plugin_record = new_plugin_info[plugin_type].get(plugin_name) or {}
134+
filename = os.path.join(
135+
original_path_to_collection,
136+
guess_relative_filename(
137+
plugin_record,
138+
plugin_short_name,
139+
plugin_type,
140+
collection_name_,
141+
collection_metadata[collection_name_]))
142+
if has_broken_docs(plugin_record, plugin_type):
143+
result.append((filename, 0, 0, 'Did not return correct DOCUMENTATION'))
144+
for error in nonfatal_errors[plugin_type][plugin_name]:
145+
result.append((filename, 0, 0, error))
139146
rst_content = create_plugin_rst(
140-
collection_name_, collection_metadata[collection_name_],
147+
collection_name_,
148+
collection_metadata[collection_name_],
141149
link_data[collection_name_],
142-
plugin_short_name, plugin_type,
143-
new_plugin_info[plugin_type].get(plugin_name) or {},
150+
plugin_short_name,
151+
plugin_type,
152+
plugin_record,
144153
nonfatal_errors[plugin_type][plugin_name],
145154
plugin_type_tmpl, error_tmpl,
146155
use_html_blobs=False,
156+
log_errors=False,
147157
)
148158
path = os.path.join(
149159
original_path_to_collection, 'plugins', plugin_type,

src/antsibull_docs/write_docs.py

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,50 @@ def follow_relative_links(path: str) -> str:
9292
path = os.path.join(os.path.dirname(path), link)
9393

9494

95+
def has_broken_docs(plugin_record: t.Mapping[str, t.Any], plugin_type: str) -> bool:
96+
"""
97+
Determine whether the plugin record is completely broken or not.
98+
"""
99+
expected_fields = ('entry_points',) if plugin_type == 'role' else ('doc', 'examples', 'return')
100+
return not plugin_record or not all(field in plugin_record for field in expected_fields)
101+
102+
103+
def guess_relative_filename(plugin_record: t.Mapping[str, t.Any],
104+
plugin_short_name: str,
105+
plugin_type: str,
106+
collection_name: str,
107+
collection_meta: AnsibleCollectionMetadata) -> str:
108+
"""
109+
Make an educated guess on the documentation source file.
110+
"""
111+
if plugin_record and plugin_record.get('doc') and plugin_record['doc'].get('filename'):
112+
filename = follow_relative_links(plugin_record['doc']['filename'])
113+
return os.path.relpath(filename, collection_meta.path)
114+
if plugin_type == 'role':
115+
return f"roles/{plugin_short_name}/meta/argument_specs.yml"
116+
plugin_dir = (
117+
# Modules in ansible-core:
118+
'modules' if plugin_type == 'module' and collection_name == 'ansible.builtin' else
119+
# Modules in collections:
120+
'plugins/modules' if plugin_type == 'module' else
121+
# Plugins in ansible-core or collections:
122+
'plugins/' + plugin_type
123+
)
124+
# Guess path inside collection tree
125+
return f"{plugin_dir}/{plugin_short_name}.py"
126+
127+
95128
def create_plugin_rst(collection_name: str,
96129
collection_meta: AnsibleCollectionMetadata,
97130
collection_links: CollectionLinks,
98-
plugin_short_name: str, plugin_type: str,
99-
plugin_record: t.Dict[str, t.Any], nonfatal_errors: t.Sequence[str],
131+
plugin_short_name: str,
132+
plugin_type: str,
133+
plugin_record: t.Dict[str, t.Any],
134+
nonfatal_errors: t.Sequence[str],
100135
plugin_tmpl: Template, error_tmpl: Template,
101136
use_html_blobs: bool = False,
102-
for_official_docsite: bool = False) -> str:
137+
for_official_docsite: bool = False,
138+
log_errors: bool = True) -> str:
103139
"""
104140
Create the rst page for one plugin.
105141
@@ -118,6 +154,7 @@ def create_plugin_rst(collection_name: str,
118154
tables instead of using RST tables.
119155
:kwarg for_official_docsite: Default False. Set to True to use wording specific for the
120156
official docsite on docs.ansible.com.
157+
:kwarg log_errors: Default True. Set to False to avoid errors to be logged.
121158
"""
122159
flog = mlog.fields(func='create_plugin_rst')
123160
flog.debug('Enter')
@@ -126,38 +163,21 @@ def create_plugin_rst(collection_name: str,
126163

127164
edit_on_github_url = None
128165
eog = collection_links.edit_on_github
129-
if eog and plugin_type != 'role':
130-
gh_plugin_dir = (
131-
# Modules in ansible-core:
132-
'modules' if plugin_type == 'module' and collection_name == 'ansible.builtin' else
133-
# Modules in collections:
134-
'plugins/modules' if plugin_type == 'module' else
135-
# Plugins in ansible-core or collections:
136-
'plugins/' + plugin_type
137-
)
138-
# Guess path inside collection tree
139-
gh_path = f"{gh_plugin_dir}/{plugin_short_name}.py"
140-
# If we have more precise information, use that!
141-
if plugin_record and plugin_record.get('doc') and plugin_record['doc'].get('filename'):
142-
filename = follow_relative_links(plugin_record['doc']['filename'])
143-
gh_path = os.path.relpath(filename, collection_meta.path)
144-
# Compose path
166+
if eog:
167+
# Compose Edit on GitHub URL
168+
gh_path = guess_relative_filename(
169+
plugin_record, plugin_short_name, plugin_type, collection_name, collection_meta)
145170
edit_on_github_url = (
146171
f"https://github.com/{eog.repository}/edit/{eog.branch}/{eog.path_prefix}{gh_path}"
147172
)
148-
if eog and plugin_type == 'role':
149-
edit_on_github_url = (
150-
f"https://github.com/{eog.repository}/edit/{eog.branch}/{eog.path_prefix}"
151-
f"roles/{plugin_short_name}/meta/argument_specs.yml"
152-
)
153173

154-
expected_fields = ('entry_points',) if plugin_type == 'role' else ('doc', 'examples', 'return')
155-
if not plugin_record or not all(field in plugin_record for field in expected_fields):
156-
flog.fields(plugin_type=plugin_type,
157-
plugin_name=plugin_name,
158-
nonfatal_errors=nonfatal_errors
159-
).error('{plugin_name} did not return correct DOCUMENTATION. An error page'
160-
' will be generated.', plugin_name=plugin_name)
174+
if has_broken_docs(plugin_record, plugin_type):
175+
if log_errors:
176+
flog.fields(plugin_type=plugin_type,
177+
plugin_name=plugin_name,
178+
nonfatal_errors=nonfatal_errors
179+
).error('{plugin_name} did not return correct DOCUMENTATION. An error'
180+
' page will be generated.', plugin_name=plugin_name)
161181
plugin_contents = _render_template(
162182
error_tmpl,
163183
plugin_name + '_' + plugin_type,
@@ -172,7 +192,7 @@ def create_plugin_rst(collection_name: str,
172192
for_official_docsite=for_official_docsite,
173193
)
174194
else:
175-
if nonfatal_errors:
195+
if log_errors and nonfatal_errors:
176196
flog.fields(plugin_type=plugin_type,
177197
plugin_name=plugin_name,
178198
nonfatal_errors=nonfatal_errors

0 commit comments

Comments
 (0)