Skip to content

Commit 9ac1e53

Browse files
committed
Merge remote-tracking branch 'origin/master' into davidfischer/storage-epubs-pdfs-zips
2 parents 4fab169 + fab3be9 commit 9ac1e53

35 files changed

+458
-93
lines changed

CHANGELOG.rst

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
Version 3.2.3
2+
-------------
3+
4+
:Date: February 19, 2019
5+
6+
* `@ericholscher <http://github.com/ericholscher>`__: Add basic auth to the generic webhook API. (`#5311 <https://github.com/rtfd/readthedocs.org/pull/5311>`__)
7+
* `@ericholscher <http://github.com/ericholscher>`__: Fix an issue where we were not properly filtering projects (`#5309 <https://github.com/rtfd/readthedocs.org/pull/5309>`__)
8+
* `@stsewd <http://github.com/stsewd>`__: Rstrip repo url (`#5308 <https://github.com/rtfd/readthedocs.org/pull/5308>`__)
9+
* `@stsewd <http://github.com/stsewd>`__: Use autosectionlabel for docs in security (`#5307 <https://github.com/rtfd/readthedocs.org/pull/5307>`__)
10+
* `@rexzing <http://github.com/rexzing>`__: Incompatible dependency for prospector with pylint-django (`#5306 <https://github.com/rtfd/readthedocs.org/pull/5306>`__)
11+
* `@pyup-bot <http://github.com/pyup-bot>`__: pyup: Scheduled weekly dependency update for week 07 (`#5305 <https://github.com/rtfd/readthedocs.org/pull/5305>`__)
12+
* `@davidfischer <http://github.com/davidfischer>`__: Allow extensions to control URL structure (`#5296 <https://github.com/rtfd/readthedocs.org/pull/5296>`__)
13+
* `@stsewd <http://github.com/stsewd>`__: Downgrade pytest-django (`#5294 <https://github.com/rtfd/readthedocs.org/pull/5294>`__)
14+
* `@rexzing <http://github.com/rexzing>`__: Docs reformatting with :guilabel: (`#5161 <https://github.com/rtfd/readthedocs.org/pull/5161>`__)
15+
116
Version 3.2.2
217
-------------
318

@@ -986,14 +1001,12 @@ Version 2.3.1
9861001
* `@humitos <http://github.com/humitos>`__: Upgrade all packages using `pur` tool (`#2916 <https://github.com/rtfd/readthedocs.org/pull/2916>`__)
9871002
* `@rixx <http://github.com/rixx>`__: Fix page redirect preview (`#2711 <https://github.com/rtfd/readthedocs.org/pull/2711>`__)
9881003

989-
.. _version-2.3.0:
990-
9911004
Version 2.3.0
9921005
-------------
9931006

9941007
.. warning::
9951008
Version 2.3.0 includes a security fix for project translations. See
996-
:ref:`security-2.3.0` for more information
1009+
:ref:`security:Release 2.3.0` for more information
9971010

9981011
* `@stsewd <http://github.com/stsewd>`__: Fix tox env for coverage (`#3772 <https://github.com/rtfd/readthedocs.org/pull/3772>`__)
9991012
* `@humitos <http://github.com/humitos>`__: Try to fix end of file (`#3761 <https://github.com/rtfd/readthedocs.org/pull/3761>`__)

common

docs/features/sitemaps.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Sitemaps
2+
========
3+
4+
Sitemaps_ allows us to inform search engines about URLs that are available for crawling
5+
and communicate them additional information about each URL of the project:
6+
7+
* when it was last updated,
8+
* how often it changes,
9+
* how important it is in relation to other URLs in the site, and
10+
* what translations are available for a page.
11+
12+
Read the Docs automatically generates a sitemap for each project that hosts
13+
to improve results when performing a search on these search engines.
14+
This allow us to prioritize results based on the version number, for example
15+
to show ``latest`` as the top result followed by ``stable`` and then all the project's
16+
versions sorted following semver_.
17+
18+
.. _semver: https://semver.org/
19+
.. _Sitemaps: https://www.sitemaps.org/

docs/security.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Security issue archive
8787
Version 3.2.0
8888
~~~~~~~~~~~~~
8989

90-
:ref:`version-3.2.0` resolved an issue where a specially crafted request
90+
:ref:`changelog:Version 3.2.0` resolved an issue where a specially crafted request
9191
could result in a DNS query to an arbitrary domain.
9292

9393
This issue was found by `Cyber Smart Defence <https://www.cybersmartdefence.com/>`_
@@ -98,7 +98,7 @@ of Read the Docs.
9898
Release 2.3.0
9999
~~~~~~~~~~~~~
100100

101-
:ref:`version-2.3.0` resolves a security issue with translations on our community
101+
:ref:`changelog:Version 2.3.0` resolves a security issue with translations on our community
102102
hosting site that allowed users to modify the hosted path of a target project by
103103
adding it as a translation project of their own project. A check was added to
104104
ensure project ownership before adding the project as a translation.

