Commits

Alexander Shorin  committed 1dae6e5

Add docs uploader.

  • Participants
  • Parent commits 0c29b01

Comments (0)

Files changed (2)

File docs/Makefile

 SPHINXBUILD   = sphinx-build
 PAPER         =
 BUILDDIR      = _build
+SOURCEDIR     = .
 
 # Internal variables.
 PAPEROPT_a4     = -D latex_paper_size=a4
 PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCEDIR)
 # the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCEDIR)
 
 .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
 
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 
+couchapp:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+	@python upload.py \
+	    --sourcedir=$(SOURCEDIR) \
+	    --builddir=$(BUILDDIR) \
+	    --user=$(user) \
+	    $(server)/$(db)
+
 dirhtml:
 	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
 	@echo

File docs/upload.py

+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013 Alexander Shorin
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution.
+#
+
+import getopt
+import re
+import os
+import logging
+import platform
+import shutil
+import socket
+import sys
+import couchdb
+import couchapp
+import couchapp.commands
+from getpass import getpass
+from couchdb.http import extract_credentials, HTTPError
+from couchapp.config import Config
+
+SYSTEM = platform.system().lower()
+
+_HELP = """Usage: %(name)s [OPTIONS] URL
+
+Arguments:
+
+  URL              CouchDB database URL in next form:
+                   http[s]://[user[:password]@]host[:port]/dbname
+                   Note that setting password in URL will make it visible in
+                   shell history.
+
+Options:
+
+  -h, --help       Display a this help message and exit.
+  -u, --user=      User name to access CouchDB. Could be also defined in URL.
+                   Password will be requested.
+  -b, --builddir=  Sphinx build directory with HTML content.
+  -s, --sourcedir= Sphinx source directory.
+
+""" % dict(name=os.path.basename(sys.argv[0]))
+
+_NO_URL = """URL argument must be specified.
+""" + _HELP
+
+_USER_DUPLICATE = """Multiple users defined, couldn't decide which one to use:
+%s or %s
+"""
+
+
+class ColoredFormatter(logging.Formatter):
+
+    def __init__(self, fmt=None, datefmt=None):
+        self.colors = {
+            'CRITICAL': '\x1b[1;31m',
+            'ERROR': '\x1b[1;31m',
+            'INFO': '\x1b[1;32m',
+            'WARN': '\x1b[1;33m',
+            'WARNING': '\x1b[1;33m',
+            'DEBUG': '\x1b[1;37m',
+            }
+        self.reset = '\033[0m'
+        logging.Formatter.__init__(self, fmt, datefmt)
+
+    def format(self, record):
+        if SYSTEM == 'linux':
+            record.reset = self.reset
+        else:
+            record.reset = ''
+        record.funcName = '[%s]' % record.funcName
+        levelname = record.levelname
+        if SYSTEM == 'linux' and levelname in self.colors:
+            color = self.colors[levelname]
+            levelname = ''.join((color, levelname[0] * 2, self.reset))
+            record.levelname = '[%s]' % levelname
+        else:
+            record.levelname = '[%s]' % (levelname[0] * 2)
+        return logging.Formatter.format(self, record)
+
+
+def get_logger(name, level=logging.DEBUG):
+    fmt = '%(reset)s%(levelname)s  %(message)s'
+    instance = logging.Logger('couchdb.audit.%s' % name, level)
+    handler = logging.StreamHandler(sys.stdout)
+    handler.setFormatter(ColoredFormatter(fmt))
+    instance.addHandler(handler)
+    instance.propagate = False
+    return instance
+
+
+root = logging.Logger('hydra')
+handler = logging.StreamHandler(sys.stdout)
+root.addHandler(handler)
+log = get_logger('hydra.uploader')
+log.setLevel(logging.INFO)
+
+
+def upload(db, sourcedir, builddir):
+    sys.path.insert(0, sourcedir)
+    conf = __import__('conf')
+    sys.path.pop(0)
+
+    couchapp_dir = os.path.join(builddir, 'couchapp')
+    attachment_dir = os.path.join(couchapp_dir, '_attachments')
+    html_dir = os.path.join(builddir, 'html')
+
+    if not os.path.exists(couchapp_dir):
+        couchapp.commands.startapp({}, couchapp_dir)
+        for item in os.listdir(couchapp_dir):
+            path = os.path.join(couchapp_dir, item)
+            if os.path.isdir(path):
+                os.rmdir(path)
+    else:
+        shutil.rmtree(attachment_dir)
+
+    shutil.copytree(html_dir, attachment_dir)
+
+    ignore_list = ['_sources', '_static']
+
+    for item in ignore_list:
+        path = os.path.join(attachment_dir, item)
+        shutil.move(path, os.path.join(attachment_dir, '.%s' % item[1:]))
+
+    for root, dirs, files in os.walk(attachment_dir):
+        for file in files:
+            with open(os.path.join(root, file), 'rb') as f:
+                data = f.read()
+            match = re.search('(%s)' % '|'.join(ignore_list), data)
+            if match:
+                with open(os.path.join(root, file), 'wb') as f:
+                    for item in ignore_list:
+                        data = re.sub(item, '.%s' % item[1:], data)
+                    f.write(data)
+
+    with open(os.path.join(couchapp_dir, '_id'), 'wb') as f:
+        f.write(conf.version)
+    couchapp.commands.push(Config(), couchapp_dir, db.resource.url)
+
+    log.info(
+        'Docs are uploaded. Browse them at %s/%s/index.html',
+        db.resource.url, conf.version)
+
+
+def run(url, credentials, sourcedir, builddir):
+    db = couchdb.Database(url)
+    if credentials:
+        db.resource.credentials = credentials
+
+    try:
+        db.info()['db_name']
+    except socket.error as err:
+        log.error('%s: %s', err.__class__.__name__, err)
+        return
+    except HTTPError as err:
+        log.error('%s: %s', err.__class__.__name__, err.args[0][1])
+        return
+    except KeyError as err:
+        log.critical('%s: %s; Possible not database?',
+                     err.__class__.__name__, err)
+        return
+
+    return upload(db, sourcedir, builddir)
+
+
+def main():
+    try:
+        options, arguments = getopt.gnu_getopt(
+            sys.argv[1:], 'hu:s:b:a:',
+            ['help', 'user=', 'sourcedir=', 'builddir=']
+        )
+    except getopt.GetoptError, err:
+        sys.stdout.write(('%s\n\n' % err).capitalize())
+        sys.stdout.write(_HELP)
+        sys.stdout.flush()
+        sys.exit(1)
+
+    apps = []
+    message = None
+    sourcedir = None
+    builddir = None
+    user = None
+    for option, value in options:
+        if option in ['-h', '--help']:
+            message = _HELP
+        elif option in ['-u', '--user'] and value:
+            user = value
+        elif option in ['-s', '--sourcedir']:
+            sourcedir = value
+        elif option in ['-b', '--builddir']:
+            builddir = value
+        elif option in ['-a', '--app']:
+            apps.append(value)
+
+    if message:
+        sys.stdout.write(message)
+        sys.stdout.flush()
+        sys.exit(0)
+
+    if not arguments:
+        sys.stdout.write(_NO_URL)
+        sys.stdout.flush()
+        sys.exit(1)
+
+    if sourcedir is None:
+        sys.stdout.write('Sphinx source directory missed.\n')
+        sys.stdout.write(_HELP)
+        sys.stdout.flush()
+        sys.exit(1)
+
+    if builddir is None:
+        sys.stdout.write('Sphinx build directory missed.\n')
+        sys.stdout.write(_HELP)
+        sys.stdout.flush()
+        sys.exit(1)
+
+    url = arguments[0]
+    if not url.startswith(('http://', 'https://')):
+        url = 'http://' + url
+    _, credentials = extract_credentials(url)
+
+    if credentials:
+        if user is not None and credentials[0] != user:
+            sys.stdout.write(_USER_DUPLICATE % (credentials[0], user))
+            sys.stdout.flush()
+            sys.exit(1)
+        credentials = list(credentials)
+    elif user:
+        credentials = [user]
+
+    if credentials and len(credentials) == 1:
+        credentials.append(getpass('Enter password for %s: ' % credentials[0]))
+
+    if credentials:
+        credentials = tuple(credentials)
+
+    sys.exit(run(url, credentials, sourcedir, builddir))
+
+if __name__ == '__main__':
+    main()