Skip to content

Commit e9cead9

Browse files
committed
Merge branch 'rel' of github.com:readthedocs/readthedocs.org into rel
2 parents 93c9005 + 9704b67 commit e9cead9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+529
-135
lines changed

CHANGELOG.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
1+
Version 4.1.0
2+
-------------
3+
4+
:Date: March 17, 2020
5+
6+
* `@ericholscher <https://github.com/ericholscher>`__: Properly proxy the Proxito headers via nginx/sendfile (`#6782 <https://github.com/readthedocs/readthedocs.org/pull/6782>`__)
7+
* `@ericholscher <https://github.com/ericholscher>`__: Revert "Add feature flag to just completely skip sync and symlink operations (#6689)" (`#6781 <https://github.com/readthedocs/readthedocs.org/pull/6781>`__)
8+
* `@humitos <https://github.com/humitos>`__: Upgrade django-storages to support URLs with more http methods (`#6771 <https://github.com/readthedocs/readthedocs.org/pull/6771>`__)
9+
* `@davidfischer <https://github.com/davidfischer>`__: Use the hotfixed version of django-messages-extends (`#6767 <https://github.com/readthedocs/readthedocs.org/pull/6767>`__)
10+
* `@ericholscher <https://github.com/ericholscher>`__: Release 4.0.3 (`#6766 <https://github.com/readthedocs/readthedocs.org/pull/6766>`__)
11+
* `@stsewd <https://github.com/stsewd>`__: Document usage or pytest marks (`#6764 <https://github.com/readthedocs/readthedocs.org/pull/6764>`__)
12+
* `@humitos <https://github.com/humitos>`__: Pull/Push cached environment using storage (`#6763 <https://github.com/readthedocs/readthedocs.org/pull/6763>`__)
13+
* `@stsewd <https://github.com/stsewd>`__: Refactor search view to make use of permission_classes (`#6761 <https://github.com/readthedocs/readthedocs.org/pull/6761>`__)
14+
* `@stsewd <https://github.com/stsewd>`__: Run proxito tests with proxito (`#6714 <https://github.com/readthedocs/readthedocs.org/pull/6714>`__)
15+
* `@stsewd <https://github.com/stsewd>`__: Proxy footer api on docs' domains (`#6630 <https://github.com/readthedocs/readthedocs.org/pull/6630>`__)
16+
17+
Version 4.0.3
18+
-------------
19+
20+
:Date: March 10, 2020
21+
22+
* `@stsewd <https://github.com/stsewd>`__: Document usage or pytest marks (`#6764 <https://github.com/readthedocs/readthedocs.org/pull/6764>`__)
23+
* `@stsewd <https://github.com/stsewd>`__: Update some dependencies (`#6762 <https://github.com/readthedocs/readthedocs.org/pull/6762>`__)
24+
* `@stsewd <https://github.com/stsewd>`__: Refactor search view to make use of permission_classes (`#6761 <https://github.com/readthedocs/readthedocs.org/pull/6761>`__)
25+
* `@ericholscher <https://github.com/ericholscher>`__: Revert "Merge pull request #6739 from readthedocs/agj/docs-tos-pdf" (`#6760 <https://github.com/readthedocs/readthedocs.org/pull/6760>`__)
26+
* `@ericholscher <https://github.com/ericholscher>`__: Expand the logic in our proxito mixin. (`#6759 <https://github.com/readthedocs/readthedocs.org/pull/6759>`__)
27+
* `@comradekingu <https://github.com/comradekingu>`__: Spelling: "Set up your environment" (`#6752 <https://github.com/readthedocs/readthedocs.org/pull/6752>`__)
28+
* `@humitos <https://github.com/humitos>`__: Use `storage.exists` on HEAD method (`#6751 <https://github.com/readthedocs/readthedocs.org/pull/6751>`__)
29+
* `@humitos <https://github.com/humitos>`__: Pull only latest image for development (`#6750 <https://github.com/readthedocs/readthedocs.org/pull/6750>`__)
30+
* `@humitos <https://github.com/humitos>`__: Update common submodule (`#6749 <https://github.com/readthedocs/readthedocs.org/pull/6749>`__)
31+
* `@ericholscher <https://github.com/ericholscher>`__: Release 4.0.2 (`#6741 <https://github.com/readthedocs/readthedocs.org/pull/6741>`__)
32+
* `@agjohnson <https://github.com/agjohnson>`__: Add TOS PDF output (`#6739 <https://github.com/readthedocs/readthedocs.org/pull/6739>`__)
33+
* `@ericholscher <https://github.com/ericholscher>`__: Don't call virtualenv with `--no-site-packages` (`#6738 <https://github.com/readthedocs/readthedocs.org/pull/6738>`__)
34+
* `@GallowayJ <https://github.com/GallowayJ>`__: Drop mock dependency (`#6723 <https://github.com/readthedocs/readthedocs.org/pull/6723>`__)
35+
* `@stsewd <https://github.com/stsewd>`__: Run proxito tests with proxito (`#6714 <https://github.com/readthedocs/readthedocs.org/pull/6714>`__)
36+
* `@humitos <https://github.com/humitos>`__: New block on footer template to override from corporate (`#6702 <https://github.com/readthedocs/readthedocs.org/pull/6702>`__)
37+
* `@humitos <https://github.com/humitos>`__: Point users to support email instead asking to open an issue (`#6650 <https://github.com/readthedocs/readthedocs.org/pull/6650>`__)
38+
* `@stsewd <https://github.com/stsewd>`__: Proxy footer api on docs' domains (`#6630 <https://github.com/readthedocs/readthedocs.org/pull/6630>`__)
39+
140
Version 4.0.2
241
-------------
342