docs/settings.rst

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,6 @@ Default: :djangosetting:`DOCUMENT_PYQUERY_PATH`
6767
The Pyquery path to an HTML element that is the root of your document.
6868
This is used for making sure we are only searching the main content of a document.
6969

70-
USE_PIP_INSTALL
71-
---------------
72-
73-
Default: :djangosetting:`USE_PIP_INSTALL`
74-
75-
Whether to use `pip install .` or `python setup.py install` when installing packages into the Virtualenv. Default is to use `python setup.py install`.
76-
77-
7870
PUBLIC_DOMAIN
7971
-------------
8072

@@ -177,4 +169,4 @@ This setting is used for automatically indexing objects to elasticsearch.
177169
project and build documentations without having elasticsearch.
178170

179171

180-
.. _elasticsearch-dsl-py.connections.configure: https://elasticsearch-dsl.readthedocs.io/en/stable/configuration.html#multiple-clusters
172+
.. _elasticsearch-dsl-py.connections.configure: https://elasticsearch-dsl.readthedocs.io/en/stable/configuration.html#multiple-clusters

docs/tests.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,19 @@ argument::
2222

2323
tox "'--including-search'"
2424

25+
.. warning::
26+
27+
Running tests for search needs an Elasticsearch :ref:`instance running locally <development/search:Installing and running Elasticsearch>`.
28+
2529
To target a specific environment::
2630

27-
tox -e py27
31+
tox -e py36
2832

2933
The ``tox`` configuration has the following environments configured. You can
3034
target a single environment to limit the test suite::
3135

32-
py27
33-
Run our test suite using Python 2.7
36+
py36
37+
Run our test suite using Python 3.6
3438

3539
lint
3640
Run code linting using `Prospector`_. This currently runs `pylint`_,

media/css/core.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,14 @@ p.build-missing { font-size: .8em; color: #9d9a55; margin: 0 0 3px; }
702702
#footer label { color: #BCC1C3; font-weight: normal; }
703703
#footer input[type="text"], #footer input[type="email"] { padding: 4px; font-size: 12px; line-height: 16px; margin-bottom: 5px }
704704

705+
/* Warning Icon for Build List triggered */
706+
.module-item.col-span a span.icon-warning:before {
707+
font-family: FontAwesome;
708+
font-size: .9em;
709+
padding-right: .3em;
710+
font-weight: normal;
711+
content: "\f071";
712+
}
705713

706714
/* utils */
707715

readthedocs/builds/models.py

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

33
"""Models for the builds app."""
44

5+
import datetime
56
import logging
67
import os.path
78
import re
@@ -31,6 +32,7 @@
3132
BRANCH,
3233
BUILD_STATE,
3334
BUILD_STATE_FINISHED,
35+
BUILD_STATE_TRIGGERED,
3436
BUILD_TYPES,
3537
LATEST,
3638
NON_REPOSITORY_VERSIONS,
@@ -629,6 +631,12 @@ def finished(self):
629631
"""Return if build has a finished state."""
630632
return self.state == BUILD_STATE_FINISHED
631633

634+
@property
635+
def is_stale(self):
636+
"""Return if build state is triggered & date more than 5m ago."""
637+
mins_ago = timezone.now() - datetime.timedelta(minutes=5)
638+
return self.state == BUILD_STATE_TRIGGERED and self.date < mins_ago
639+
632640

633641
class BuildCommandResultMixin:
634642

readthedocs/builds/version_slug.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
from django.db import models
2828
from django.utils.encoding import force_text
29+
from slugify import slugify as unicode_slugify
2930

3031

3132
def get_fields_with_model(cls):
@@ -53,13 +54,15 @@ def get_fields_with_model(cls):
5354

5455
class VersionSlugField(models.CharField):
5556

56-
"""Inspired by ``django_extensions.db.fields.AutoSlugField``."""
57+
"""
58+
Inspired by ``django_extensions.db.fields.AutoSlugField``.
5759
58-
invalid_chars_re = re.compile('[^-._a-z0-9]')
59-
leading_punctuation_re = re.compile('^[-._]+')
60-
placeholder = '-'
61-
fallback_slug = 'unknown'
60+
Uses ``unicode-slugify`` to generate the slug.
61+
"""
62+
63+
ok_chars = '-._' # dash, dot, underscore
6264
test_pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX))
65+
fallback_slug = 'unknown'
6366

6467
def __init__(self, *args, **kwargs):
6568
kwargs.setdefault('db_index', True)
@@ -78,13 +81,42 @@ def get_queryset(self, model_cls, slug_field):
7881
return model._default_manager.all()
7982
return model_cls._default_manager.all()
8083

