Skip to content

Commit 6b17dfa

Browse files
committed
Merge branch 'humitos/use-storage-properly-tar' into rel-environments
2 parents e9cead9 + 657cbfc commit 6b17dfa

File tree

10 files changed

+74
-72
lines changed

10 files changed

+74
-72
lines changed

common

readthedocs/api/v2/views/footer_views.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Endpoint to generate footer HTML."""
22

3+
import re
4+
35
from django.conf import settings
46
from django.shortcuts import get_object_or_404
57
from django.template import loader as template_loader
@@ -142,13 +144,12 @@ def _get_context(self):
142144
version = self._get_version()
143145

144146
page_slug = self.request.GET.get('page', '')
147+
path = ''
145148
if page_slug and page_slug != 'index':
146-
if main_project.documentation_type == 'sphinx_htmldir':
147-
path = page_slug + '/'
149+
if version.documentation_type == 'sphinx_htmldir':
150+
path = re.sub('/index$', '', page_slug) + '/'
148151
else:
149152
path = page_slug + '.html'
150-
else:
151-
path = ''
152153

153154
context = {
154155
'project': project,

readthedocs/config/config.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -685,8 +685,6 @@ def validate(self):
685685
self.validate_doc_types()
686686
self._config['mkdocs'] = self.validate_mkdocs()
687687
self._config['sphinx'] = self.validate_sphinx()
688-
# TODO: remove later
689-
self.validate_final_doc_type()
690688
self._config['submodules'] = self.validate_submodules()
691689
self.validate_keys()
692690

@@ -965,29 +963,6 @@ def validate_sphinx(self):
965963

966964
return sphinx
967965

968-
def validate_final_doc_type(self):
969-
"""
970-
Validates that the doctype is the same as the admin panel.
971-
972-
This a temporal validation, as the configuration file should support per
973-
version doctype, but we need to adapt the rtd code for that.
974-
"""
975-
dashboard_doctype = self.defaults.get('doctype', 'sphinx')
976-
if self.doctype != dashboard_doctype:
977-
error_msg = (
978-
'Your project is configured as "{}" in your admin dashboard,'
979-
).format(self.builders_display[dashboard_doctype])
980-
981-
if dashboard_doctype == 'mkdocs' or not self.sphinx:
982-
error_msg += ' but there is no "{}" key specified.'.format(
983-
'mkdocs' if dashboard_doctype == 'mkdocs' else 'sphinx',
984-
)
985-
else:
986-
error_msg += ' but your "sphinx.builder" key does not match.'
987-
988-
key = 'mkdocs' if self.doctype == 'mkdocs' else 'sphinx.builder'
989-
self.error(key, error_msg, code=INVALID_KEYS_COMBINATION)
990-
991966
def validate_submodules(self):
992967
"""
993968
Validates the submodules key.

readthedocs/config/tests/test_config.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,9 +1511,6 @@ def test_sphinx_builder_default(self):
15111511
build.validate()
15121512
build.sphinx.builder == 'sphinx'
15131513

