Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement multiple .readthedocs.yml files per repo #10001

Merged
merged 51 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
308c60e
initial work at multiple .readthedocs.yml files per repo
ewdurbin Feb 7, 2023
89fc389
darker
ewdurbin Feb 7, 2023
1bd6a69
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Mar 10, 2023
80c3140
Updates: Rename new field to build_config_file, add on Build and Vers…
benjaoming Mar 10, 2023
59d3a31
Fix for test failures
benjaoming Mar 10, 2023
f91201b
Fix up tests to expect no build_config_file when default is used
benjaoming Mar 10, 2023
7ce5271
Trimming down the scope by not storing a Version.build_config_path
benjaoming Mar 13, 2023
0199252
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Mar 16, 2023
0683268
DDD = Documentation-driven-development: Adding the first draft for a …
benjaoming Mar 16, 2023
d16f694
How-to: Rephrase bad intro
benjaoming Mar 16, 2023
69c9bb3
Basic test that a custom path is applied and loaded, default is then …
benjaoming Mar 20, 2023
f66fc38
Test that a project-defined build config is read by build tasks
benjaoming Mar 20, 2023
7003d68
Validate properly in clean_conf_py_file
benjaoming Mar 20, 2023
7fd841f
Validate input, check that we can also handle non-yaml extensions, ad…
benjaoming Mar 20, 2023
ab15ce4
Handle unrelated linting errors
benjaoming Mar 20, 2023
50a83e6
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Mar 20, 2023
4999c24
Update migration
benjaoming Mar 20, 2023
78daf56
New validation for build config file
benjaoming Mar 21, 2023
b85ad31
Don't fix clean_conf_py_file
benjaoming Mar 21, 2023
e2f9231
Update help_text and test case name
benjaoming Mar 21, 2023
9653001
Apply suggestions from @ericholscher's code review
benjaoming Mar 21, 2023
3c76274
Disable fetching per-build build_config_file + clarify logic about re…
benjaoming Mar 21, 2023
412ba34
New validator, configurable with allowed file names and no regex @hum…
benjaoming Mar 21, 2023
d713461
Update readthedocs/doc_builder/director.py
benjaoming Mar 21, 2023
153a5fc
Use new validator on model fields and add database assertion on view …
benjaoming Mar 21, 2023
6f0e143
Merge branch 'multi_config' of github.com:ewdurbin/readthedocs.org in…
benjaoming Mar 21, 2023
1ee67fc
Swap around if condition blocks
benjaoming Mar 21, 2023
eb6f42b
Apply suggestions from @humitos code review
benjaoming Mar 21, 2023
1406939
Update docs/user/guides/setup/monorepo.rst
benjaoming Mar 21, 2023
7d89d69
Improve "next steps"
benjaoming Mar 21, 2023
6d420f1
Unrelated: Fix breakage from similar issue as https://github.com/exec…
benjaoming Mar 21, 2023
59e9cac
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Mar 21, 2023
3c8a61c
I have no idea about why pre-commit in Circle CI wants this and my lo…
benjaoming Mar 21, 2023
fbd82a0
Update migrations + refactor validator, it wasn't serializable for th…
benjaoming Mar 21, 2023
d568862
Add a seealso for subprojects
benjaoming Mar 21, 2023
7d9d830
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Mar 22, 2023
6e6c682
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Mar 28, 2023
9f07682
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Apr 4, 2023
dbc52a5
Refactor build_config_file=>readthedocs_yaml_path, config_file=>readt…
benjaoming Apr 4, 2023
bbd5809
Remove view test from projecs/tests/test_views, this module seems to …
benjaoming Apr 4, 2023
91cfda7
Anohter renaming fix config_file=>readthedocs_yaml_path
benjaoming Apr 4, 2023
7844c10
Improve help text
benjaoming Apr 4, 2023
9a09825
Fix a strange formulation in docs
benjaoming Apr 4, 2023
2b1521b
Mark validators as safe strings
benjaoming Apr 4, 2023
faa8f95
Clarify paths that are tested wrt. validation
benjaoming Apr 13, 2023
6eca6ee
Update readthedocs/doc_builder/director.py
benjaoming Apr 13, 2023
e0aa2c5
Removing pattern from a more generic validator
benjaoming Apr 13, 2023
03afea0
Add link to Wikipedia definition
benjaoming Apr 13, 2023
32c87a7
Mention ability to use different documentation tools. Clarify introdu…
benjaoming Apr 13, 2023
9667c19
Arrested Software Development: Hello darker my old friend, you've com…
benjaoming Apr 13, 2023
dbbbdec
Merge branch 'main' of github.com:readthedocs/readthedocs.org into mu…
benjaoming Apr 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions readthedocs/api/v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,26 +73,27 @@ def get_skip(self, obj):

