1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
#!/usr/bin/env python3
# coding: utf-8
# Copyright ⓒ 2016 Daniel Keep.
#
# Licensed under the MIT license (see LICENSE or <http://opensource.org
# /licenses/MIT>) or the Apache License, Version 2.0 (see LICENSE of
# <http://www.apache.org/licenses/LICENSE-2.0>), at your option. All
# files in the project carrying such notice may not be copied, modified,
# or distributed except according to those terms.
import distutils.dir_util
import os
import shutil
import sys
import tempfile
import time
from common import *
DOC_ARGS = '--no-deps'
DOC_FEATURES = ""
DOC_TARGET_BRANCH = 'gh-pages'
DOC_PKG_DIR = None
DOC_TOOLCHAIN = None
TEMP_CHECKOUT_PREFIX = 'gh-pages-checkout-'
TEMP_OUTPUT_PREFIX = 'gh-pages-generated-'
TRACE_UPDATE_DOCS = os.environ.get('TRACE_UPDATE_DOCS', '') != ''
def copytree(src, dst):
msg_trace('copytree(%r, %r)' % (src, dst))
distutils.dir_util.copy_tree(src=src, dst=dst)
def really_rmtree(path):
msg_trace('really_rmtree(%r)' % path)
WAIT_TIME_SECS = 1.0
MAX_TRIES = 10
def on_error(func, path, exc_info):
"""
Error handler for ``shutil.rmtree``.
If the error is due to an access error (read only file)
it attempts to add write permission and then retries.
If the error is for another reason it re-raises the error.
Usage: ``shutil.rmtree(path, onerror=on_error)``
From <http://stackoverflow.com/a/2656405>_.
"""
import stat
if not os.access(path, os.W_OK):
# Is the error an access error ?
os.chmod(path, stat.S_IWUSR)
func(path)
else:
raise
for _ in range(MAX_TRIES):
failed = True
try:
msg_trace('shutil.rmtree(%r)' % path)
shutil.rmtree(path, onerror=on_error)
failed = False
except WindowsError:
time.sleep(WAIT_TIME_SECS)
if not failed: return
msg('Warning: failed to remove directory %r' % path)
def init_doc_branch():
msg("Initialising %s branch" % DOC_TARGET_BRANCH)
dir = os.getcwd()
msg_trace('dir = %r' % dir)
tmp = tempfile.mkdtemp(prefix=TEMP_CHECKOUT_PREFIX)
msg_trace('tmp = %r' % tmp)
try:
msg("Cloning into a temporary directory...")
sh('git init -q "%s"' % tmp)
msg_trace('os.chdir(%r)' % tmp)
os.chdir(tmp)
sh('git checkout -q --orphan "%s"' % DOC_TARGET_BRANCH)
sh('git commit -qm "Initial commit." --allow-empty')
sh('git remote add origin "%s"' % dir)
sh('git push -q origin gh-pages')
finally:
msg('Cleaning up...')
msg_trace('os.chdir(%r)' % dir)
os.chdir(dir)
msg_trace('shutil.rmtree(%r)' % tmp)
really_rmtree(tmp)
msg('%s is ready. Continuing.' % DOC_TARGET_BRANCH)
def gen_doc_bare(tmp1, tmp2):
msg("Generating documentation...")
args = '%s --features="%s"' % (DOC_ARGS, DOC_FEATURES)
if DOC_TOOLCHAIN is not None:
toolchain = "%s run %s " % (RUSTUP, DOC_TOOLCHAIN)
else:
toolchain = ""
sh('%scargo doc %s' % (toolchain, args))
tmp1_target_doc = '%s/target/doc' % tmp1
msg_trace('shutil.move(%r, %r)' % (tmp1_target_doc, tmp2))
shutil.move(tmp1_target_doc, tmp2)
def gen_doc_pkg(tmp1, tmp2, doc_pkg):
if DOC_FEATURES != "":
msg("Error: doc packages don't support features.")
sys.exit(1)
import json
old_dir = os.getcwd()
msg("Generating documentation from doc package...")
msg_trace('doc_pkg = %r' % doc_pkg)
msg_trace('os.chdir(%r)' % doc_pkg)
try:
os.chdir(doc_pkg)
manifest_str = sh_eval('cargo read-manifest --manifest-path "%s"'
% os.path.join(doc_pkg, 'Cargo.toml'))
manifest = json.loads(manifest_str)
if DOC_TOOLCHAIN is not None:
toolchain = "%s run %s " % (RUSTUP, DOC_TOOLCHAIN)
else:
toolchain = ""
packages = " ".join("--package %s" % d['name'] for d in manifest['dependencies'])
sh('%scargo doc %s' % (toolchain, packages))
target_doc = os.path.join(doc_pkg, 'target/doc')
msg_trace('shutil.move(%r, %r)' % (target_doc, tmp2))
shutil.move(target_doc, tmp2)
finally:
msg_trace('os.chdir(%r)' % old_dir)
os.chdir(old_dir)
def main():
load_globals_from_metadata('update-docs', globals(),
{
'DOC_ARGS',
'DOC_FEATURES',
'DOC_TARGET_BRANCH',
'DOC_PKG_DIR',
'DOC_TOOLCHAIN',
'TEMP_CHECKOUT_PREFIX',
'TEMP_OUTPUT_PREFIX',
})
if sh_eval('git symbolic-ref --short HEAD') != u'master':
msg('Not on master; doing nothing.')
return 0
# Sanity check: does the doc branch exist at all?
branches = {b[2:].strip() for b in sh_eval('git branch', dont_strip=True).splitlines()}
msg_trace('branches = %r' % branches)
if DOC_TARGET_BRANCH not in branches:
init_doc_branch()
last_rev = sh_eval('git rev-parse HEAD')
last_msg = sh_eval('git log -1 --pretty=%B')
msg_trace('last_rev = %r' % last_rev)
msg_trace('last_msg = %r' % last_msg)
dir = os.getcwd()
msg_trace('dir = %r' % dir)
tmp1 = tempfile.mkdtemp(prefix=TEMP_CHECKOUT_PREFIX)
tmp2 = tempfile.mkdtemp(prefix=TEMP_OUTPUT_PREFIX)
msg_trace('tmp1 = %r' % tmp1)
msg_trace('tmp2 = %r' % tmp2)
try:
msg("Cloning into a temporary directory...")
sh('git clone -qb "%s" "%s" "%s"' % (DOC_TARGET_BRANCH, dir, tmp1))
msg_trace('os.chdir(%r)' % tmp1)
os.chdir(tmp1)
sh('git checkout -q master')
if DOC_PKG_DIR is not None:
gen_doc_pkg(tmp1, tmp2, os.path.abspath(DOC_PKG_DIR))
else:
gen_doc_bare(tmp1, tmp2)
msg('Updating %s...' % DOC_TARGET_BRANCH)
sh('git checkout -q "%s"' % DOC_TARGET_BRANCH)
sh('git clean -dfq')
tmp2_doc = '%s/doc' % tmp2
msg_trace('copytree(%r, %r)' % (tmp2_doc, './doc'))
copytree(tmp2_doc, './doc')
msg('Committing changes...')
sh('git add .')
sh('git commit --amend -m "Update docs for %s" -m "%s"' % (last_rev[:7], last_msg))
sh('git push -fqu origin "%s"' % DOC_TARGET_BRANCH)
finally:
msg('Cleaning up...')
msg_trace('os.chdir(%r)' % dir)
os.chdir(dir)
msg_trace('shutil.rmtree(%r)' % tmp2)
really_rmtree(tmp2)
msg_trace('shutil.rmtree(%r)' % tmp1)
really_rmtree(tmp1)
msg('Publishing...')
sh('git push -f origin "%s"' % DOC_TARGET_BRANCH)
msg('Done.')
if __name__ == '__main__':
sys.exit(main())
|