commit
273776cbfd
|
@ -52,21 +52,27 @@ Example layout
|
|||
fabric -> rtd-builds/fabric/en/latest/ # single version
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from builtins import object
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from builtins import object
|
||||
from django.conf import settings
|
||||
|
||||
from readthedocs.builds.models import Version
|
||||
from readthedocs.core.utils.extend import SettingsOverrideObject
|
||||
from readthedocs.core.utils import safe_makedirs, safe_unlink
|
||||
from readthedocs.core.utils.extend import SettingsOverrideObject
|
||||
from readthedocs.doc_builder.environments import LocalEnvironment
|
||||
from readthedocs.projects import constants
|
||||
from readthedocs.projects.models import Domain
|
||||
from readthedocs.projects.utils import run
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -83,6 +89,7 @@ class Symlink(object):
|
|||
self.subproject_root = os.path.join(
|
||||
self.project_root, 'projects'
|
||||
)
|
||||
self.environment = LocalEnvironment(project)
|
||||
self.sanity_check()
|
||||
|
||||
def sanity_check(self):
|
||||
|
@ -146,17 +153,27 @@ class Symlink(object):
|
|||
else:
|
||||
domains = Domain.objects.filter(project=self.project)
|
||||
for dom in domains:
|
||||
log_msg = 'Symlinking CNAME: {0} -> {1}'.format(dom.domain, self.project.slug)
|
||||
log.info(constants.LOG_TEMPLATE.format(project=self.project.slug,
|
||||
version='', msg=log_msg))
|
||||
log_msg = 'Symlinking CNAME: {} -> {}'.format(
|
||||
dom.domain, self.project.slug
|
||||
)
|
||||
log.info(
|
||||
constants.LOG_TEMPLATE.format(
|
||||
project=self.project.slug,
|
||||
version='', msg=log_msg
|
||||
)
|
||||
)
|
||||
|
||||
# CNAME to doc root
|
||||
symlink = os.path.join(self.CNAME_ROOT, dom.domain)
|
||||
run(['ln', '-nsf', self.project_root, symlink])
|
||||
self.environment.run('ln', '-nsf', self.project_root, symlink)
|
||||
|
||||
# Project symlink
|
||||
project_cname_symlink = os.path.join(self.PROJECT_CNAME_ROOT, dom.domain)
|
||||
run(['ln', '-nsf', self.project.doc_path, project_cname_symlink])
|
||||
project_cname_symlink = os.path.join(
|
||||
self.PROJECT_CNAME_ROOT, dom.domain
|
||||
)
|
||||
self.environment.run(
|
||||
'ln', '-nsf', self.project.doc_path, project_cname_symlink
|
||||
)
|
||||
|
||||
def remove_symlink_cname(self, domain):
|
||||
"""Remove CNAME symlink."""
|
||||
|
@ -201,10 +218,12 @@ class Symlink(object):
|
|||
# TODO this should use os.symlink, not a call to shell. For now,
|
||||
# this passes command as a list to be explicit about escaping
|
||||
# characters like spaces.
|
||||
status, _, stderr = run(['ln', '-nsf', docs_dir, symlink])
|
||||
if status > 0:
|
||||
log.error('Could not symlink path: status=%d error=%s',
|
||||
status, stderr)
|
||||
result = self.environment.run('ln', '-nsf', docs_dir, symlink)
|
||||
if result.exit_code > 0:
|
||||
log.error(
|
||||
'Could not symlink path: status=%d error=%s',
|
||||
result.exit_code, result.error
|
||||
)
|
||||
|
||||
# Remove old symlinks
|
||||
if os.path.exists(self.subproject_root):
|
||||
|
@ -233,12 +252,16 @@ class Symlink(object):
|
|||
|
||||
for (language, slug) in list(translations.items()):
|
||||
|
||||
log_msg = 'Symlinking translation: {0}->{1}'.format(language, slug)
|
||||
log.info(constants.LOG_TEMPLATE.format(project=self.project.slug,
|
||||
version='', msg=log_msg))
|
||||
log_msg = 'Symlinking translation: {}->{}'.format(language, slug)
|
||||
log.info(
|
||||
constants.LOG_TEMPLATE.format(
|
||||
project=self.project.slug,
|
||||
version='', msg=log_msg
|
||||
)
|
||||
)
|
||||
symlink = os.path.join(self.project_root, language)
|
||||
docs_dir = os.path.join(self.WEB_ROOT, slug, language)
|
||||
run(['ln', '-nsf', docs_dir, symlink])
|
||||
self.environment.run('ln', '-nsf', docs_dir, symlink)
|
||||
|
||||
# Remove old symlinks
|
||||
for lang in os.listdir(self.project_root):
|
||||
|
@ -268,9 +291,13 @@ class Symlink(object):
|
|||
|
||||
# Create symlink
|
||||
if version is not None:
|
||||
docs_dir = os.path.join(settings.DOCROOT, self.project.slug,
|
||||
'rtd-builds', version.slug)
|
||||
run(['ln', '-nsf', docs_dir, symlink])
|
||||
docs_dir = os.path.join(
|
||||
settings.DOCROOT,
|
||||
self.project.slug,
|
||||
'rtd-builds',
|
||||
version.slug
|
||||
)
|
||||
self.environment.run('ln', '-nsf', docs_dir, symlink)
|
||||
|
||||
def symlink_versions(self):
|
||||
"""
|
||||
|
@ -280,7 +307,9 @@ class Symlink(object):
|
|||
HOME/user_builds/<project>/rtd-builds/<version>
|
||||
"""
|
||||
versions = set()
|
||||
version_dir = os.path.join(self.WEB_ROOT, self.project.slug, self.project.language)
|
||||
version_dir = os.path.join(
|
||||
self.WEB_ROOT, self.project.slug, self.project.language
|
||||
)
|
||||
# Include active public versions,
|
||||
# as well as public versions that are built but not active, for archived versions
|
||||
version_queryset = self.get_version_queryset()
|
||||
|
@ -289,11 +318,21 @@ class Symlink(object):
|
|||
safe_makedirs(version_dir)
|
||||
for version in version_queryset:
|
||||
log_msg = 'Symlinking Version: {}'.format(version)
|
||||
log.info(constants.LOG_TEMPLATE.format(project=self.project.slug,
|
||||
version='', msg=log_msg))
|
||||
log.info(
|
||||
constants.LOG_TEMPLATE.format(
|
||||
project=self.project.slug,
|
||||
version='',
|
||||
msg=log_msg
|
||||
)
|
||||
)
|
||||
symlink = os.path.join(version_dir, version.slug)
|
||||
docs_dir = os.path.join(settings.DOCROOT, self.project.slug, 'rtd-builds', version.slug)
|
||||
run(['ln', '-nsf', docs_dir, symlink])
|
||||
docs_dir = os.path.join(
|
||||
settings.DOCROOT,
|
||||
self.project.slug,
|
||||
'rtd-builds',
|
||||
version.slug
|
||||
)
|
||||
self.environment.run('ln', '-nsf', docs_dir, symlink)
|
||||
versions.add(version.slug)
|
||||
|
||||
# Remove old symlinks
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
"""Utility functions used by projects."""
|
||||
|
||||
from __future__ import (
|
||||
absolute_import, division, print_function, unicode_literals)
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
import six
|
||||
from builtins import object, open
|
||||
from builtins import open
|
||||
from django.conf import settings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -32,82 +32,6 @@ def version_from_slug(slug, version):
|
|||
return v
|
||||
|
||||
|
||||
def find_file(filename):
|
||||
"""
|
||||
Recursively find matching file from the current working path.
|
||||
|
||||
:param file: Filename to match
|
||||
:returns: A list of matching filenames.
|
||||
"""
|
||||
matches = []
|
||||
for root, __, filenames in os.walk('.'):
|
||||
for match in fnmatch.filter(filenames, filename):
|
||||
matches.append(os.path.join(root, match))
|
||||
return matches
|
||||
|
||||
|
||||
def run(*commands):
|
||||
"""
|
||||
Run one or more commands.
|
||||
|
||||
Each argument in `commands` can be passed as a string or as a list. Passing
|
||||
as a list is the preferred method, as space escaping is more explicit and it
|
||||
avoids the need for executing anything in a shell.
|
||||
|
||||
If more than one command is given, then this is equivalent to
|
||||
chaining them together with ``&&``; if all commands succeed, then
|
||||
``(status, out, err)`` will represent the last successful command.
|
||||
If one command failed, then ``(status, out, err)`` will represent
|
||||
the failed command.
|
||||
|
||||
:returns: ``(status, out, err)``
|
||||
"""
|
||||
environment = os.environ.copy()
|
||||
environment['READTHEDOCS'] = 'True'
|
||||
if 'DJANGO_SETTINGS_MODULE' in environment:
|
||||
del environment['DJANGO_SETTINGS_MODULE']
|
||||
if 'PYTHONPATH' in environment:
|
||||
del environment['PYTHONPATH']
|
||||
# Remove PYTHONHOME env variable if set, otherwise pip install of requirements
|
||||
# into virtualenv will install incorrectly
|
||||
if 'PYTHONHOME' in environment:
|
||||
del environment['PYTHONHOME']
|
||||
cwd = os.getcwd()
|
||||
if not commands:
|
||||
raise ValueError('run() requires one or more command-line strings')
|
||||
|
||||
for command in commands:
|
||||
# If command is a string, split it up by spaces to pass into Popen.
|
||||
# Otherwise treat the command as an iterable.
|
||||
if isinstance(command, six.string_types):
|
||||
run_command = command.split()
|
||||
else:
|
||||
try:
|
||||
run_command = list(command)
|
||||
command = ' '.join(command)
|
||||
except TypeError:
|
||||
run_command = command
|
||||
log.debug('Running command: cwd=%s command=%s', cwd, command)
|
||||
try:
|
||||
p = subprocess.Popen(
|
||||
run_command,
|
||||
cwd=cwd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=environment,
|
||||
)
|
||||
|
||||
out, err = p.communicate()
|
||||
ret = p.returncode
|
||||
except OSError:
|
||||
out = ''
|
||||
err = traceback.format_exc()
|
||||
ret = -1
|
||||
log.exception('Command failed')
|
||||
|
||||
return (ret, out, err)
|
||||
|
||||
|
||||
def safe_write(filename, contents):
|
||||
"""
|
||||
Normalize and write to filename.
|
||||
|
@ -126,9 +50,3 @@ def safe_write(filename, contents):
|
|||
with open(filename, 'w', encoding='utf-8', errors='ignore') as fh:
|
||||
fh.write(contents)
|
||||
fh.close()
|
||||
|
||||
|
||||
class DictObj(object):
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return self.__dict__.get(attr)
|
||||
|
|
Loading…
Reference in New Issue