ccbfdec334
* excludes scenario guides from core docs, splits porting guides and roadmaps, symlinks indices to create index.html pages, and adds .gitignore entries for conf.py and the toplevel index.rst files generated by the docs build This solution builds three types of docs: * ansible-2.10 and earlier: all the docs. Handle this via `make webdocs ANSIBLE_VERSION=2.10` * ansible-3 and later: a subset of the docs for the ansible package. Handle this via `make webdocs ANSIBLE_VERSION=3` (change the ANSIBLE_VERSION to match the version being built for. * ansible-core: a subset of the docs for the ansible-core package. Handle this via `make coredocs`. * `make webdocs` now always builds all the collection docs * Use `make coredocs` to limit it to core plugins only * The user specifies the desired version. If no ANSIBLE_VERSION is specified, build plugins for the latest release of ansible Co-authored-by: Toshio Kuratomi <a.badger@gmail.com> Co-authored-by: Matt Clay <matt@mystile.com>
155 lines
5.1 KiB
Python
Executable file
155 lines
5.1 KiB
Python
Executable file
#!/usr/bin/env python
|
|
from __future__ import (absolute_import, division, print_function)
|
|
__metaclass__ = type
|
|
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
def main():
|
|
base_dir = os.getcwd() + os.path.sep
|
|
docs_dir = os.path.abspath('docs/docsite')
|
|
|
|
# TODO: Remove this temporary hack to constrain 'cryptography' when we have
|
|
# a better story for dealing with it.
|
|
tmpfd, tmp = tempfile.mkstemp()
|
|
requirements_txt = os.path.join(base_dir, 'requirements.txt')
|
|
shutil.copy2(requirements_txt, tmp)
|
|
lines = []
|
|
with open(requirements_txt, 'r') as f:
|
|
for line in f.readlines():
|
|
if line.strip() == 'cryptography':
|
|
line = 'cryptography < 3.4\n'
|
|
lines.append(line)
|
|
|
|
with open(requirements_txt, 'w') as f:
|
|
f.writelines(lines)
|
|
|
|
try:
|
|
cmd = ['make', 'core_singlehtmldocs']
|
|
sphinx = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=docs_dir)
|
|
stdout, stderr = sphinx.communicate()
|
|
finally:
|
|
shutil.move(tmp, requirements_txt)
|
|
|
|
stdout = stdout.decode('utf-8')
|
|
stderr = stderr.decode('utf-8')
|
|
|
|
if sphinx.returncode != 0:
|
|
sys.stderr.write("Command '%s' failed with status code: %d\n" % (' '.join(cmd), sphinx.returncode))
|
|
|
|
if stdout.strip():
|
|
stdout = simplify_stdout(stdout)
|
|
|
|
sys.stderr.write("--> Standard Output\n")
|
|
sys.stderr.write("%s\n" % stdout.strip())
|
|
|
|
if stderr.strip():
|
|
sys.stderr.write("--> Standard Error\n")
|
|
sys.stderr.write("%s\n" % stderr.strip())
|
|
|
|
sys.exit(1)
|
|
|
|
with open('docs/docsite/rst_warnings', 'r') as warnings_fd:
|
|
output = warnings_fd.read().strip()
|
|
lines = output.splitlines()
|
|
|
|
known_warnings = {
|
|
'block-quote-missing-blank-line': r'^Block quote ends without a blank line; unexpected unindent.$',
|
|
'literal-block-lex-error': r'^Could not lex literal_block as "[^"]*". Highlighting skipped.$',
|
|
'duplicate-label': r'^duplicate label ',
|
|
'undefined-label': r'undefined label: ',
|
|
'unknown-document': r'unknown document: ',
|
|
'toc-tree-missing-document': r'toctree contains reference to nonexisting document ',
|
|
'reference-target-not-found': r'[^ ]* reference target not found: ',
|
|
'not-in-toc-tree': r"document isn't included in any toctree$",
|
|
'unexpected-indentation': r'^Unexpected indentation.$',
|
|
'definition-list-missing-blank-line': r'^Definition list ends without a blank line; unexpected unindent.$',
|
|
'explicit-markup-missing-blank-line': r'Explicit markup ends without a blank line; unexpected unindent.$',
|
|
'toc-tree-glob-pattern-no-match': r"^toctree glob pattern '[^']*' didn't match any documents$",
|
|
'unknown-interpreted-text-role': '^Unknown interpreted text role "[^"]*".$',
|
|
}
|
|
|
|
for line in lines:
|
|
match = re.search('^(?P<path>[^:]+):((?P<line>[0-9]+):)?((?P<column>[0-9]+):)? (?P<level>WARNING|ERROR): (?P<message>.*)$', line)
|
|
|
|
if not match:
|
|
path = 'docs/docsite/rst/index.rst'
|
|
lineno = 0
|
|
column = 0
|
|
code = 'unknown'
|
|
message = line
|
|
|
|
# surface unknown lines while filtering out known lines to avoid excessive output
|
|
print('%s:%d:%d: %s: %s' % (path, lineno, column, code, message))
|
|
continue
|
|
|
|
path = match.group('path')
|
|
lineno = int(match.group('line') or 0)
|
|
column = int(match.group('column') or 0)
|
|
level = match.group('level').lower()
|
|
message = match.group('message')
|
|
|
|
path = os.path.abspath(path)
|
|
|
|
if path.startswith(base_dir):
|
|
path = path[len(base_dir):]
|
|
|
|
if path.startswith('rst/'):
|
|
path = 'docs/docsite/' + path # fix up paths reported relative to `docs/docsite/`
|
|
|
|
if level == 'warning':
|
|
code = 'warning'
|
|
|
|
for label, pattern in known_warnings.items():
|
|
if re.search(pattern, message):
|
|
code = label
|
|
break
|
|
else:
|
|
code = 'error'
|
|
|
|
print('%s:%d:%d: %s: %s' % (path, lineno, column, code, message))
|
|
|
|
|
|
def simplify_stdout(value):
|
|
"""Simplify output by omitting earlier 'rendering: ...' messages."""
|
|
lines = value.strip().splitlines()
|
|
|
|
rendering = []
|
|
keep = []
|
|
|
|
def truncate_rendering():
|
|
"""Keep last rendering line (if any) with a message about omitted lines as needed."""
|
|
if not rendering:
|
|
return
|
|
|
|
notice = rendering[-1]
|
|
|
|
if len(rendering) > 1:
|
|
notice += ' (%d previous rendering line(s) omitted)' % (len(rendering) - 1)
|
|
|
|
keep.append(notice)
|
|
# Could change to rendering.clear() if we do not support python2
|
|
rendering[:] = []
|
|
|
|
for line in lines:
|
|
if line.startswith('rendering: '):
|
|
rendering.append(line)
|
|
continue
|
|
|
|
truncate_rendering()
|
|
keep.append(line)
|
|
|
|
truncate_rendering()
|
|
|
|
result = '\n'.join(keep)
|
|
|
|
return result
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|