fune/tools/docs/mach_commands.py
Gregory Szorc 10773f0d43 Bug 1390693 - Upload docs to project and version specific locations; r=dustin
Previously, we uploaded the main Firefox tree docs to /.

In reality, there are multiple Sphinx projects in the repo. In
addition, it is sometimes desirable to access docs for an older
version of Firefox.

In this commit, we add support for specifying the S3 key prefix
for uploads. Then we change the upload code to upload to multiple
locations:

* <project>/latest (always)
* <project>/<version> (if a version is defined in the Sphinx config)
* / (for the main Sphinx docs project)

For the Firefox docs, ``version`` corresponds to a sanitized value from
``milestone.txt``. Currently, it resolves to ``57.0``.

While we're here, we add support for declaring an alternate project
name in the Sphinx conf.py file. If ``moz_project_name`` is defined,
we use that as the project name. For Firefox, we set it to ``main``.
This means our paths (local and uploaded) are now ``main`` instead of
``Mozilla_Source_Tree_Docs``. That's much more pleasant.

MozReview-Commit-ID: 8Gl6l2m6uU4

--HG--
extra : rebase_source : e56885092c12eb8cc76e5e7300f938be566e3e5a
extra : intermediate-source : 8509af1e135177a93460270b27f263c10a62d996
extra : source : 71b4f32caf209fe9dffc340c0b8ccb51ac79c7de
2017-08-24 11:12:21 -07:00

169 lines
5.8 KiB
Python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, # You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import absolute_import, print_function, unicode_literals
import os
import sys
from mach.decorators import (
Command,
CommandArgument,
CommandProvider,
)
import mozhttpd
from mozbuild.base import MachCommandBase
@CommandProvider
class Documentation(MachCommandBase):
"""Helps manage in-tree documentation."""
@Command('doc', category='devenv',
description='Generate and display documentation from the tree.')
@CommandArgument('what', nargs='*', metavar='DIRECTORY [, DIRECTORY]',
help='Path(s) to documentation to build and display.')
@CommandArgument('--format', default='html',
help='Documentation format to write.')
@CommandArgument('--outdir', default=None, metavar='DESTINATION',
help='Where to write output.')
@CommandArgument('--archive', action='store_true',
help='Write a gzipped tarball of generated docs')
@CommandArgument('--no-open', dest='auto_open', default=True,
action='store_false',
help="Don't automatically open HTML docs in a browser.")
@CommandArgument('--http', const=':6666', metavar='ADDRESS', nargs='?',
help='Serve documentation on an HTTP server, '
'e.g. ":6666".')
@CommandArgument('--upload', action='store_true',
help='Upload generated files to S3')
def build_docs(self, what=None, format=None, outdir=None, auto_open=True,
http=None, archive=False, upload=False):
self._activate_virtualenv()
self.virtualenv_manager.install_pip_package('sphinx_rtd_theme==0.1.6')
import sphinx
import webbrowser
import moztreedocs
if not outdir:
outdir = os.path.join(self.topobjdir, 'docs')
if not what:
what = [os.path.join(self.topsrcdir, 'tools')]
format_outdir = os.path.join(outdir, format)
generated = []
failed = []
for path in what:
path = os.path.normpath(os.path.abspath(path))
docdir = self._find_doc_dir(path)
if not docdir:
failed.append((path, 'could not find docs at this location'))
continue
props = self._project_properties(docdir)
savedir = os.path.join(format_outdir, props['project'])
args = [
'sphinx',
'-b', format,
docdir,
savedir,
]
result = sphinx.build_main(args)
if result != 0:
failed.append((path, 'sphinx return code %d' % result))
else:
generated.append(savedir)
if archive:
archive_path = os.path.join(outdir,
'%s.tar.gz' % props['project'])
moztreedocs.create_tarball(archive_path, savedir)
print('Archived to %s' % archive_path)
if upload:
self._s3_upload(savedir, props['project'], props['version'])
index_path = os.path.join(savedir, 'index.html')
if not http and auto_open and os.path.isfile(index_path):
webbrowser.open(index_path)
if generated:
print('\nGenerated documentation:\n%s\n' % '\n'.join(generated))
if failed:
failed = ['%s: %s' % (f[0], f[1]) for f in failed]
return die('failed to generate documentation:\n%s' % '\n'.join(failed))
if http is not None:
host, port = http.split(':', 1)
addr = (host, int(port))
if len(addr) != 2:
return die('invalid address: %s' % http)
httpd = mozhttpd.MozHttpd(host=addr[0], port=addr[1],
docroot=format_outdir)
print('listening on %s:%d' % addr)
httpd.start(block=True)
def _project_properties(self, path):
import imp
path = os.path.join(path, 'conf.py')
with open(path, 'r') as fh:
conf = imp.load_module('doc_conf', fh, path,
('.py', 'r', imp.PY_SOURCE))
# Prefer the Mozilla project name, falling back to Sphinx's
# default variable if it isn't defined.
project = getattr(conf, 'moz_project_name', None)
if not project:
project = conf.project.replace(' ', '_')
return {
'project': project,
'version': getattr(conf, 'version', None)
}
def _find_doc_dir(self, path):
search_dirs = ('doc', 'docs')
for d in search_dirs:
p = os.path.join(path, d)
if os.path.isfile(os.path.join(p, 'conf.py')):
return p
def _s3_upload(self, root, project, version=None):
self.virtualenv_manager.install_pip_package('boto3==1.4.4')
from moztreedocs import distribution_files
from moztreedocs.upload import s3_upload
# Files are uploaded to multiple locations:
#
# <project>/latest
# <project>/<version>
#
# This allows multiple projects and versions to be stored in the
# S3 bucket.
files = list(distribution_files(root))
s3_upload(files, key_prefix='%s/latest' % project)
if version:
s3_upload(files, key_prefix='%s/%s' % (project, version))
# Until we redirect / to main/latest, upload the main docs
# to the root.
if project == 'main':
s3_upload(files)
def die(msg, exit_code=1):
msg = '%s: %s' % (sys.argv[0], msg)
print(msg, file=sys.stderr)
return exit_code