84+
def _normalize(self, content):
85+
"""
86+
Normalize some invalid characters (/, %, !, ?) to become a dash (``-``).
87+
88+
.. note::
89+
90+
We replace these characters to a dash to keep compatibility with the
91+
old behavior and also because it makes this more readable.
92+
93+
For example, ``release/1.0`` will become ``release-1.0``.
94+
"""
95+
return re.sub('[/%!?]', '-', content)
96+
8197
def slugify(self, content):
98+
"""
99+
Make ``content`` a valid slug.
100+
101+
It uses ``unicode-slugify`` behind the scenes which works properly with
102+
Unicode characters.
103+
"""
82104
if not content:
83105
return ''
84106

85-
slugified = content.lower()
86-
slugified = self.invalid_chars_re.sub(self.placeholder, slugified)
87-
slugified = self.leading_punctuation_re.sub('', slugified)
107+
normalized = self._normalize(content)
108+
slugified = unicode_slugify(
109+
normalized,
110+
only_ascii=True,
111+
spaces=False,
112+
lower=True,
113+
ok=self.ok_chars,
114+
space_replacement='-',
115+
)
116+
117+
# Remove first character wile it's an invalid character for the
118+
# beginning of the slug
119+
slugified = slugified.lstrip(self.ok_chars)
88120

89121
if not slugified:
90122
return self.fallback_slug

readthedocs/config/config.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -489,15 +489,14 @@ def validate_conda(self):
489489
raw_conda = self.raw_config['conda']
490490
with self.catch_validation_error('conda'):
491491
validate_dict(raw_conda)
492-
conda_environment = None
493-
if 'file' in raw_conda:
494-
with self.catch_validation_error('conda.file'):
495-
conda_environment = validate_file(
496-
raw_conda['file'],
497-
self.base_path,
498-
)
499-
conda['environment'] = conda_environment
500-
492+
with self.catch_validation_error('conda.file'):
493+
if 'file' not in raw_conda:
494+
raise ValidationError('file', VALUE_NOT_FOUND)
495+
conda_environment = validate_file(
496+
raw_conda['file'],
497+
self.base_path,
498+
)
499+
conda['environment'] = conda_environment
501500
return conda
502501
return None
503502

readthedocs/config/tests/test_config.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -557,29 +557,33 @@ def test_it_priorities_image_from_env_config(self, tmpdir, image):
557557
assert build.build.image == image
558558

559559

560-
def test_use_conda_default_false():
560+
def test_use_conda_default_none():
561561
build = get_build_config({})
562562
build.validate()
563563
assert build.conda is None
564564

565565

566-
def test_use_conda_respects_config():
566+
def test_validates_conda_file(tmpdir):
567+
apply_fs(tmpdir, {'environment.yml': ''})
567568
build = get_build_config(
568-
{'conda': {}},
569+
{'conda': {'file': 'environment.yml'}},
570+
source_file=str(tmpdir.join('readthedocs.yml')),
569571
)
570572
build.validate()
571573
assert isinstance(build.conda, Conda)
574+
assert build.conda.environment == str(tmpdir.join('environment.yml'))
572575

573576

574-
def test_validates_conda_file(tmpdir):
577+
def test_file_is_required_when_using_conda(tmpdir):
575578
apply_fs(tmpdir, {'environment.yml': ''})
576579
build = get_build_config(
577-
{'conda': {'file': 'environment.yml'}},
580+
{'conda': {'foo': 'environment.yml'}},
578581
source_file=str(tmpdir.join('readthedocs.yml')),
579582
)
580-
build.validate()
581-
assert isinstance(build.conda, Conda)
582-
assert build.conda.environment == str(tmpdir.join('environment.yml'))
583+
with raises(InvalidConfig) as excinfo:
584+
build.validate()
585+
assert excinfo.value.key == 'conda.file'
586+
assert excinfo.value.code == VALUE_NOT_FOUND
583587

584588

585589
def test_requirements_file_empty():

readthedocs/core/management/commands/clean_builds.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def add_arguments(self, parser):
2323
parser.add_argument(
2424
'--days',
2525
dest='days',
26-
type='int',
26+
type=int,
2727
default=365,
2828
help='Find builds older than DAYS days, default: 365',
2929
)

readthedocs/core/urls/subdomain.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# -*- coding: utf-8 -*-
2-
31
"""URL configurations for subdomains."""
42
from functools import reduce
53
from operator import add
@@ -15,14 +13,16 @@
1513
redirect_project_slug,
1614
robots_txt,
1715
serve_docs,
16+
sitemap_xml,
1817
)
1918

2019

2120
handler500 = server_error_500
2221
handler404 = server_error_404
2322

2423
subdomain_urls = [
25-
url(r'robots.txt$', robots_txt, name='robots_txt'),
24+
url(r'robots\.txt$', robots_txt, name='robots_txt'),
25+
url(r'sitemap\.xml$', sitemap_xml, name='sitemap_xml'),
2626
url(
2727
r'^(?:|projects/(?P<subproject_slug>{project_slug})/)'
2828
r'page/(?P<filename>.*)$'.format(**pattern_opts),

0 commit comments

Comments
 (0)