1514-
@pytest.mark.skip(
1515-
'This test is not compatible with the new validation around doctype.',
1516-
)
15171514
def test_sphinx_builder_ignores_default(self):
15181515
build = self.get_build_config(
15191516
{},
@@ -1686,28 +1683,6 @@ def test_mkdocs_fail_on_warning_check_default(self):
16861683
build.validate()
16871684
assert build.mkdocs.fail_on_warning is False
16881685

1689-
def test_validates_different_filetype_mkdocs(self):
1690-
build = self.get_build_config(
1691-
{'mkdocs': {}},
1692-
{'defaults': {'doctype': 'sphinx'}},
1693-
)
1694-
with raises(InvalidConfig) as excinfo:
1695-
build.validate()
1696-
assert excinfo.value.key == 'mkdocs'
1697-
assert 'configured as "Sphinx Html"' in str(excinfo.value)
1698-
assert 'there is no "sphinx" key' in str(excinfo.value)
1699-
1700-
def test_validates_different_filetype_sphinx(self):
1701-
build = self.get_build_config(
1702-
{'sphinx': {}},
1703-
{'defaults': {'doctype': 'sphinx_htmldir'}},
1704-
)
1705-
with raises(InvalidConfig) as excinfo:
1706-
build.validate()
1707-
assert excinfo.value.key == 'sphinx.builder'
1708-
assert 'configured as "Sphinx HtmlDir"' in str(excinfo.value)
1709-
assert 'your "sphinx.builder" key does not match' in str(excinfo.value)
1710-
17111686
def test_submodule_defaults(self):
17121687
build = self.get_build_config({})
17131688
build.validate()

readthedocs/doc_builder/environments.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ def __init__(
548548
record=True,
549549
environment=None,
550550
update_on_success=True,
551+
start_time=None,
551552
):
552553
super().__init__(project, environment)
553554
self.version = version
@@ -557,7 +558,7 @@ def __init__(
557558
self.update_on_success = update_on_success
558559

559560
self.failure = None
560-
self.start_time = datetime.utcnow()
561+
self.start_time = start_time or datetime.utcnow()
561562

562563
def __enter__(self):
563564
return self

readthedocs/projects/tasks.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,17 +119,9 @@ def pull_cached_environment(self):
119119
'msg': msg,
120120
}
121121
)
122-
_, tmp_filename = tempfile.mkstemp(suffix='.tar')
123122
remote_fd = storage.open(filename, mode='rb')
124-
with open(tmp_filename, mode='wb') as local_fd:
125-
local_fd.write(remote_fd.read())
126-
127-
with tarfile.open(tmp_filename) as tar:
128-
tar.extractall(self.version.project.doc_path)
129-
130-
# Cleanup the temporary file
131-
if os.path.exists(tmp_filename):
132-
os.remove(tmp_filename)
123+
with tarfile.open(fileobj=remote_fd) as tar:
124+
tar.extractall(self.project.doc_path)
133125

134126
def push_cached_environment(self):
135127
if not self.project.has_feature(feature_id=Feature.CACHED_ENVIRONMENT):
@@ -157,7 +149,8 @@ def push_cached_environment(self):
157149

158150
# Special handling for .cache directory because it's per-project
159151
path = os.path.join(project_path, '.cache')
160-
tar.add(path, arcname='.cache')
152+
if os.path.exists(path):
153+
tar.add(path, arcname='.cache')
161154

162155
storage = get_storage_class(settings.RTD_BUILD_ENVIRONMENT_STORAGE)()
163156
with open(tmp_filename, 'rb') as fd:
@@ -473,6 +466,7 @@ def __init__(
473466
if config is not None:
474467
self.config = config
475468
self.task = task
469+
self.build_start_time = None
476470
# TODO: remove this
477471
self.setup_env = None
478472

@@ -577,6 +571,7 @@ def run_setup(self, record=True):
577571
update_on_success=False,
578572
environment=self.get_rtd_env_vars(),
579573
)
574+
self.build_start_time = environment.start_time
580575

581576
# TODO: Remove.
582577
# There is code that still depends of this attribute
@@ -667,6 +662,9 @@ def run_build(self, record):
667662
build=self.build,
668663
record=record,
669664
environment=env_vars,
665+
666+
# Pass ``start_time`` here to not reset the timer
667+
start_time=self.build_start_time,
670668
)
671669

672670
# Environment used for building code, usually with Docker

