2023-03-09 12:18:50 -05:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import html
|
|
|
|
import os
|
|
|
|
import pathlib
|
|
|
|
import re
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
|
2023-05-09 09:45:35 -05:00
|
|
|
page_contents = sys.stdin.read()
|
2023-03-09 12:18:50 -05:00
|
|
|
|
|
|
|
base_url = "https://projects.blender.org"
|
|
|
|
local_url = "http://localhost:3000"
|
|
|
|
placeholder_url = "https://placeholder.org"
|
|
|
|
|
|
|
|
# Gitea sets this environment variable with the URL prefix for the current file.
|
|
|
|
gitea_prefix = os.environ.get("GITEA_PREFIX_SRC", "")
|
|
|
|
if gitea_prefix.startswith(base_url):
|
|
|
|
gitea_prefix = gitea_prefix[len(base_url):]
|
|
|
|
if gitea_prefix.startswith(local_url):
|
|
|
|
gitea_prefix = gitea_prefix[len(local_url):]
|
|
|
|
|
|
|
|
if len(gitea_prefix):
|
2023-04-06 12:36:58 -05:00
|
|
|
path_tokens = gitea_prefix.strip('/').split('/')
|
|
|
|
org, repo, view, ref, branch = path_tokens[:5]
|
2023-03-09 12:18:50 -05:00
|
|
|
|
|
|
|
doc_url = f"{base_url}/{org}/{repo}/{view}/{ref}/{branch}"
|
2023-04-06 12:36:58 -05:00
|
|
|
image_url = f"{base_url}/{org}/{repo}/media/{ref}/{branch}"
|
|
|
|
|
|
|
|
# Hardcoded exception for blender-manual, that has links relative
|
|
|
|
# to manual/ folder.
|
|
|
|
if len(path_tokens) > 5 and path_tokens[5] == 'manual':
|
|
|
|
doc_url += "/manual"
|
|
|
|
image_url += "/manual"
|
2023-03-09 12:18:50 -05:00
|
|
|
else:
|
|
|
|
doc_url = ""
|
|
|
|
image_url = ""
|
|
|
|
|
|
|
|
# Set up temporary directory with sphinx configuration.
|
2023-05-09 09:45:35 -05:00
|
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
2023-03-09 12:18:50 -05:00
|
|
|
work_dir = pathlib.Path(tmp_dir) / "work"
|
|
|
|
|
|
|
|
script_dir = pathlib.Path(__file__).parent.resolve()
|
|
|
|
shutil.copytree(script_dir / "template", work_dir)
|
|
|
|
page_filepath = work_dir / "contents.rst"
|
|
|
|
|
|
|
|
# Turn links into external links since internal links are not found and stripped.
|
2023-04-06 12:36:58 -05:00
|
|
|
def path_to_label(path):
|
|
|
|
path = path.removesuffix('/index')
|
|
|
|
return path.split('/')[-1].replace('_', ' ').replace('-', ' ').capitalize()
|
|
|
|
def doc_label_link(matchobj):
|
2023-03-09 12:18:50 -05:00
|
|
|
return f"`{matchobj.group(1)}<{doc_url}/{matchobj.group(2).strip('/')}.rst>`_"
|
2023-04-06 12:36:58 -05:00
|
|
|
def doc_link(matchobj):
|
|
|
|
return f"`{path_to_label(matchobj.group(1))} <{doc_url}/{matchobj.group(1).strip('/')}.rst>`_"
|
|
|
|
def ref_label_link(matchobj):
|
2023-03-09 12:18:50 -05:00
|
|
|
return f"`{matchobj.group(1)} <{placeholder_url}>`_"
|
2023-04-06 12:36:58 -05:00
|
|
|
def ref_link(matchobj):
|
|
|
|
return f"`{path_to_label(matchobj.group(1))} <{placeholder_url}>`_"
|
2023-03-09 12:18:50 -05:00
|
|
|
def term_link(matchobj):
|
|
|
|
return f"`{matchobj.group(1)} <{placeholder_url}>`_"
|
|
|
|
def figure_link(matchobj):
|
|
|
|
return f"figure:: {image_url}/{matchobj.group(1).strip('/')}"
|
|
|
|
def image_link(matchobj):
|
|
|
|
return f"image:: {image_url}/{matchobj.group(1).strip('/')}"
|
|
|
|
|
2023-04-06 12:36:58 -05:00
|
|
|
page_contents = re.sub(":doc:`/(.+?)`", doc_link, page_contents)
|
|
|
|
page_contents = re.sub(":doc:`(.+?)<(.+?)>`", doc_label_link, page_contents)
|
|
|
|
page_contents = re.sub(":ref:`(.+?)<(.+?)>`", ref_label_link, page_contents)
|
|
|
|
page_contents = re.sub(":ref:`([\w\s-]+?)`", ref_link, page_contents)
|
|
|
|
page_contents = re.sub(":term:`(.+?)<(.+?)>`", term_link, page_contents)
|
|
|
|
page_contents = re.sub(":term:`([\w\s-]+?)`", term_link, page_contents)
|
|
|
|
page_contents = re.sub("figure:: (.+?)", figure_link, page_contents)
|
|
|
|
page_contents = re.sub("image:: (.+?)", image_link, page_contents)
|
2023-03-09 12:18:50 -05:00
|
|
|
|
|
|
|
# Disable include directives and raw for security. They are already disabled
|
|
|
|
# by docutils.py, this is just to be extra careful.
|
|
|
|
def include_directive(matchobj):
|
2023-03-13 18:58:31 -05:00
|
|
|
return f"warning:: include not available in preview: {html.escape(matchobj.group(1))}"
|
2023-03-09 12:18:50 -05:00
|
|
|
def raw_directive(matchobj):
|
2023-03-13 18:58:31 -05:00
|
|
|
return f"warning:: raw not available in preview: {html.escape(matchobj.group(1))}"
|
|
|
|
page_contents = re.sub("literalinclude::(.*)", include_directive, page_contents)
|
2023-03-09 12:18:50 -05:00
|
|
|
page_contents = re.sub("include::(.*)", include_directive, page_contents)
|
|
|
|
page_contents = re.sub("raw::(.*)", raw_directive, page_contents)
|
|
|
|
|
|
|
|
page_filepath.write_text(page_contents)
|
|
|
|
|
|
|
|
# Debug processed RST
|
|
|
|
# print(html.escape(page_contents).replace('\n', '<br/>\n'))
|
|
|
|
# sys.exit(0)
|
|
|
|
|
|
|
|
# Run sphinx-build.
|
|
|
|
out_dir = work_dir / "out"
|
|
|
|
out_filepath = out_dir / "contents.html"
|
|
|
|
|
|
|
|
sphinx_cmd = ["sphinx-build", "-b", "html", work_dir, out_dir]
|
2023-05-09 09:45:35 -05:00
|
|
|
result = subprocess.run(sphinx_cmd, capture_output=True)
|
2023-03-09 12:18:50 -05:00
|
|
|
|
|
|
|
# Output errors.
|
|
|
|
error = result.stderr.decode("utf-8", "ignore").strip()
|
|
|
|
if len(error):
|
|
|
|
error = error.replace(str(page_filepath) + ":", "")
|
|
|
|
error = html.escape(error)
|
|
|
|
print("<h2>Sphinx Warnings</h2>\n")
|
|
|
|
print(f"<pre>{error}</pre>")
|
|
|
|
print("<p>Note the preview is not accurate and warnings may not indicate real issues.</p>")
|
|
|
|
|
|
|
|
# Output contents of body.
|
|
|
|
if result.returncode == 0 and out_filepath.is_file():
|
|
|
|
contents = out_filepath.read_text()
|
|
|
|
body = contents.split("<body>")[1].split("</body>")[0]
|
2023-03-13 18:58:31 -05:00
|
|
|
body = body.replace(f'href="{placeholder_url}', 'href="#link-not-available-in-preview"')
|
2023-03-09 12:18:50 -05:00
|
|
|
body = body.replace('href="http', 'target="_blank" href="http')
|
2023-03-13 18:58:31 -05:00
|
|
|
body = '<div class="restructuredtext">' + body + '</div>'
|
2023-03-09 12:18:50 -05:00
|
|
|
print(body)
|