Repository: readthedocs/readthedocs.org

Issue:
Validate docs dir before writing custom js
If the user puts an invalid docs dir, we fail with a generic error, we should raise a more specific one.

https://github.com/rtfd/readthedocs.org/blob/44951fbf8fe1fe12fa9f99f3075f87bd77188525/readthedocs/doc_builder/backends/mkdocs.py#L175-L176


Discussion:
@stsewd Should the validation be here?
https://github.com/rtfd/readthedocs.org/blob/44951fbf8fe1fe12fa9f99f3075f87bd77188525/readthedocs/doc_builder/base.py#L84

or should we put it be before
https://github.com/rtfd/readthedocs.org/blob/44951fbf8fe1fe12fa9f99f3075f87bd77188525/readthedocs/doc_builder/backends/mkdocs.py#L175-L176


Resolving patch:
diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py
--- a/readthedocs/doc_builder/backends/mkdocs.py
+++ b/readthedocs/doc_builder/backends/mkdocs.py
@@ -119,6 +119,7 @@ def append_conf(self, **__):
             )
 
         docs_dir = self.docs_dir(docs_dir=user_docs_dir)
+
         self.create_index(extension='md')
         user_config['docs_dir'] = docs_dir
 
@@ -161,6 +162,12 @@ def append_conf(self, **__):
             docs_dir,
         )
 
+        # if user puts an invalid `docs_dir` path raise an Exception
+        if not os.path.exists(docs_path):
+            raise MkDocsYAMLParseError(
+                MkDocsYAMLParseError.INVALID_DOCS_DIR_PATH,
+            )
+
         # RTD javascript writing
         rtd_data = self.generate_rtd_data(
             docs_dir=os.path.relpath(docs_path, self.root_path),
diff --git a/readthedocs/doc_builder/exceptions.py b/readthedocs/doc_builder/exceptions.py
--- a/readthedocs/doc_builder/exceptions.py
+++ b/readthedocs/doc_builder/exceptions.py
@@ -67,6 +67,11 @@ class MkDocsYAMLParseError(BuildEnvironmentError):
         'string with relative or absolute path.',
     )
 
+    INVALID_DOCS_DIR_PATH = ugettext_noop(
+        'The "docs_dir" config from your MkDocs YAML config file does not '
+        'contain a valid path.',
+    )
+
     INVALID_EXTRA_CONFIG = ugettext_noop(
         'The "{config}" config from your MkDocs YAML config file has to be a '
         'a list of relative paths.',


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_doc_builder.py b/readthedocs/rtd_tests/tests/test_doc_builder.py
--- a/readthedocs/rtd_tests/tests/test_doc_builder.py
+++ b/readthedocs/rtd_tests/tests/test_doc_builder.py
@@ -488,6 +488,38 @@ def test_write_js_data_docs_dir(self, checkout_path, run, generate_rtd_data):
             mkdocs_config=mock.ANY,
         )
 
+    @patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.generate_rtd_data')
+    @patch('readthedocs.projects.models.Project.checkout_path')
+    def test_write_js_data_on_invalid_docs_dir(self, checkout_path, generate_rtd_data):
+        tmpdir = tempfile.mkdtemp()
+        os.mkdir(os.path.join(tmpdir, 'docs'))
+        yaml_file = os.path.join(tmpdir, 'mkdocs.yml')
+        yaml.safe_dump(
+            {
+                'site_name': 'mkdocs',
+                'google_analytics': ['UA-1234-5', 'mkdocs.org'],
+                'docs_dir': 'invalid_docs_dir',
+                'extra_css': [
+                    'http://readthedocs.org/static/css/badge_only.css'
+                ],
+                'extra_javascript': ['readthedocs-data.js'],
+            },
+            open(yaml_file, 'w'),
+        )
+        checkout_path.return_value = tmpdir
+
+        python_env = Virtualenv(
+            version=self.version,
+            build_env=self.build_env,
+            config=None,
+        )
+        self.searchbuilder = MkdocsHTML(
+            build_env=self.build_env,
+            python_env=python_env,
+        )
+        with self.assertRaises(MkDocsYAMLParseError):
+            self.searchbuilder.append_conf()
+
     @patch('readthedocs.doc_builder.base.BaseBuilder.run')
     @patch('readthedocs.projects.models.Project.checkout_path')
     def test_append_conf_existing_yaml_with_extra(self, checkout_path, run):