class Meta(ProjectSerializer.Meta):
fields = ProjectSerializer.Meta.fields + (
'enable_epub_build',
'enable_pdf_build',
'conf_py_file',
'analytics_code',
'analytics_disabled',
'cdn_enabled',
'container_image',
'container_mem_limit',
'container_time_limit',
'install_project',
'use_system_packages',
'skip',
'requirements_file',
'python_interpreter',
'features',
'has_valid_clone',
'has_valid_webhook',
'show_advertising',
'environment_variables',
'max_concurrent_builds',
"enable_epub_build",
"enable_pdf_build",
"conf_py_file",
"analytics_code",
"analytics_disabled",
"cdn_enabled",
"container_image",
"container_mem_limit",
"container_time_limit",
"install_project",
"use_system_packages",
"skip",
"requirements_file",
"python_interpreter",
"features",
"has_valid_clone",
"has_valid_webhook",
"show_advertising",
"environment_variables",
"max_concurrent_builds",
"rtd_conf_file",
)


Expand Down
11 changes: 9 additions & 2 deletions readthedocs/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1369,15 +1369,22 @@ def search(self):
return Search(**self._config['search'])


def load(path, env_config):
def load(path, env_config, config_file=None):
"""
Load a project configuration and the top-most build config for a given path.

That is usually the root of the project, but will look deeper. According to
the version of the configuration a build object would be load and validated,
``BuildConfigV1`` is the default.
"""
filename = find_one(path, CONFIG_FILENAME_REGEX)
if config_file is None or config_file == "":
filename = find_one(path, CONFIG_FILENAME_REGEX)
else:
filename = os.path.join(path, config_file)
if not os.path.exists(filename):
raise ConfigError(
f".readthedocs.yml not found at {config_file}", CONFIG_FILE_REQUIRED
)

benjaoming marked this conversation as resolved.
Show resolved Hide resolved
if not filename:
raise ConfigFileNotFound(path)
Expand Down
1 change: 1 addition & 0 deletions readthedocs/doc_builder/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def load_yaml_config(version):
config = load_config(
path=checkout_path,
env_config=env_config,
config_file=project.rtd_conf_file,
)
except ConfigFileNotFound:
# Default to use v1 with some defaults from the web interface
Expand Down
30 changes: 21 additions & 9 deletions readthedocs/projects/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,16 @@ class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm):
class Meta:
model = Project
per_project_settings = (
'default_version',
'default_branch',
'privacy_level',
'analytics_code',
'analytics_disabled',
'show_version_warning',
'single_version',
'external_builds_enabled',
'external_builds_privacy_level',
"default_version",
"default_branch",
"privacy_level",
"analytics_code",
"analytics_disabled",
"show_version_warning",
"single_version",
"external_builds_enabled",
"external_builds_privacy_level",
"rtd_conf_file",
)
# These that can be set per-version using a config file.
per_version_settings = (
Expand Down Expand Up @@ -357,6 +358,17 @@ def clean_conf_py_file(self):
) # yapf: disable
return filename

def clean_rtd_conf_file(self):
filename = self.cleaned_data.get("rtd_conf_file", "").strip()
if filename and ".readthedocs.yml" not in filename:
benjaoming marked this conversation as resolved.
Show resolved Hide resolved
raise forms.ValidationError(
_(
"Your configuration file is invalid, make sure it contains "
".readthedocs.yml in it.",
),
) # yapf: disable
return filename

