From 51892492457d55516802c1e53146a3733d1c8d3d Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 12 Dec 2024 16:08:51 -0500 Subject: [PATCH 1/6] Config file: make sphinx or mkdocs configuration required --- readthedocs/config/config.py | 78 ++++++++++++++++++- readthedocs/config/exceptions.py | 3 + readthedocs/config/notifications.py | 26 +++++++ readthedocs/doc_builder/director.py | 7 +- .../rtd_tests/fixtures/spec/v2/schema.json | 6 +- 5 files changed, 114 insertions(+), 6 deletions(-) diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index e1cbbd60b33..fbbc7059cb0 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -1,11 +1,12 @@ """Build configuration for rtd.""" - import copy +import datetime import os import re from contextlib import contextmanager from functools import lru_cache +import pytz from django.conf import settings from pydantic import BaseModel @@ -87,7 +88,13 @@ class BuildConfigBase: version = None - def __init__(self, raw_config, source_file, base_path=None): + def __init__( + self, + raw_config, + source_file, + base_path=None, + validate_deprecated_implicit_keys=None, + ): self._raw_config = copy.deepcopy(raw_config) self.source_config = copy.deepcopy(raw_config) self.source_file = source_file @@ -102,6 +109,25 @@ def __init__(self, raw_config, source_file, base_path=None): self._config = {} + if validate_deprecated_implicit_keys is not None: + self.validate_deprecated_implicit_keys = validate_deprecated_implicit_keys + elif settings.RTD_ENFORCE_BROWNOUTS_FOR_DEPRECATIONS: + tzinfo = pytz.timezone("America/Los_Angeles") + now = datetime.datetime.now(tz=tzinfo) + # Dates as per https://about.readthedocs.com/blog/2024/12/deprecate-config-files-without-sphinx-or-mkdocs-config/ + # fmt: off + self.validate_deprecated_implicit_keys = ( + # 12 hours brownout. + datetime.datetime(2025, 1, 6, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2025, 1, 6, 12, 0, 0, tzinfo=tzinfo) + # 24 hours brownout. + or datetime.datetime(2025, 1, 20, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2025, 1, 21, 0, 0, 0, tzinfo=tzinfo) + # Permanent removal. + or datetime.datetime(2025, 2, 3, 0, 0, 0, tzinfo=tzinfo) < now + ) + # fmt: on + else: + self.validate_deprecated_implicit_keys = False + @contextmanager def catch_validation_error(self, key): """Catch a ``ConfigValidationError`` and raises a ``ConfigError`` error.""" @@ -219,7 +245,6 @@ def __getattr__(self, name): class BuildConfigV2(BuildConfigBase): - """Version 2 of the configuration file.""" version = "2" @@ -251,6 +276,8 @@ def validate(self): self._config["sphinx"] = self.validate_sphinx() self._config["submodules"] = self.validate_submodules() self._config["search"] = self.validate_search() + if self.validate_deprecated_implicit_keys: + self.validate_deprecated_implicit_keys() self.validate_keys() def validate_formats(self): @@ -722,6 +749,48 @@ def validate_search(self): return search + def validate_deprecated_implicit_keys(self): + """ + Check for deprecated usages and raise an exception if found. + + - If the sphinx key is used, a path to the configuration file is required. + - If the mkdocs key is used, a path to the configuration file is required. + - If none of the sphinx or mkdocs keys are used, + and the user isn't overriding the new build jobs, + the sphinx key is explicitly required. + """ + if self.sphinx and not self.sphinx.configuration: + raise ConfigError( + message_id=ConfigError.SPHINX_CONFIG_MISSING, + ) + + if self.mkdocs and not self.mkdocs.configuration: + raise ConfigError( + message_id=ConfigError.MKDOCS_CONFIG_MISSING, + ) + + if not self.new_jobs_overriden and self.sphinx is None and self.mkdocs is None: + raise ConfigError( + message_id=ConfigError.SPHINX_CONFIG_MISSING, + ) + + @property + def new_jobs_overriden(self): + """Check if any of the new (undocumented) build jobs are overridden.""" + build_jobs = self.build.jobs + new_jobs = ( + build_jobs.create_environment, + build_jobs.install, + build_jobs.build.html, + build_jobs.build.pdf, + build_jobs.build.epub, + build_jobs.build.htmlzip, + ) + for job in new_jobs: + if job is not None: + return True + return False + def validate_keys(self): """ Checks that we don't have extra keys (invalid ones). @@ -812,6 +881,9 @@ def doctype(self): if "commands" in self._config["build"] and self._config["build"]["commands"]: return GENERIC + if self.new_jobs_overriden and not self.sphinx and not self.mkdocs: + return GENERIC + if self.mkdocs: return "mkdocs" return self.sphinx.builder diff --git a/readthedocs/config/exceptions.py b/readthedocs/config/exceptions.py index d0fcb30b5f1..fe7c2f36580 100644 --- a/readthedocs/config/exceptions.py +++ b/readthedocs/config/exceptions.py @@ -26,6 +26,9 @@ class ConfigError(BuildUserError): SYNTAX_INVALID = "config:base:invalid-syntax" CONDA_KEY_REQUIRED = "config:conda:required" + SPHINX_CONFIG_MISSING = "config:sphinx:missing-config" + MKDOCS_CONFIG_MISSING = "config:mkdocs:missing-config" + # TODO: improve these error messages shown to the user # See https://github.com/readthedocs/readthedocs.org/issues/10502 diff --git a/readthedocs/config/notifications.py b/readthedocs/config/notifications.py index 954c3bd5f19..bf9c9f37876 100644 --- a/readthedocs/config/notifications.py +++ b/readthedocs/config/notifications.py @@ -361,5 +361,31 @@ ), type=ERROR, ), + Message( + id=ConfigError.SPHINX_CONFIG_MISSING, + header=_("Missing Sphinx configuration key"), + body=_( + textwrap.dedent( + """ + The sphinx.configuration key is missing. + This key is now required, see our blog post for more information. + """ + ).strip(), + ), + type=ERROR, + ), + Message( + id=ConfigError.MKDOCS_CONFIG_MISSING, + header=_("Missing MkDocs configuration key"), + body=_( + textwrap.dedent( + """ + The mkdocs.configuration key is missing. + This key is now required, see our blog post for more information. + """ + ).strip(), + ), + type=ERROR, + ), ] registry.add(messages) diff --git a/readthedocs/doc_builder/director.py b/readthedocs/doc_builder/director.py index 5917b0441ae..ce93c967c7a 100644 --- a/readthedocs/doc_builder/director.py +++ b/readthedocs/doc_builder/director.py @@ -23,7 +23,7 @@ from readthedocs.doc_builder.exceptions import BuildUserError from readthedocs.doc_builder.loader import get_builder_class from readthedocs.doc_builder.python_environments import Conda, Virtualenv -from readthedocs.projects.constants import BUILD_COMMANDS_OUTPUT_PATH_HTML +from readthedocs.projects.constants import BUILD_COMMANDS_OUTPUT_PATH_HTML, GENERIC from readthedocs.projects.exceptions import RepositoryError from readthedocs.projects.signals import after_build, before_build, before_vcs from readthedocs.storage import build_tools_storage @@ -639,6 +639,11 @@ def build_docs_class(self, builder_class): only raise a warning exception here. A hard error will halt the build process. """ + # If the builder is generic, we have nothing to do here, + # as the commnads are provided by the user. + if builder_class == GENERIC: + return + builder = get_builder_class(builder_class)( build_env=self.build_environment, python_env=self.language_environment, diff --git a/readthedocs/rtd_tests/fixtures/spec/v2/schema.json b/readthedocs/rtd_tests/fixtures/spec/v2/schema.json index 716b04e55ef..f11436c0ca6 100644 --- a/readthedocs/rtd_tests/fixtures/spec/v2/schema.json +++ b/readthedocs/rtd_tests/fixtures/spec/v2/schema.json @@ -337,7 +337,8 @@ "default": false } }, - "additionalProperties": false + "additionalProperties": false, + "required": ["configuration"] }, "mkdocs": { "title": "mkdocs", @@ -356,7 +357,8 @@ "default": false } }, - "additionalProperties": false + "additionalProperties": false, + "required": ["configuration"] }, "submodules": { "title": "Submodules", From 2d20d9c86ee5b16497ed1598081106b0ce4b7833 Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 12 Dec 2024 20:07:02 -0500 Subject: [PATCH 2/6] Tests --- readthedocs/config/config.py | 20 ++++---- readthedocs/config/tests/test_config.py | 61 ++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index fbbc7059cb0..f6b79fdf530 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -93,7 +93,7 @@ def __init__( raw_config, source_file, base_path=None, - validate_deprecated_implicit_keys=None, + deprecate_implicit_keys=None, ): self._raw_config = copy.deepcopy(raw_config) self.source_config = copy.deepcopy(raw_config) @@ -109,14 +109,14 @@ def __init__( self._config = {} - if validate_deprecated_implicit_keys is not None: - self.validate_deprecated_implicit_keys = validate_deprecated_implicit_keys + if deprecate_implicit_keys is not None: + self.deprecate_implicit_keys = deprecate_implicit_keys elif settings.RTD_ENFORCE_BROWNOUTS_FOR_DEPRECATIONS: tzinfo = pytz.timezone("America/Los_Angeles") now = datetime.datetime.now(tz=tzinfo) # Dates as per https://about.readthedocs.com/blog/2024/12/deprecate-config-files-without-sphinx-or-mkdocs-config/ # fmt: off - self.validate_deprecated_implicit_keys = ( + self.deprecate_implicit_keys = ( # 12 hours brownout. datetime.datetime(2025, 1, 6, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2025, 1, 6, 12, 0, 0, tzinfo=tzinfo) # 24 hours brownout. @@ -126,7 +126,7 @@ def __init__( ) # fmt: on else: - self.validate_deprecated_implicit_keys = False + self.deprecate_implicit_keys = False @contextmanager def catch_validation_error(self, key): @@ -276,7 +276,7 @@ def validate(self): self._config["sphinx"] = self.validate_sphinx() self._config["submodules"] = self.validate_submodules() self._config["search"] = self.validate_search() - if self.validate_deprecated_implicit_keys: + if self.deprecate_implicit_keys: self.validate_deprecated_implicit_keys() self.validate_keys() @@ -759,17 +759,19 @@ def validate_deprecated_implicit_keys(self): and the user isn't overriding the new build jobs, the sphinx key is explicitly required. """ - if self.sphinx and not self.sphinx.configuration: + has_sphinx_key = "sphinx" in self.source_config + has_mkdocs_key = "mkdocs" in self.source_config + if has_sphinx_key and not self.sphinx.configuration: raise ConfigError( message_id=ConfigError.SPHINX_CONFIG_MISSING, ) - if self.mkdocs and not self.mkdocs.configuration: + if has_mkdocs_key and not self.mkdocs.configuration: raise ConfigError( message_id=ConfigError.MKDOCS_CONFIG_MISSING, ) - if not self.new_jobs_overriden and self.sphinx is None and self.mkdocs is None: + if not self.new_jobs_overriden and not has_sphinx_key and not has_mkdocs_key: raise ConfigError( message_id=ConfigError.SPHINX_CONFIG_MISSING, ) diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index e61d76a5c7a..aca038771d3 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -23,7 +23,7 @@ from .utils import apply_fs -def get_build_config(config, source_file="readthedocs.yml", validate=False): +def get_build_config(config, source_file="readthedocs.yml", validate=False, **kwargs): # I'm adding these defaults here to avoid modifying all the config file from all the tests final_config = { "version": "2", @@ -39,6 +39,7 @@ def get_build_config(config, source_file="readthedocs.yml", validate=False): build_config = BuildConfigV2( final_config, source_file=source_file, + **kwargs, ) if validate: build_config.validate() @@ -1805,6 +1806,64 @@ def test_pop_config_raise_exception(self): assert excinfo.value.format_values.get("value") == "invalid" assert excinfo.value.message_id == ConfigValidationError.VALUE_NOT_FOUND + def test_sphinx_without_explicit_configuration(self): + data = { + "sphinx": {}, + } + get_build_config(data, validate=True) + + with raises(ConfigError) as excinfo: + get_build_config(data, validate=True, deprecate_implicit_keys=True) + + assert excinfo.value.message_id == ConfigError.SPHINX_CONFIG_MISSING + + data["sphinx"]["configuration"] = "conf.py" + get_build_config(data, validate=True, deprecate_implicit_keys=True) + + def test_mkdocs_without_explicit_configuration(self): + data = { + "mkdocs": {}, + } + get_build_config(data, validate=True) + + with raises(ConfigError) as excinfo: + get_build_config(data, validate=True, deprecate_implicit_keys=True) + + assert excinfo.value.message_id == ConfigError.MKDOCS_CONFIG_MISSING + + data["mkdocs"]["configuration"] = "mkdocs.yml" + get_build_config(data, validate=True, deprecate_implicit_keys=True) + + def test_config_without_sphinx_key(self): + data = { + "build": { + "os": "ubuntu-22.04", + "tools": { + "python": "3", + }, + "jobs": {}, + }, + } + get_build_config(data, validate=True) + + with raises(ConfigError) as excinfo: + get_build_config(data, validate=True, deprecate_implicit_keys=True) + + assert excinfo.value.message_id == ConfigError.SPHINX_CONFIG_MISSING + + # No exception should be raised when overriding any of the the new jobs. + data_copy = data.copy() + data_copy["build"]["jobs"]["create_environment"] = ["echo 'Hello World'"] + get_build_config(data_copy, validate=True, deprecate_implicit_keys=True) + + data_copy = data.copy() + data_copy["build"]["jobs"]["install"] = ["echo 'Hello World'"] + get_build_config(data_copy, validate=True, deprecate_implicit_keys=True) + + data_copy = data.copy() + data_copy["build"]["jobs"]["build"] = {"html": ["echo 'Hello World'"]} + get_build_config(data_copy, validate=True, deprecate_implicit_keys=True) + def test_as_dict_new_build_config(self, tmpdir): build = get_build_config( { From 1a0cfc90ebe255ebb6f4b6f9d32ffb41550f8055 Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 12 Dec 2024 22:08:20 -0500 Subject: [PATCH 3/6] Match build process --- readthedocs/config/config.py | 4 +- .../doc_builder/python_environments.py | 5 + .../projects/tests/test_build_tasks.py | 234 ++++++++++++++++++ 3 files changed, 242 insertions(+), 1 deletion(-) diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index f6b79fdf530..408f0f2a0a6 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -883,7 +883,9 @@ def doctype(self): if "commands" in self._config["build"] and self._config["build"]["commands"]: return GENERIC - if self.new_jobs_overriden and not self.sphinx and not self.mkdocs: + has_sphinx_key = "sphinx" in self.source_config + has_mkdocs_key = "mkdocs" in self.source_config + if self.new_jobs_overriden and not has_sphinx_key and not has_mkdocs_key: return GENERIC if self.mkdocs: diff --git a/readthedocs/doc_builder/python_environments.py b/readthedocs/doc_builder/python_environments.py index 06d1b59f82e..80ccb3407fc 100644 --- a/readthedocs/doc_builder/python_environments.py +++ b/readthedocs/doc_builder/python_environments.py @@ -11,6 +11,7 @@ from readthedocs.config.models import PythonInstall, PythonInstallRequirements from readthedocs.core.utils.filesystem import safe_open from readthedocs.doc_builder.config import load_yaml_config +from readthedocs.projects.constants import GENERIC from readthedocs.projects.exceptions import UserFileNotFound from readthedocs.projects.models import Feature @@ -168,6 +169,10 @@ def _install_latest_requirements(self, pip_install_cmd): cwd=self.checkout_path, ) + # Nothing else to install for generic projects. + if self.config.doctype == GENERIC: + return + # Second, install all the latest core requirements requirements = [] diff --git a/readthedocs/projects/tests/test_build_tasks.py b/readthedocs/projects/tests/test_build_tasks.py index 7e5639224a3..454c805d5af 100644 --- a/readthedocs/projects/tests/test_build_tasks.py +++ b/readthedocs/projects/tests/test_build_tasks.py @@ -1290,6 +1290,240 @@ def test_build_jobs_partial_build_override(self, load_yaml_config): ] ) + @mock.patch("readthedocs.doc_builder.director.load_yaml_config") + def test_build_jobs_partial_build_override_without_sphinx(self, load_yaml_config): + config = BuildConfigV2( + { + "version": 2, + "formats": ["pdf", "epub", "htmlzip"], + "build": { + "os": "ubuntu-24.04", + "tools": {"python": "3.12"}, + "jobs": { + "build": { + "html": ["echo build html"], + }, + "post_build": ["echo end of build"], + }, + }, + }, + source_file="readthedocs.yml", + ) + config.validate() + load_yaml_config.return_value = config + self._trigger_update_docs_task() + + python_version = settings.RTD_DOCKER_BUILD_SETTINGS["tools"]["python"]["3.12"] + self.mocker.mocks["environment.run"].assert_has_calls( + [ + mock.call("asdf", "install", "python", python_version), + mock.call("asdf", "global", "python", python_version), + mock.call("asdf", "reshim", "python", record=False), + mock.call( + "python", + "-mpip", + "install", + "-U", + "virtualenv", + "setuptools", + ), + mock.call( + "python", + "-mvirtualenv", + "$READTHEDOCS_VIRTUALENV_PATH", + bin_path=None, + cwd=None, + ), + mock.call( + mock.ANY, + "-m", + "pip", + "install", + "--upgrade", + "--no-cache-dir", + "pip", + "setuptools", + bin_path=mock.ANY, + cwd=mock.ANY, + ), + mock.call( + "echo build html", + escape_command=False, + cwd=mock.ANY, + ), + mock.call( + "echo end of build", + escape_command=False, + cwd=mock.ANY, + ), + ] + ) + + @mock.patch("readthedocs.doc_builder.director.load_yaml_config") + def test_build_jobs_partial_build_override_sphinx(self, load_yaml_config): + config = BuildConfigV2( + { + "version": 2, + "sphinx": { + "configuration": "docs/conf.py", + }, + "build": { + "os": "ubuntu-24.04", + "tools": {"python": "3.12"}, + "jobs": { + "build": { + "html": ["echo build html"], + }, + "post_build": ["echo end of build"], + }, + }, + }, + source_file="readthedocs.yml", + ) + config.validate() + load_yaml_config.return_value = config + self._trigger_update_docs_task() + + python_version = settings.RTD_DOCKER_BUILD_SETTINGS["tools"]["python"]["3.12"] + self.mocker.mocks["environment.run"].assert_has_calls( + [ + mock.call("asdf", "install", "python", python_version), + mock.call("asdf", "global", "python", python_version), + mock.call("asdf", "reshim", "python", record=False), + mock.call( + "python", + "-mpip", + "install", + "-U", + "virtualenv", + "setuptools", + ), + mock.call( + "python", + "-mvirtualenv", + "$READTHEDOCS_VIRTUALENV_PATH", + bin_path=None, + cwd=None, + ), + mock.call( + mock.ANY, + "-m", + "pip", + "install", + "--upgrade", + "--no-cache-dir", + "pip", + "setuptools", + bin_path=mock.ANY, + cwd=mock.ANY, + ), + mock.call( + mock.ANY, + "-m", + "pip", + "install", + "--upgrade", + "--no-cache-dir", + "sphinx", + bin_path=mock.ANY, + cwd=mock.ANY, + ), + mock.call( + "echo build html", + escape_command=False, + cwd=mock.ANY, + ), + mock.call( + "echo end of build", + escape_command=False, + cwd=mock.ANY, + ), + ] + ) + + @mock.patch("readthedocs.doc_builder.director.load_yaml_config") + def test_build_jobs_partial_build_override_mkdocs(self, load_yaml_config): + config = BuildConfigV2( + { + "version": 2, + "formats": ["pdf", "epub", "htmlzip"], + "mkdocs": { + "configuration": "mkdocs.yml", + }, + "build": { + "os": "ubuntu-24.04", + "tools": {"python": "3.12"}, + "jobs": { + "build": { + "html": ["echo build html"], + }, + "post_build": ["echo end of build"], + }, + }, + }, + source_file="readthedocs.yml", + ) + config.validate() + load_yaml_config.return_value = config + self._trigger_update_docs_task() + + python_version = settings.RTD_DOCKER_BUILD_SETTINGS["tools"]["python"]["3.12"] + self.mocker.mocks["environment.run"].assert_has_calls( + [ + mock.call("asdf", "install", "python", python_version), + mock.call("asdf", "global", "python", python_version), + mock.call("asdf", "reshim", "python", record=False), + mock.call( + "python", + "-mpip", + "install", + "-U", + "virtualenv", + "setuptools", + ), + mock.call( + "python", + "-mvirtualenv", + "$READTHEDOCS_VIRTUALENV_PATH", + bin_path=None, + cwd=None, + ), + mock.call( + mock.ANY, + "-m", + "pip", + "install", + "--upgrade", + "--no-cache-dir", + "pip", + "setuptools", + bin_path=mock.ANY, + cwd=mock.ANY, + ), + mock.call( + mock.ANY, + "-m", + "pip", + "install", + "--upgrade", + "--no-cache-dir", + "mkdocs", + bin_path=mock.ANY, + cwd=mock.ANY, + ), + mock.call( + "echo build html", + escape_command=False, + cwd=mock.ANY, + ), + mock.call( + "echo end of build", + escape_command=False, + cwd=mock.ANY, + ), + ] + ) + @mock.patch("readthedocs.doc_builder.director.load_yaml_config") def test_build_jobs_partial_build_override_empty_commands(self, load_yaml_config): config = BuildConfigV2( From 2d02f5f0cb40b29ad04e2a559923cc276c912e8c Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 12 Dec 2024 22:23:34 -0500 Subject: [PATCH 4/6] Update docs --- docs/user/config-file/v2.rst | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/user/config-file/v2.rst b/docs/user/config-file/v2.rst index 48e3b465e42..30ee7bff304 100644 --- a/docs/user/config-file/v2.rst +++ b/docs/user/config-file/v2.rst @@ -493,8 +493,7 @@ The ``$READTHEDOCS_OUTPUT/html`` directory will be uploaded and hosted by Read t sphinx ~~~~~~ -Configuration for Sphinx documentation -(this is the default documentation type). +Configuration for Sphinx documentation. .. code-block:: yaml @@ -535,10 +534,7 @@ sphinx.configuration The path to the ``conf.py`` file, relative to the root of the project. :Type: ``path`` -:Default: ``null`` - -If the value is ``null``, -Read the Docs will try to find a ``conf.py`` file in your project. +:Required: ``true`` sphinx.fail_on_warning `````````````````````` @@ -580,10 +576,7 @@ mkdocs.configuration The path to the ``mkdocs.yml`` file, relative to the root of the project. :Type: ``path`` -:Default: ``null`` - -If the value is ``null``, -Read the Docs will try to find a ``mkdocs.yml`` file in your project. +:Required: ``true`` mkdocs.fail_on_warning `````````````````````` From c20e209b2e015fa4e464b1b91143701b48f34f6b Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 17 Dec 2024 10:58:39 +0100 Subject: [PATCH 5/6] Match deprecation timelines with the blog post We updated these dates there to move faster. --- readthedocs/config/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 408f0f2a0a6..e3c96d73e0f 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -120,9 +120,9 @@ def __init__( # 12 hours brownout. datetime.datetime(2025, 1, 6, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2025, 1, 6, 12, 0, 0, tzinfo=tzinfo) # 24 hours brownout. - or datetime.datetime(2025, 1, 20, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2025, 1, 21, 0, 0, 0, tzinfo=tzinfo) + or datetime.datetime(2025, 1, 13, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2025, 1, 14, 0, 0, 0, tzinfo=tzinfo) # Permanent removal. - or datetime.datetime(2025, 2, 3, 0, 0, 0, tzinfo=tzinfo) < now + or datetime.datetime(2025, 1, 20, 0, 0, 0, tzinfo=tzinfo) < now ) # fmt: on else: From 8e7efe01f0a1520687b2b3a856a95577a0402fbb Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 17 Dec 2024 17:29:45 +0100 Subject: [PATCH 6/6] Skip running default commands when GENRIC doctool (#11863) Continues the work done in #11852 to allow builds without `sphinx` or `mkdocs` config keys to work. --- readthedocs/doc_builder/director.py | 11 +++++++++++ .../projects/tests/test_build_tasks.py | 19 ------------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/readthedocs/doc_builder/director.py b/readthedocs/doc_builder/director.py index ce93c967c7a..11514a64deb 100644 --- a/readthedocs/doc_builder/director.py +++ b/readthedocs/doc_builder/director.py @@ -307,6 +307,12 @@ def create_environment(self): if self.data.config.build.jobs.create_environment is not None: self.run_build_job("create_environment") return + + # If the builder is generic, we have nothing to do here, + # as the commnads are provided by the user. + if self.data.config.doctype == GENERIC: + return + self.language_environment.setup_base() # Install @@ -315,6 +321,11 @@ def install(self): self.run_build_job("install") return + # If the builder is generic, we have nothing to do here, + # as the commnads are provided by the user. + if self.data.config.doctype == GENERIC: + return + self.language_environment.install_core_requirements() self.language_environment.install_requirements() diff --git a/readthedocs/projects/tests/test_build_tasks.py b/readthedocs/projects/tests/test_build_tasks.py index 454c805d5af..9941d9fdf16 100644 --- a/readthedocs/projects/tests/test_build_tasks.py +++ b/readthedocs/projects/tests/test_build_tasks.py @@ -1327,25 +1327,6 @@ def test_build_jobs_partial_build_override_without_sphinx(self, load_yaml_config "virtualenv", "setuptools", ), - mock.call( - "python", - "-mvirtualenv", - "$READTHEDOCS_VIRTUALENV_PATH", - bin_path=None, - cwd=None, - ), - mock.call( - mock.ANY, - "-m", - "pip", - "install", - "--upgrade", - "--no-cache-dir", - "pip", - "setuptools", - bin_path=mock.ANY, - cwd=mock.ANY, - ), mock.call( "echo build html", escape_command=False,