sphinx-multiversion/sphinx_multiversion/main.py
2020-02-23 17:10:33 +01:00

154 lines
5.6 KiB
Python

# -*- coding: utf-8 -*-
import argparse
import json
import logging
import os
import pathlib
import re
import subprocess
import sys
import tempfile
from sphinx.cmd import build as sphinx_build
from sphinx import config as sphinx_config
from sphinx import project as sphinx_project
from . import sphinx
from . import git
def main(argv=None):
if not argv:
argv = sys.argv[1:]
parser = argparse.ArgumentParser()
parser.add_argument('sourcedir', help='path to documentation source files')
parser.add_argument('outputdir', help='path to output directory')
parser.add_argument('filenames', nargs='*', help='a list of specific files to rebuild. Ignored if -a is specified')
parser.add_argument('-c', metavar='PATH', dest='confdir', help='path where configuration file (conf.py) is located (default: same as SOURCEDIR)')
parser.add_argument('-C', action='store_true', dest='noconfig', help='use no config file at all, only -D options')
parser.add_argument('-D', metavar='setting=value', action='append', dest='define', default=[], help='override a setting in configuration file')
parser.add_argument('--dump-metadata', action='store_true', help='dump generated metadata and exit')
args, argv = parser.parse_known_args(argv)
if args.noconfig:
return 1
# Conf-overrides
confoverrides = {}
for d in args.define:
key, _, value = d.partition('=')
confoverrides[key] = value
# Parse config
config = sphinx_config.Config.read(
os.path.abspath(args.confdir if args.confdir else args.sourcedir),
confoverrides,
)
config.add("smv_tag_whitelist", sphinx.DEFAULT_TAG_WHITELIST, "html", str)
config.add("smv_branch_whitelist", sphinx.DEFAULT_TAG_WHITELIST, "html", str)
config.add("smv_remote_whitelist", sphinx.DEFAULT_REMOTE_WHITELIST, "html", str)
config.add("smv_released_pattern", sphinx.DEFAULT_RELEASED_PATTERN, "html", str)
config.add("smv_outputdir_format", sphinx.DEFAULT_OUTPUTDIR_FORMAT, "html", str)
# Get git references
gitroot = pathlib.Path('.').resolve()
gitrefs = git.get_refs(
str(gitroot),
config.smv_tag_whitelist,
config.smv_branch_whitelist,
config.smv_remote_whitelist,
)
logger = logging.getLogger(__name__)
# Get Sourcedir
sourcedir = os.path.relpath(args.sourcedir, str(gitroot))
if args.confdir:
confdir = os.path.relpath(args.confdir, str(gitroot))
else:
confdir = sourcedir
with tempfile.TemporaryDirectory() as tmp:
# Generate Metadata
metadata = {}
outputdirs = set()
for gitref in gitrefs:
# Clone Git repo
repopath = os.path.join(tmp, gitref.commit)
try:
git.copy_tree(gitroot.as_uri(), repopath, gitref)
except (OSError, subprocess.CalledProcessError):
logger.error(
"Failed to copy git tree for %s to %s",
gitref.refname, repopath)
continue
# Find config
confpath = os.path.join(repopath, confdir)
try:
current_config = sphinx_config.Config.read(
confpath,
confoverrides,
)
except sphinx_config.ConfigError:
logger.error(
"Failed load config for %s from %s",
gitref.refname, confpath)
continue
# Ensure that there are not duplicate output dirs
outputdir = config.smv_outputdir_format.format(
ref=gitref,
config=current_config,
)
if outputdir in outputdirs:
logger.warning(
"outputdir '%s' for %s conflicts with other versions",
outputdir, gitref.name)
continue
outputdirs.add(outputdir)
# Get List of files
source_suffixes = current_config.source_suffix
if isinstance(source_suffixes, str):
source_suffixes = [current_config.source_suffix]
project = sphinx_project.Project(sourcedir, source_suffixes)
metadata[gitref.name] = {
"name": gitref.name,
"version": current_config.version,
"release": current_config.release,
"is_released": bool(
re.match(config.smv_released_pattern, gitref.refname)),
"source": gitref.source,
"sourcedir": sourcedir,
"outputdir": outputdir,
"docnames": list(project.discover())
}
if args.dump_metadata:
print(json.dumps(metadata, indent=2))
return
# Write Metadata
metadata_path = os.path.abspath(os.path.join(tmp, "versions.json"))
with open(metadata_path, mode='w') as fp:
json.dump(metadata, fp, indent=2)
# Run Sphinx
argv.extend(["-D", "smv_metadata_path={}".format(metadata_path)])
for version_name, data in metadata.items():
outdir = os.path.join(args.outputdir, data["outputdir"])
os.makedirs(outdir, exist_ok=True)
current_argv = argv.copy()
current_argv.extend([
*args.define,
"-D", "smv_current_version={}".format(version_name),
"-c", args.confdir,
data["sourcedir"],
outdir,
*args.filenames,
])
status = sphinx_build.build_main(current_argv)
if status not in (0, None):
break