def get_all_active_versions(self):
"""
Returns all active versions.
Expand Down
35 changes: 35 additions & 0 deletions readthedocs/projects/migrations/0096_auto_20230207_1642.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 3.2.17 on 2023-02-07 16:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("projects", "0095_default_branch_helptext"),
]

operations = [
migrations.AddField(
model_name="historicalproject",
name="rtd_conf_file",
field=models.CharField(
blank=True,
default="",
help_text="Path from project root to <code>.readthedocs.yml</code> file (ex. <code>docs/.readthedocs.yml</code>). Leave blank if you want us to find it for you.",
max_length=255,
verbose_name=".readthedocs.yml configuration file",
),
),
migrations.AddField(
model_name="project",
name="rtd_conf_file",
field=models.CharField(
blank=True,
default="",
help_text="Path from project root to <code>.readthedocs.yml</code> file (ex. <code>docs/.readthedocs.yml</code>). Leave blank if you want us to find it for you.",
max_length=255,
verbose_name=".readthedocs.yml configuration file",
),
),
]
17 changes: 14 additions & 3 deletions readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,20 @@ class Project(models.Model):
default='',
blank=True,
help_text=_(
'Path from project root to <code>conf.py</code> file '
'(ex. <code>docs/conf.py</code>). '
'Leave blank if you want us to find it for you.',
"Path from project root to <code>conf.py</code> file "
"(ex. <code>docs/conf.py</code>). "
"Leave blank if you want us to find it for you.",
),
)
rtd_conf_file = models.CharField(
_(".readthedocs.yml configuration file"),
max_length=255,
default="",
blank=True,
benjaoming marked this conversation as resolved.
Show resolved Hide resolved
help_text=_(
"Path from project root to <code>.readthedocs.yml</code> file "
"(ex. <code>docs/.readthedocs.yml</code>). "
"Leave blank if you want us to find it for you.",
benjaoming marked this conversation as resolved.
Show resolved Hide resolved
),
)

Expand Down
14 changes: 10 additions & 4 deletions readthedocs/rtd_tests/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,19 +686,24 @@ class APITests(TestCase):
def test_user_doesnt_get_full_api_return(self):
user_normal = get(User, is_staff=False)
user_admin = get(User, is_staff=True)
project = get(Project, main_language_project=None, conf_py_file='foo')
project = get(
Project, main_language_project=None, conf_py_file="foo", rtd_conf_file="bar"
)
client = APIClient()

client.force_authenticate(user=user_normal)
resp = client.get('/api/v2/project/%s/' % (project.pk))
self.assertEqual(resp.status_code, 200)
self.assertNotIn('conf_py_file', resp.data)
self.assertNotIn("conf_py_file", resp.data)
self.assertNotIn("rtd_conf_file", resp.data)

client.force_authenticate(user=user_admin)
resp = client.get('/api/v2/project/%s/' % (project.pk))
self.assertEqual(resp.status_code, 200)
self.assertIn('conf_py_file', resp.data)
self.assertEqual(resp.data['conf_py_file'], 'foo')
self.assertIn("conf_py_file", resp.data)
self.assertEqual(resp.data["conf_py_file"], "foo")
self.assertIn("rtd_conf_file", resp.data)
self.assertEqual(resp.data["rtd_conf_file"], "bar")

def test_project_features(self):
user = get(User, is_staff=True)
Expand Down Expand Up @@ -2453,6 +2458,7 @@ def test_get_version_by_id(self):
"repo": "https://github.com/pypa/pip",
"repo_type": "git",
"requirements_file": None,
"rtd_conf_file": "",
"show_advertising": True,
"skip": False,
"slug": "pip",
Expand Down
7 changes: 4 additions & 3 deletions readthedocs/rtd_tests/tests/test_config_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def create_load(config=None):
if config is None:
config = {}

def inner(path=None, env_config=None):
def inner(path=None, env_config=None, config_file=None):
env_config_defaults = {
'output_base': '',
'name': '1',
Expand Down Expand Up @@ -97,8 +97,9 @@ def test_python_supported_versions_default_image_1_0(self, load_config):
expected_env_config.update(img_settings)

load_config.assert_called_once_with(
path=mock.ANY,
env_config=expected_env_config,
path=mock.ANY,
env_config=expected_env_config,
config_file="",
)
self.assertEqual(config.python.version, '3')

Expand Down