dockerfiles/nginx/proxito.conf

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ server {
2020
location / {
2121
proxy_pass http://proxito:8000;
2222
proxy_set_header Host $host;
23-
proxy_intercept_errors on;
24-
error_page 404 = @notfoundfallback;
23+
proxy_set_header X-Real-IP $remote_addr;
24+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
25+
proxy_set_header X-Forwarded-Host $host;
26+
27+
proxy_intercept_errors on;
28+
error_page 404 = @proxito404;
2529

2630
add_header X-Served Django-Proxito always;
2731
}
@@ -31,17 +35,29 @@ server {
3135
internal;
3236
# Nginx will strip the `/proxito/` and pass just the `$storage/$type/$proj/$ver/$filename`
3337
proxy_pass http://storage:10000/;
38+
3439
proxy_set_header Host storage:10000;
3540
proxy_set_header X-Real-IP $remote_addr;
3641
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
3742
proxy_set_header X-Forwarded-Host $host;
3843

39-
4044
proxy_intercept_errors on;
4145
error_page 404 = @notfoundfallback;
4246

43-
add_header X-Served Nginx-Proxito-Sendfile always;
44-
add_header X-RTD-Project $upstream_http_x_rtd_project always;
47+
# Nginx is silly, so we have to *set* these then use them.
48+
# Using them directly gets the values from the storage upstream.
49+
# I think the `set` must get read before doing the proxy_pass
50+
# but the `add_header` is read after
51+
set $rtd_project $upstream_http_x_rtd_project;
52+
add_header X-RTD-Project $rtd_project always;
53+
set $rtd_version $upstream_http_x_rtd_version;
54+
add_header X-RTD-Version $rtd_version always;
55+
set $rtd_path $upstream_http_x_rtd_path;
56+
add_header X-RTD-Path $rtd_path always;
57+
set $rtd_domain $upstream_http_x_rtd_domain;
58+
add_header X-RTD-Domain $rtd_domain always;
59+
set $rtd_method $upstream_http_x_rtd_version_method;
60+
add_header X-RTD-Version-Method $rtd_method always;
4561
}
4662

4763
# Serve 404 pages here

docs/development/standards.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ Search enabled by default
7272
Besides, all the documentation indexes are updated after a build is finished.
7373

7474

75-
Setup your environment
76-
----------------------
75+
Set up your environment
76+
-----------------------
7777

7878
After cloning ``readthedocs.org`` repository, you need to
7979

@@ -105,7 +105,7 @@ After cloning ``readthedocs.org`` repository, you need to
105105

106106
.. prompt:: bash
107107

108-
inv docker.pull
108+
inv docker.pull --only-latest
109109

110110
#. start all the containers:
111111

@@ -172,6 +172,7 @@ save some work while typing docker compose commands. This section explains these
172172
``inv docker.pull``
173173
Downloads and tags all the Docker images required for builders.
174174

175+
* ``--only-latest`` does not pull ``stable`` and ``testing`` images.
175176

176177
Adding a new Python dependency
177178
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

docs/development/tests.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,33 @@ docs
5959
.. _`pyflakes`: https://github.com/pyflakes/pyflakes
6060
.. _`pep8`: https://pep8.readthedocs.io/en/latest/index.html
6161

62+
63+
Pytest marks
64+
------------
65+
66+
The Read the Docs code base is deployed as three instances:
67+
68+
- Main: where you can see the dashboard.
69+
- Build: where the builds happen.
70+
- Serve/proxito: It is in charge of serving the documentation pages.
71+
72+
Each instance has its own settings.
73+
To make sure we test each part as close as possible to its real settings,
74+
we use `pytest marks <https://docs.pytest.org/en/latest/mark.html>`__.
75+
This allow us to run each set of tests with different settings files,
76+
or skip some (like search tests)::
77+
78+
79+
DJANGO_SETTINGS_MODULE=custom.settings.file pytest -m mark
80+
DJANGO_SETTINGS_MODULE=another.settings.file pytest -m "not mark"
81+
82+
Current marks are:
83+
84+
- search (tests that require Elastic Search)
85+
- proxito (tests from the serve/proxito instance)
86+
87+
Tests without mark are from the main instance.
88+
6289
Continuous Integration
6390
----------------------
6491

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ addopts = --reuse-db --strict-markers
33
markers =
44
search
55
serve
6+
proxito
67
python_files = tests.py test_*.py *_tests.py
78
filterwarnings =
89
# Ignore external dependencies warning deprecations

readthedocs/api/v2/permissions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class IsAuthorizedToViewVersion(permissions.BasePermission):
9191
"""
9292
Checks if the user from the request has permissions to see the version.
9393
94-
This permission class used in the FooterHTML view.
94+
This permission class used in the FooterHTML and PageSearchAPIView views.
9595
9696
.. note::
9797

readthedocs/api/v2/proxied_urls.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
Proxied API URLs.
3+
4+
Served from the same domain docs are served,
5+
so they can make use of features that require to have access to their cookies.
6+
"""
7+
8+
from django.conf.urls import include, url
9+
10+
from .views.proxied import ProxiedFooterHTML
11+
from readthedocs.search.proxied_api import ProxiedPageSearchAPIView
12+
13+
api_footer_urls = [
14+
url(r'footer_html/', ProxiedFooterHTML.as_view(), name='footer_html'),
15+
url(r'docsearch/$', ProxiedPageSearchAPIView.as_view(), name='doc_search'),
16+
]
17+
18+
urlpatterns = api_footer_urls

readthedocs/api/v2/views/proxied.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from readthedocs.core.utils.extend import SettingsOverrideObject
2+
3+
from .footer_views import BaseFooterHTML
4+
5+
6+
class BaseProxiedFooterHTML(BaseFooterHTML):
7+
8+
# DRF has BasicAuthentication and SessionAuthentication as default classes.
9+
# We don't support neither in the community site.
10+
authentication_classes = []
11+
12+
13+
class ProxiedFooterHTML(SettingsOverrideObject):
14+
15+
_default_class = BaseProxiedFooterHTML

readthedocs/builds/models.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import regex
1010
from django.conf import settings
11+
from django.core.files.storage import get_storage_class
1112
from django.db import models
1213
from django.db.models import F
1314
from django.urls import reverse
@@ -318,37 +319,33 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ
318319
"""Add permissions to the Version for all owners on save."""
319320
from readthedocs.projects import tasks
320321
obj = super().save(*args, **kwargs)
321-
if not self.project.has_feature(feature_id='skip_sync'):
322-
broadcast(
323-
type='app',
324-
task=tasks.symlink_project,
325-
args=[self.project.pk],
326-
)
322+
broadcast(
323+
type='app',
324+
task=tasks.symlink_project,
325+
args=[self.project.pk],
326+
)
327327
return obj
328328

329329
def delete(self, *args, **kwargs): # pylint: disable=arguments-differ
330330
from readthedocs.projects import tasks
331331
log.info('Removing files for version %s', self.slug)
332-
has_skip_sync = self.project.has_feature(feature_id='skip_sync')
333-
if not has_skip_sync:
334-
broadcast(
335-
type='app',
336-
task=tasks.remove_dirs,
337-
args=[self.get_artifact_paths()],
338-
)
332+
broadcast(
333+
type='app',
334+
task=tasks.remove_dirs,
335+
args=[self.get_artifact_paths()],
336+
)
339337

340338
# Remove resources if the version is not external
341339
if self.type != EXTERNAL:
342340
tasks.clean_project_resources(self.project, self)
343341

344342
project_pk = self.project.pk
345343
super().delete(*args, **kwargs)
346-
if not has_skip_sync:
347-
broadcast(
348-
type='app',
349-
task=tasks.symlink_project,
350-
args=[project_pk],
351-
)
344+
broadcast(
345+
type='app',
346+
task=tasks.symlink_project,
347+
args=[project_pk],
348+
)
352349

353350
@property
354351
def identifier_friendly(self):
@@ -451,6 +448,11 @@ def get_storage_paths(self):
451448

452449
return paths
453450

451+
def get_storage_environment_cache_path(self):
452+
"""Return the path of the cached environment tar file."""
453+
storage = get_storage_class(settings.RTD_BUILD_ENVIRONMENT_STORAGE)()
454+
return storage.join(self.project.slug, f'{self.slug}.tar')
455+
454456
def clean_build_path(self):
455457
"""
456458
Clean build path for project version.

readthedocs/builds/storage.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,16 @@ def listdir(self, path):
186186
if not self.exists(path):
187187
return [], []
188188
return super().listdir(path)
189+
190+
def url(self, name, *args, **kwargs): # noqa
191+
"""
192+
Override to accept extra arguments and ignore them all.
193+
194+
This method helps us to bring compatibility between Azure Blob Storage
195+
(which does not use the HTTP method) and Amazon S3 (who requires HTTP
196+
method to build the signed URL).
197+
198+
``FileSystemStorage`` does not support any other argument than ``name``.
199+
https://docs.djangoproject.com/en/2.2/ref/files/storage/#django.core.files.storage.Storage.url
200+
"""
201+
return super().url(name)

readthedocs/core/utils/general.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import os
44

5+
from django.conf import settings
6+
from django.core.files.storage import get_storage_class
57
from django.shortcuts import get_object_or_404
68

79
from readthedocs.core.utils import broadcast
@@ -24,3 +26,7 @@ def wipe_version_via_slugs(version_slug, project_slug):
2426
]
2527
for del_dir in del_dirs:
2628
broadcast(type='build', task=remove_dirs, args=[(del_dir,)])
29+
30+
# Delete the cache environment from storage
31+
storage = get_storage_class(settings.RTD_BUILD_ENVIRONMENT_STORAGE)()
32+
storage.delete(version.get_storage_environment_cache_path())

readthedocs/doc_builder/python_environments.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,15 @@ def _pip_cache_cmd_argument(self):
129129
130130
The decision is made considering if the directories are going to be
131131
cleaned after the build (``RTD_CLEAN_AFTER_BUILD=True`` or project has
132-
the ``CLEAN_AFTER_BUILD`` feature enabled). In this case, there is no
133-
need to cache anything.
132+
the ``CLEAN_AFTER_BUILD`` feature enabled) and project has not the
133+
feature ``CACHED_ENVIRONMENT``. In this case, there is no need to cache
134+
anything.
134135
"""
135136
if (
136-
settings.RTD_CLEAN_AFTER_BUILD or
137-
self.project.has_feature(Feature.CLEAN_AFTER_BUILD)
137+
(
138+
settings.RTD_CLEAN_AFTER_BUILD or
139+
self.project.has_feature(Feature.CLEAN_AFTER_BUILD)
140+
) and not self.project.has_feature(Feature.CACHED_ENVIRONMENT)
138141
):
139142
return [
140143
'--no-cache-dir',

readthedocs/projects/models.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,7 +1514,7 @@ def add_features(sender, **kwargs):
15141514
ALL_VERSIONS_IN_HTML_CONTEXT = 'all_versions_in_html_context'
15151515
SKIP_SYNC_TAGS = 'skip_sync_tags'
15161516
SKIP_SYNC_BRANCHES = 'skip_sync_branches'
1517-
SKIP_SYNC = 'skip_sync'
1517+
CACHED_ENVIRONMENT = 'cached_environment'
15181518

15191519
FEATURES = (
15201520
(USE_SPHINX_LATEST, _('Use latest version of Sphinx')),
@@ -1573,17 +1573,17 @@ def add_features(sender, **kwargs):
15731573
'when building with Sphinx'
15741574
),
15751575
),
1576-
(
1577-
SKIP_SYNC_TAGS,
1578-
_('Skip syncing tags'),
1579-
),
15801576
(
15811577
SKIP_SYNC_BRANCHES,
15821578
_('Skip syncing branches'),
15831579
),
15841580
(
1585-
SKIP_SYNC,
1586-
_('Skip symlinking and file syncing to webs'),
1581+
SKIP_SYNC_TAGS,
1582+
_('Skip syncing tags'),
1583+
),
1584+
(
1585+
CACHED_ENVIRONMENT,
1586+
_('Cache the environment (virtualenv, conda, pip cache, repository) in storage'),
15871587
),
15881588
)
15891589

0 commit comments

Comments
 (0)