readthedocs/proxito/views/mixins.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ def _serve_docs_nginx(self, request, final_project, version_slug, path, download
124124
response['X-RTD-Domain'] = request.get_host()
125125
response['X-RTD-Project'] = final_project.slug
126126
response['X-RTD-Version'] = version_slug
127-
response['X-RTD-Path'] = path
127+
# Needed to strip any GET args, etc.
128+
response['X-RTD-Path'] = urlparse(path).path
128129
if hasattr(request, 'rtdheader'):
129130
response['X-RTD-Version-Method'] = 'rtdheader'
130131
if hasattr(request, 'subdomain'):

readthedocs/proxito/views/serve.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ def get(self, request, proxito_path, template_name='404.html'):
261261
# If that doesn't work, attempt to serve the 404 of the current version (version_slug)
262262
# Secondly, try to serve the 404 page for the default version
263263
# (project.get_default_version())
264-
for version_slug_404 in [version_slug, final_project.get_default_version()]:
264+
for version_slug_404 in set([version_slug, final_project.get_default_version()]):
265265
for tryfile in ('404.html', '404/index.html'):
266266
storage_root_path = final_project.get_storage_path(
267267
type_='html',

readthedocs/rtd_tests/tests/test_config_integration.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -781,9 +781,6 @@ def test_sphinx_builder(
781781

782782
get_builder_class.assert_called_with(result)
783783

784-
@pytest.mark.skip(
785-
'This test is not compatible with the new validation around doctype.',
786-
)
787784
@patch('readthedocs.projects.tasks.get_builder_class')
788785
def test_sphinx_builder_default(
789786
self, get_builder_class, checkout_path, tmpdir,

readthedocs/rtd_tests/tests/test_footer.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,60 @@ def test_not_show_edit_on_github(self):
143143
self.assertIn('View', response.data['html'])
144144
self.assertNotIn('Edit', response.data['html'])
145145

146+
def test_index_pages_sphinx_htmldir(self):
147+
version = self.pip.versions.get(slug=LATEST)
148+
version.documentation_type = 'sphinx_htmldir'
149+
version.save()
150+
151+
# A page with slug 'index' should render like /en/latest/
152+
self.url = (
153+
reverse('footer_html') +
154+
f'?project={self.pip.slug}&version={self.latest.slug}&page=index&docroot=/'
155+
)
156+
response = self.render()
157+
self.assertIn('/en/latest/', response.data['html'])
158+
self.assertNotIn('/en/latest/index.html', response.data['html'])
159+
160+
# A page with slug 'foo/index' should render like /en/latest/foo/
161+
self.url = (
162+
reverse('footer_html') +
163+
f'?project={self.pip.slug}&version={self.latest.slug}&page=foo/index&docroot=/'
164+
)
165+
response = self.render()
166+
self.assertIn('/en/latest/foo/', response.data['html'])
167+
self.assertNotIn('/en/latest/foo.html', response.data['html'])
168+
self.assertNotIn('/en/latest/foo/index.html', response.data['html'])
169+
170+
# A page with slug 'foo/bar' should render like /en/latest/foo/bar/
171+
self.url = (
172+
reverse('footer_html') +
173+
f'?project={self.pip.slug}&version={self.latest.slug}&page=foo/bar&docroot=/'
174+
)
175+
response = self.render()
176+
self.assertIn('/en/latest/foo/bar/', response.data['html'])
177+
self.assertNotIn('/en/latest/foo/bar.html', response.data['html'])
178+
self.assertNotIn('/en/latest/foo/bar/index.html', response.data['html'])
179+
180+
# A page with slug 'foo/bar/index' should render like /en/latest/foo/bar/
181+
self.url = (
182+
reverse('footer_html') +
183+
f'?project={self.pip.slug}&version={self.latest.slug}&page=foo/bar/index&docroot=/'
184+
)
185+
response = self.render()
186+
self.assertIn('/en/latest/foo/bar/', response.data['html'])
187+
self.assertNotIn('/en/latest/foo/bar.html', response.data['html'])
188+
self.assertNotIn('/en/latest/foo/bar/index.html', response.data['html'])
189+
190+
# A page with slug 'foo/index/bar' should render like /en/latest/foo/index/bar/
191+
self.url = (
192+
reverse('footer_html') +
193+
f'?project={self.pip.slug}&version={self.latest.slug}&page=foo/index/bar&docroot=/'
194+
)
195+
response = self.render()
196+
self.assertIn('/en/latest/foo/index/bar/', response.data['html'])
197+
self.assertNotIn('/en/latest/foo/index/bar.html', response.data['html'])
198+
self.assertNotIn('/en/latest/foo/index/bar/index.html', response.data['html'])
199+
146200

147201
class TestFooterHTML(BaseTestFooterHTML, TestCase):
148202

0 commit comments

Comments
 (0)