Skip to content

Commit 880a350

Browse files
humitosagjohnson
authored andcommitted
Feature flag to make readthedocs theme default on MkDocs docs
Historically, we were using `readthedocs` as default theme for MkDocs but in #4556 we decided to change it to get the same behavior when building locally than in Read the Docs. This commit adds a Feature flag to keep having the old behavior for some particular projects so we can add these project and do not break their documentation (change the theme without asking/reason).
1 parent b78d1f3 commit 880a350

File tree

4 files changed

+124
-1
lines changed

4 files changed

+124
-1
lines changed

readthedocs/doc_builder/backends/mkdocs.py

+25
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from readthedocs.doc_builder.base import BaseBuilder
1818
from readthedocs.doc_builder.exceptions import BuildEnvironmentError
19+
from readthedocs.projects.models import Feature
1920

2021
log = logging.getLogger(__name__)
2122

@@ -50,6 +51,23 @@ def __init__(self, *args, **kwargs):
5051
self.root_path = self.version.project.checkout_path(self.version.slug)
5152
self.yaml_file = self.get_yaml_config()
5253

54+
# README: historically, the default theme was ``readthedocs`` but in
55+
# https://github.com/rtfd/readthedocs.org/pull/4556 we change it to
56+
# ``mkdocs`` to maintain the same behavior in Read the Docs than
57+
# building locally. Although, we can't apply the this into the Corporate
58+
# site. To keep the same default theme there, we created a Feature flag
59+
# for these project that were building with MkDocs in the Corporate
60+
# site.
61+
if self.project.has_feature(Feature.MKDOCS_THEME_RTD):
62+
self.DEFAULT_THEME_NAME = 'readthedocs'
63+
log.warning(
64+
'Project using readthedocs theme as default for MkDocs: slug=%s',
65+
self.project.slug,
66+
)
67+
else:
68+
self.DEFAULT_THEME_NAME = 'mkdocs'
69+
70+
5371
def get_yaml_config(self):
5472
"""Find the ``mkdocs.yml`` file in the project root."""
5573
mkdoc_path = self.config.mkdocs.configuration
@@ -130,6 +148,13 @@ def append_conf(self, **__):
130148
# This supports using RTD's privacy improvements around analytics
131149
user_config['google_analytics'] = None
132150

151+
# README: make MkDocs to use ``readthedocs`` theme as default if the
152+
# user didn't specify a specific theme manually
153+
if self.project.has_feature(Feature.MKDOCS_THEME_RTD):
154+
if 'theme' not in user_config:
155+
# mkdocs<0.17 syntax
156+
user_config['theme'] = self.DEFAULT_THEME_NAME
157+
133158
# Write the modified mkdocs configuration
134159
yaml.safe_dump(
135160
user_config,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.16 on 2018-10-24 07:43
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations
6+
7+
8+
FEATURE_ID = 'mkdocs_theme_rtd'
9+
10+
11+
def forward_add_feature(apps, schema_editor):
12+
Feature = apps.get_model('projects', 'Feature')
13+
Feature.objects.create(
14+
feature_id=FEATURE_ID,
15+
# Not using ``default_true=True`` because we will do this manually in
16+
# the database from the Corporate site only, since this is not required
17+
# in the Community site
18+
# default_true=True,
19+
)
20+
21+
22+
def reverse_add_feature(apps, schema_editor):
23+
Feature = apps.get_model('projects', 'Feature')
24+
Feature.objects.filter(feature_id=FEATURE_ID).delete()
25+
26+
27+
class Migration(migrations.Migration):
28+
29+
dependencies = [
30+
('projects', '0027_remove_json_with_html_feature'),
31+
]
32+
33+
operations = [
34+
migrations.RunPython(forward_add_feature, reverse_add_feature),
35+
]

readthedocs/projects/models.py

+2
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,7 @@ def add_features(sender, **kwargs):
10601060
SKIP_SUBMODULES = 'skip_submodules'
10611061
DONT_OVERWRITE_SPHINX_CONTEXT = 'dont_overwrite_sphinx_context'
10621062
ALLOW_V2_CONFIG_FILE = 'allow_v2_config_file'
1063+
MKDOCS_THEME_RTD = 'mkdocs_theme_rtd'
10631064

10641065
FEATURES = (
10651066
(USE_SPHINX_LATEST, _('Use latest version of Sphinx')),
@@ -1071,6 +1072,7 @@ def add_features(sender, **kwargs):
10711072
'Do not overwrite context vars in conf.py with Read the Docs context',)),
10721073
(ALLOW_V2_CONFIG_FILE, _(
10731074
'Allow to use the v2 of the configuration file')),
1075+
(MKDOCS_THEME_RTD, _('Use Read the Docs theme for MkDocs as default theme')),
10741076
)
10751077

