diff --git a/docs/Makefile b/docs/Makefile
index d8f042f..7c684cb 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -32,7 +32,7 @@ help:
@echo "gitwash-update update git workflow from source repo"
clean:
- -rm -rf build/* dist/* $(SRCDIR)/api/generated gh-pages
+ -rm -rf build/* dist/* $(SRCDIR)/api/generated
pdf: latex
cd build/latex && make all-pdf
@@ -104,4 +104,7 @@ nightly: dist
rsync -avH --delete dist/ ipython:www/doc/nightly
gh-pages: html
- sh update_ghpages.sh
+ python gh-pages.py
+
+gh-pages-current: html
+ python gh-pages.py current
diff --git a/docs/gh-pages.py b/docs/gh-pages.py
new file mode 100755
index 0000000..e785611
--- /dev/null
+++ b/docs/gh-pages.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+"""Script to commit the doc build outputs into the github-pages repo.
+
+Use:
+
+ gh-pages.py [tag]
+
+If no tag is given, the current output of 'git describe' is used. If given,
+that is how the resulting directory will be named.
+
+In practice, you should use either actual clean tags from a current build or
+something like 'current' as a stable URL for the most current version of the """
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+import os
+import re
+import shutil
+import sys
+from os import chdir as cd
+from os.path import join as pjoin
+
+from subprocess import Popen, PIPE, CalledProcessError, check_call
+
+#-----------------------------------------------------------------------------
+# Globals
+#-----------------------------------------------------------------------------
+
+pages_dir = 'gh-pages'
+html_dir = 'build/html'
+pages_repo = 'git@github.com:ipython/ipython-doc.git'
+
+#-----------------------------------------------------------------------------
+# Functions
+#-----------------------------------------------------------------------------
+def sh(cmd):
+ """Execute command in a subshell, return status code."""
+ return check_call(cmd, shell=True)
+
+
+def sh2(cmd):
+ """Execute command in a subshell, return stdout.
+
+ Stderr is unbuffered from the subshell.x"""
+ p = Popen(cmd, stdout=PIPE, shell=True)
+ out = p.communicate()[0]
+ retcode = p.returncode
+ if retcode:
+ raise CalledProcessError(retcode, cmd)
+ else:
+ return out.rstrip()
+
+
+def sh3(cmd):
+ """Execute command in a subshell, return stdout, stderr
+
+ If anything appears in stderr, print it out to sys.stderr"""
+ p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
+ out, err = p.communicate()
+ retcode = p.returncode
+ if retcode:
+ raise CalledProcessError(retcode, cmd)
+ else:
+ return out.rstrip(), err.rstrip()
+
+
+def init_repo(path):
+ """clone the gh-pages repo if we haven't already."""
+ sh("git clone %s %s"%(pages_repo, path))
+ here = os.getcwd()
+ cd(path)
+ sh('git checkout gh-pages')
+ cd(here)
+
+
+def render_htmlindex(fname, tag):
+ rel = '
Release: {t}'.format(t=tag)
+ rep = re.compile('')
+ out = []
+ with file(fname) as f:
+ for line in f:
+ out.append(line)
+ if rep.search(line):
+ out.append(rep.sub(rel, line))
+ return ''.join(out)
+
+
+def new_htmlindex(fname, tag):
+ new_page = render_htmlindex(fname, tag)
+ os.rename(fname, fname+'~')
+ with file(fname, 'w') as f:
+ f.write(new_page)
+
+
+#-----------------------------------------------------------------------------
+# Script starts
+#-----------------------------------------------------------------------------
+if __name__ == '__main__':
+ # The tag can be given as a positional argument
+ try:
+ tag = sys.argv[1]
+ except IndexError:
+ tag = sh2('git describe')
+
+ startdir = os.getcwd()
+ if not os.path.exists(pages_dir):
+ init_repo(pages_dir)
+
+ dest = pjoin(pages_dir, tag)
+
+ # don't `make html` here, because gh-pages already depends on html in Makefile
+ # sh('make html')
+
+ # This is pretty unforgiving: we unconditionally nuke the destination
+ # directory, and then copy the html tree in there
+ shutil.rmtree(dest, ignore_errors=True)
+ shutil.copytree(html_dir, dest)
+
+ try:
+ cd(pages_dir)
+ sh('git checkout gh-pages')
+ status = sh2('git status | head -1')
+ branch = re.match('\# On branch (.*)$', status).group(1)
+ if branch != 'gh-pages':
+ e = 'On %r, git branch is %r, MUST be "gh-pages"' % (pages_dir,
+ branch)
+ raise RuntimeError(e)
+
+ sh('git add %s' % tag)
+ new_htmlindex('index.html', tag)
+ sh('git add index.html')
+ sh('git commit -m"Created new doc release, named: %s"' % tag)
+ print
+ print 'Most recent 3 commits:'
+ sys.stdout.flush()
+ sh('git --no-pager log --oneline HEAD~3..')
+ finally:
+ cd(startdir)
+
+ print
+ print 'Now verify the build in: %r' % dest
+ print "If everything looks good, 'git push'"
diff --git a/docs/source/conf.py b/docs/source/conf.py
index c41f56e..60a9e48 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -44,7 +44,6 @@ extensions = [
'inheritance_diagram',
'ipython_console_highlighting',
'numpydoc', # to preprocess docstrings
- 'sphinxtogithub',
]
# Add any paths that contain templates here, relative to this directory.
diff --git a/docs/sphinxext/sphinxtogithub.py b/docs/sphinxext/sphinxtogithub.py
deleted file mode 100644
index 9b72b87..0000000
--- a/docs/sphinxext/sphinxtogithub.py
+++ /dev/null
@@ -1,381 +0,0 @@
-#! /usr/bin/env python
-"""
-sphinxtogithub extension Copyright Michael Jones
-
-BSD License
-
-Original at: https://github.com/michaeljones/sphinx-to-github
-"""
-from optparse import OptionParser
-import os
-import sys
-import shutil
-
-
-class DirHelper(object):
-
- def __init__(self, is_dir, list_dir, walk, rmtree):
-
- self.is_dir = is_dir
- self.list_dir = list_dir
- self.walk = walk
- self.rmtree = rmtree
-
-class FileSystemHelper(object):
-
- def __init__(self, open_, path_join, move, exists):
-
- self.open_ = open_
- self.path_join = path_join
- self.move = move
- self.exists = exists
-
-class Replacer(object):
- "Encapsulates a simple text replace"
-
- def __init__(self, from_, to):
-
- self.from_ = from_
- self.to = to
-
- def process(self, text):
-
- return text.replace( self.from_, self.to )
-
-class FileHandler(object):
- "Applies a series of replacements the contents of a file inplace"
-
- def __init__(self, name, replacers, opener):
-
- self.name = name
- self.replacers = replacers
- self.opener = opener
-
- def process(self):
-
- text = self.opener(self.name).read()
-
- for replacer in self.replacers:
- text = replacer.process( text )
-
- self.opener(self.name, "w").write(text)
-
-class Remover(object):
-
- def __init__(self, exists, remove):
- self.exists = exists
- self.remove = remove
-
- def __call__(self, name):
-
- if self.exists(name):
- self.remove(name)
-
-class ForceRename(object):
-
- def __init__(self, renamer, remove):
-
- self.renamer = renamer
- self.remove = remove
-
- def __call__(self, from_, to):
-
- self.remove(to)
- self.renamer(from_, to)
-
-class VerboseRename(object):
-
- def __init__(self, renamer, stream):
-
- self.renamer = renamer
- self.stream = stream
-
- def __call__(self, from_, to):
-
- self.stream.write(
- "Renaming directory '%s' -> '%s'\n"
- % (os.path.basename(from_), os.path.basename(to))
- )
-
- self.renamer(from_, to)
-
-
-class DirectoryHandler(object):
- "Encapsulates renaming a directory by removing its first character"
-
- def __init__(self, name, root, renamer):
-
- self.name = name
- self.new_name = name[1:]
- self.root = root + os.sep
- self.renamer = renamer
-
- def path(self):
-
- return os.path.join(self.root, self.name)
-
- def relative_path(self, directory, filename):
-
- path = directory.replace(self.root, "", 1)
- return os.path.join(path, filename)
-
- def new_relative_path(self, directory, filename):
-
- path = self.relative_path(directory, filename)
- return path.replace(self.name, self.new_name, 1)
-
- def process(self):
-
- from_ = os.path.join(self.root, self.name)
- to = os.path.join(self.root, self.new_name)
- self.renamer(from_, to)
-
-
-class HandlerFactory(object):
-
- def create_file_handler(self, name, replacers, opener):
-
- return FileHandler(name, replacers, opener)
-
- def create_dir_handler(self, name, root, renamer):
-
- return DirectoryHandler(name, root, renamer)
-
-
-class OperationsFactory(object):
-
- def create_force_rename(self, renamer, remover):
-
- return ForceRename(renamer, remover)
-
- def create_verbose_rename(self, renamer, stream):
-
- return VerboseRename(renamer, stream)
-
- def create_replacer(self, from_, to):
-
- return Replacer(from_, to)
-
- def create_remover(self, exists, remove):
-
- return Remover(exists, remove)
-
-
-class Layout(object):
- """
- Applies a set of operations which result in the layout
- of a directory changing
- """
-
- def __init__(self, directory_handlers, file_handlers):
-
- self.directory_handlers = directory_handlers
- self.file_handlers = file_handlers
-
- def process(self):
-
- for handler in self.file_handlers:
- handler.process()
-
- for handler in self.directory_handlers:
- handler.process()
-
-
-class NullLayout(object):
- """
- Layout class that does nothing when asked to process
- """
- def process(self):
- pass
-
-class LayoutFactory(object):
- "Creates a layout object"
-
- def __init__(self, operations_factory, handler_factory, file_helper, dir_helper, verbose, stream, force):
-
- self.operations_factory = operations_factory
- self.handler_factory = handler_factory
-
- self.file_helper = file_helper
- self.dir_helper = dir_helper
-
- self.verbose = verbose
- self.output_stream = stream
- self.force = force
-
- def create_layout(self, path):
-
- contents = self.dir_helper.list_dir(path)
-
- renamer = self.file_helper.move
-
- if self.force:
- remove = self.operations_factory.create_remover(self.file_helper.exists, self.dir_helper.rmtree)
- renamer = self.operations_factory.create_force_rename(renamer, remove)
-
- if self.verbose:
- renamer = self.operations_factory.create_verbose_rename(renamer, self.output_stream)
-
- # Build list of directories to process
- directories = [d for d in contents if self.is_underscore_dir(path, d)]
- underscore_directories = [
- self.handler_factory.create_dir_handler(d, path, renamer)
- for d in directories
- ]
-
- if not underscore_directories:
- if self.verbose:
- self.output_stream.write(
- "No top level directories starting with an underscore "
- "were found in '%s'\n" % path
- )
- return NullLayout()
-
- # Build list of files that are in those directories
- replacers = []
- for handler in underscore_directories:
- for directory, dirs, files in self.dir_helper.walk(handler.path()):
- for f in files:
- replacers.append(
- self.operations_factory.create_replacer(
- handler.relative_path(directory, f),
- handler.new_relative_path(directory, f)
- )
- )
-
- # Build list of handlers to process all files
- filelist = []
- for root, dirs, files in self.dir_helper.walk(path):
- for f in files:
- if f.endswith(".html"):
- filelist.append(
- self.handler_factory.create_file_handler(
- self.file_helper.path_join(root, f),
- replacers,
- self.file_helper.open_)
- )
- if f.endswith(".js"):
- filelist.append(
- self.handler_factory.create_file_handler(
- self.file_helper.path_join(root, f),
- [self.operations_factory.create_replacer("'_sources/'", "'sources/'")],
- self.file_helper.open_
- )
- )
-
- return Layout(underscore_directories, filelist)
-
- def is_underscore_dir(self, path, directory):
-
- return (self.dir_helper.is_dir(self.file_helper.path_join(path, directory))
- and directory.startswith("_"))
-
-
-
-def sphinx_extension(app, exception):
- "Wrapped up as a Sphinx Extension"
-
- if not app.builder.name in ("html", "dirhtml"):
- return
-
- if not app.config.sphinx_to_github:
- if app.config.sphinx_to_github_verbose:
- print "Sphinx-to-github: Disabled, doing nothing."
- return
-
- if exception:
- if app.config.sphinx_to_github_verbose:
- print "Sphinx-to-github: Exception raised in main build, doing nothing."
- return
-
- dir_helper = DirHelper(
- os.path.isdir,
- os.listdir,
- os.walk,
- shutil.rmtree
- )
-
- file_helper = FileSystemHelper(
- open,
- os.path.join,
- shutil.move,
- os.path.exists
- )
-
- operations_factory = OperationsFactory()
- handler_factory = HandlerFactory()
-
- layout_factory = LayoutFactory(
- operations_factory,
- handler_factory,
- file_helper,
- dir_helper,
- app.config.sphinx_to_github_verbose,
- sys.stdout,
- force=True
- )
-
- layout = layout_factory.create_layout(app.outdir)
- layout.process()
-
-
-def setup(app):
- "Setup function for Sphinx Extension"
-
- app.add_config_value("sphinx_to_github", True, '')
- app.add_config_value("sphinx_to_github_verbose", True, '')
-
- app.connect("build-finished", sphinx_extension)
-
-
-def main(args):
-
- usage = "usage: %prog [options] "
- parser = OptionParser(usage=usage)
- parser.add_option("-v","--verbose", action="store_true",
- dest="verbose", default=False, help="Provides verbose output")
- opts, args = parser.parse_args(args)
-
- try:
- path = args[0]
- except IndexError:
- sys.stderr.write(
- "Error - Expecting path to html directory:"
- "sphinx-to-github \n"
- )
- return
-
- dir_helper = DirHelper(
- os.path.isdir,
- os.listdir,
- os.walk,
- shutil.rmtree
- )
-
- file_helper = FileSystemHelper(
- open,
- os.path.join,
- shutil.move,
- os.path.exists
- )
-
- operations_factory = OperationsFactory()
- handler_factory = HandlerFactory()
-
- layout_factory = LayoutFactory(
- operations_factory,
- handler_factory,
- file_helper,
- dir_helper,
- opts.verbose,
- sys.stdout,
- force=False
- )
-
- layout = layout_factory.create_layout(path)
- layout.process()
-
-
-
-if __name__ == "__main__":
- main(sys.argv[1:])
diff --git a/docs/update_ghpages.sh b/docs/update_ghpages.sh
deleted file mode 100755
index 57dd59c..0000000
--- a/docs/update_ghpages.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env sh
-# pick repo for gh-pages branch
-repo=origin
-
-if [ ! -d gh-pages ]; then
- echo "setting up gh-pages subdir"
- mkdir gh-pages || exit -1
- cp -r ../.git gh-pages/ || exit -1
- cd gh-pages || exit -1
- init=0
- git checkout $repo/gh-pages || init=1
- if [ "$init" != "0" ]; then
- echo "initializing gh-pages repo"
- git symbolic-ref HEAD refs/heads/gh-pages || exit -1
- rm .git/index || exit -1
- git clean -fdx || exit -1
- touch index.html
- git add .
- git commit -a -m 'init gh-pages' || exit -1
- git push origin HEAD:gh-pages
- fi
- cd ..
-fi
-echo "updating local gh-pages with html build"
-rsync -va build/html/ gh-pages/ --delete --exclude .git || exit -1
-cd gh-pages
-git add .
-git commit -a || exit -1
-echo "pushing to remote gh-pages"
-# pwd
-git push $repo HEAD:gh-pages