10761078
projects = models.ManyToManyField(

readthedocs/rtd_tests/tests/test_doc_builder.py

+62-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from readthedocs.doc_builder.backends.sphinx import BaseSphinx
2121
from readthedocs.doc_builder.python_environments import Virtualenv
2222
from readthedocs.projects.exceptions import ProjectConfigurationError
23-
from readthedocs.projects.models import Project
23+
from readthedocs.projects.models import Feature, Project
2424

2525

2626
class SphinxBuilderTest(TestCase):
@@ -224,6 +224,67 @@ def test_get_theme_name(self, checkout_path):
224224
}
225225
self.assertEqual(builder.get_theme_name(config), 'mydir')
226226

227+
@patch('readthedocs.doc_builder.base.BaseBuilder.run')
228+
@patch('readthedocs.projects.models.Project.checkout_path')
229+
def test_get_theme_name_with_feature_flag(self, checkout_path, run):
230+
tmpdir = tempfile.mkdtemp()
231+
checkout_path.return_value = tmpdir
232+
Feature.objects.get(
233+
feature_id=Feature.MKDOCS_THEME_RTD,
234+
).projects.add(self.project)
235+
236+
python_env = Virtualenv(
237+
version=self.version,
238+
build_env=self.build_env,
239+
config=None,
240+
)
241+
builder = MkdocsHTML(
242+
build_env=self.build_env,
243+
python_env=python_env,
244+
)
245+
self.assertEqual(builder.get_theme_name({}), 'readthedocs')
246+
with patch('readthedocs.doc_builder.backends.mkdocs.yaml') as mock_yaml:
247+
with patch('readthedocs.doc_builder.backends.mkdocs.MkdocsHTML.load_yaml_config') as mock_load_yaml_config:
248+
mock_load_yaml_config.return_value = {'site_name': self.project.name}
249+
builder.append_conf()
250+
251+
mock_yaml.safe_dump.assert_called_once_with(
252+
{
253+
'site_name': mock.ANY,
254+
'docs_dir': mock.ANY,
255+
'extra_javascript': mock.ANY,
256+
'extra_css': mock.ANY,
257+
'google_analytics': mock.ANY,
258+
'theme': 'readthedocs',
259+
},
260+
mock.ANY,
261+
)
262+
mock_yaml.reset_mock()
263+
264+
config = {
265+
'theme': 'customtheme',
266+
}
267+
self.assertEqual(builder.get_theme_name(config), 'customtheme')
268+
with patch('readthedocs.doc_builder.backends.mkdocs.MkdocsHTML.load_yaml_config') as mock_load_yaml_config:
269+
mock_load_yaml_config.return_value = {
270+
'site_name': self.project.name,
271+
'theme': 'customtheme',
272+
}
273+
builder.append_conf()
274+
275+
mock_yaml.safe_dump.assert_called_once_with(
276+
{
277+
'site_name': mock.ANY,
278+
'docs_dir': mock.ANY,
279+
'extra_javascript': mock.ANY,
280+
'extra_css': mock.ANY,
281+
'google_analytics': mock.ANY,
282+
'theme': 'customtheme',
283+
},
284+
mock.ANY,
285+
)
286+
287+
227288
@patch('readthedocs.doc_builder.base.BaseBuilder.run')
228289
@patch('readthedocs.projects.models.Project.checkout_path')
229290
def test_append_conf_create_yaml(self, checkout_path, run):

0 commit comments

Comments
 (0)