Skip to content

Commit 224e5b0

Browse files
committed
Merge remote-tracking branch 'origin/master' into humitos/new-docker-release
2 parents 2f022dd + 9676bdd commit 224e5b0

File tree

17 files changed

+190
-45
lines changed

17 files changed

+190
-45
lines changed

.travis.yml

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ jobs:
99
env: TOXENV=py36,codecov ES_VERSION=6.2.4 ES_DOWNLOAD_URL=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz
1010
- python: 3.6
1111
env: TOXENV=docs
12+
- python: 3.6
13+
env: TOXENV=docs-linkcheck
1214
- python: 3.6
1315
env: TOXENV=docs-lint
1416
- python: 3.6
@@ -18,6 +20,9 @@ jobs:
1820
env: TOXENV=eslint NODE_VERSION=10.17.0
1921
- python: 3.6
2022
env: TOXENV=migrations
23+
24+
allow_failures:
25+
- env: TOXENV=docs-linkcheck
2126
cache:
2227
directories:
2328
- ~/.cache/pip

CHANGELOG.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ Version 3.2.2
918918
* `@pyup-bot <https://github.com/pyup-bot>`__: Pin pytest-cov to latest version 2.6.1 (`#5276 <https://github.com/readthedocs/readthedocs.org/pull/5276>`__)
919919
* `@pyup-bot <https://github.com/pyup-bot>`__: Pin pillow to latest version 5.4.1 (`#5275 <https://github.com/readthedocs/readthedocs.org/pull/5275>`__)
920920
* `@pyup-bot <https://github.com/pyup-bot>`__: Update elasticsearch to 6.3.1 (`#5274 <https://github.com/readthedocs/readthedocs.org/pull/5274>`__)
921-
* `@discdiver <https://github.com/discdiver>`__: clarify github integration needs https:// prepended (`#5273 <https://github.com/readthedocs/readthedocs.org/pull/5273>`__)
921+
* `@discdiver <https://github.com/discdiver>`__: clarify github integration needs ``https://`` prepended (`#5273 <https://github.com/readthedocs/readthedocs.org/pull/5273>`__)
922922
* `@humitos <https://github.com/humitos>`__: Setup and configure pyup.io (`#5272 <https://github.com/readthedocs/readthedocs.org/pull/5272>`__)
923923
* `@humitos <https://github.com/humitos>`__: Update all Python dependencies (`#5269 <https://github.com/readthedocs/readthedocs.org/pull/5269>`__)
924924
* `@davidfischer <https://github.com/davidfischer>`__: Add note about security issue (`#5263 <https://github.com/readthedocs/readthedocs.org/pull/5263>`__)

dockerfiles/Dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
3434

3535
RUN pip3 install --no-cache-dir --upgrade pip
3636

37+
# Ensure that ``python`` is in the PATH so that ``./manage.py`` works
38+
RUN ln -s /usr/bin/python3 /usr/bin/python
39+
3740
WORKDIR /tmp
3841

3942
RUN curl -O https://raw.githubusercontent.com/readthedocs/readthedocs.org/master/requirements/pip.txt

docs/conf.py

+12
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ def get_version():
103103
<p>Try using the search box or go to the homepage.</p>
104104
''',
105105
}
106+
linkcheck_ignore = [
107+
r'http://127\.0\.0\.1',
108+
r'http://localhost',
109+
r'https://yourproject\.readthedocs\.io',
110+
r'https?://docs\.example\.com',
111+
r'https://foo\.readthedocs\.io/projects',
112+
r'https://github\.com.+?#L\d+',
113+
r'https://github\.com/readthedocs/readthedocs\.org/issues',
114+
r'https://github\.com/readthedocs/readthedocs\.org/pull',
115+
r'https://docs\.readthedocs\.io/\?rtd_search',
116+
r'https://readthedocs\.org/search',
117+
]
106118

107119

108120
def setup(app):

readthedocs/api/v2/templates/restapi/footer.html

+6-4
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,17 @@
6161

6262
{% block readthedocs %}
6363
<dl>
64+
{# We hardcode the URLS because we don't have access to the URLCONF of the main app from proxito #}
6465
<!-- These are kept as relative links for internal installs that are http -->
6566
<dt>{% trans "On Read the Docs" %}</dt>
6667
<dd>
67-
<a href="//{{ settings.PRODUCTION_DOMAIN }}{% url 'projects_detail' project.slug %}">{% trans "Project Home" %}</a>
68+
<a href="//{{ settings.PRODUCTION_DOMAIN }}/projects/{{ project.slug }}/">{% trans "Project Home" %}</a>
6869
</dd>
6970
<dd>
70-
<a href="//{{ settings.PRODUCTION_DOMAIN }}{% url 'builds_project_list' project.slug %}">{% trans "Builds" %}</a>
71+
<a href="//{{ settings.PRODUCTION_DOMAIN }}/projects/{{ project.slug }}/builds/">{% trans "Builds" %}</a>
7172
</dd>
7273
<dd>
73-
<a href="//{{ settings.PRODUCTION_DOMAIN }}{% url 'project_downloads' project.slug %}">{% trans "Downloads" %}</a>
74+
<a href="//{{ settings.PRODUCTION_DOMAIN }}/projects/{{ project.slug }}/downloads/">{% trans "Downloads" %}</a>
7475
</dd>
7576
</dl>
7677
{% endblock %}
@@ -116,7 +117,8 @@
116117
<dt>{% trans "Search" %}</dt>
117118
<dd>
118119
<div style="padding: 6px;">
119-
<form id="flyout-search-form" class="wy-form" target="_blank" action="//{{ settings.PRODUCTION_DOMAIN }}{% url 'elastic_project_search' project.slug %}" method="get">
120+
{# We hardcode the URLS because we don't have access to the URLCONF of the main app from proxito #}
121+
<form id="flyout-search-form" class="wy-form" target="_blank" action="//{{ settings.PRODUCTION_DOMAIN }}/projects/{{ project.slug }}/search/" method="get">
120122
<input type="text" name="q" placeholder="{% trans "Search docs" %}">
121123
</form>
122124
</div>

readthedocs/api/v2/views/integrations.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def sync_versions(self, project):
202202
'build_triggered': False,
203203
'project': project.slug,
204204
'versions': [version],
205-
'versions_synced': True,
205+
'versions_synced': version is not None,
206206
}
207207

208208
def get_external_version_response(self, project):

readthedocs/core/views/hooks.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from readthedocs.builds.constants import EXTERNAL
66
from readthedocs.core.utils import trigger_build
7+
from readthedocs.projects.models import Project
78
from readthedocs.projects.tasks import sync_repository_task
89

910

@@ -73,8 +74,16 @@ def sync_versions(project):
7374
we always pass the default version.
7475
7576
:returns: The version slug that was used to trigger the clone.
76-
:rtype: str
77+
:rtype: str or ``None`` if failed
7778
"""
79+
80+
if not Project.objects.is_active(project):
81+
log.warning(
82+
'Sync not triggered because Project is not active: project=%s',
83+
project.slug,
84+
)
85+
return None
86+
7887
try:
7988
version_identifier = project.get_default_branch()
8089
version = (
@@ -85,7 +94,16 @@ def sync_versions(project):
8594
if not version:
8695
log.info('Unable to sync from %s version', version_identifier)
8796
return None
88-
sync_repository_task.delay(version.pk)
97+
98+
options = {}
99+
if project.build_queue:
100+
# respect the queue for this project
101+
options['queue'] = project.build_queue
102+
103+
sync_repository_task.apply_async(
104+
(version.pk,),
105+
**options,
106+
)
89107
return version.slug
90108
except Exception:
91109
log.exception('Unknown sync versions exception')

readthedocs/projects/admin.py

+16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from django.contrib import admin, messages
44
from django.contrib.admin.actions import delete_selected
5+
from django.forms import BaseInlineFormSet
56
from django.utils.translation import ugettext_lazy as _
67

78
from readthedocs.builds.models import Version
@@ -53,12 +54,27 @@ class ProjectRelationshipInline(admin.TabularInline):
5354
raw_id_fields = ('child',)
5455

5556

57+
class VersionInlineFormSet(BaseInlineFormSet):
58+
59+
"""Limit the number of versions displayed in the inline."""
60+
61+
LIMIT = 200
62+
63+
def __init__(self, *args, **kwargs):
64+
super().__init__(*args, **kwargs)
65+
self.queryset = self.queryset[:self.LIMIT]
66+
67+
5668
class VersionInline(admin.TabularInline):
5769

5870
"""Version inline relationship view for :py:class:`ProjectAdmin`."""
5971

72+
formset = VersionInlineFormSet
6073
model = Version
6174

75+
def get_queryset(self, request):
76+
return super().get_queryset(request).select_related("project")
77+
6278

6379
class RedirectInline(admin.TabularInline):
6480

readthedocs/projects/tasks.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def sync_versions(self, version_repo):
154154
"""
155155
Update tags/branches hitting the API.
156156
157-
It may trigger a new build to the stable version when hittig the
157+
It may trigger a new build to the stable version when hitting the
158158
``sync_versions`` endpoint.
159159
"""
160160
version_post_data = {'repo': version_repo.repo_url}
@@ -1798,7 +1798,11 @@ def webhook_notification(version, build, hook_url):
17981798
}
17991799
)
18001800
try:
1801-
requests.post(hook_url, data=data)
1801+
requests.post(
1802+
hook_url,
1803+
data=data,
1804+
headers={'content-type': 'application/json'}
1805+
)
18021806
except Exception:
18031807
log.exception('Failed to POST on webhook url: url=%s', hook_url)
18041808

readthedocs/rtd_tests/tests/test_api.py

+38-10
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,10 @@ class IntegrationsTests(TestCase):
773773
fixtures = ['eric.json', 'test_data.json']
774774

775775
def setUp(self):
776-
self.project = get(Project)
776+
self.project = get(
777+
Project,
778+
build_queue=None,
779+
)
777780
self.feature_flag = get(
778781
Feature,
779782
projects=[self.project],
@@ -847,6 +850,31 @@ def test_webhook_skipped_project(self, trigger_build):
847850
self.assertEqual(response.status_code, status.HTTP_406_NOT_ACCEPTABLE)
848851
self.assertFalse(trigger_build.called)
849852

853+
@mock.patch('readthedocs.core.views.hooks.sync_repository_task')
854+
def test_sync_repository_custom_project_queue(self, sync_repository_task, trigger_build):
855+
client = APIClient()
856+
self.project.build_queue = 'specific-build-queue'
857+
self.project.save()
858+
859+
headers = {GITHUB_EVENT_HEADER: GITHUB_CREATE}
860+
resp = client.post(
861+
'/api/v2/webhook/github/{}/'.format(self.project.slug),
862+
self.github_payload,
863+
format='json',
864+
**headers,
865+
)
866+
self.assertEqual(resp.status_code, status.HTTP_200_OK)
867+
self.assertFalse(resp.data['build_triggered'])
868+
self.assertEqual(resp.data['project'], self.project.slug)
869+
self.assertEqual(resp.data['versions'], [LATEST])
870+
self.assertTrue(resp.data['versions_synced'])
871+
trigger_build.assert_not_called()
872+
latest_version = self.project.versions.get(slug=LATEST)
873+
sync_repository_task.apply_async.assert_called_with(
874+
(latest_version.pk,),
875+
queue='specific-build-queue',
876+
)
877+
850878
def test_github_webhook_for_branches(self, trigger_build):
851879
"""GitHub webhook API."""
852880
client = APIClient()
@@ -927,7 +955,7 @@ def test_github_webhook_no_build_on_delete(self, sync_repository_task, trigger_b
927955
self.assertEqual(resp.data['versions'], [LATEST])
928956
trigger_build.assert_not_called()
929957
latest_version = self.project.versions.get(slug=LATEST)
930-
sync_repository_task.delay.assert_called_with(latest_version.pk)
958+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
931959

932960
@mock.patch('readthedocs.core.views.hooks.sync_repository_task')
933961
def test_github_create_event(self, sync_repository_task, trigger_build):
@@ -946,7 +974,7 @@ def test_github_create_event(self, sync_repository_task, trigger_build):
946974
self.assertEqual(resp.data['versions'], [LATEST])
947975
trigger_build.assert_not_called()
948976
latest_version = self.project.versions.get(slug=LATEST)
949-
sync_repository_task.delay.assert_called_with(latest_version.pk)
977+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
950978

951979
@mock.patch('readthedocs.core.utils.trigger_build')
952980
def test_github_pull_request_opened_event(self, trigger_build, core_trigger_build):
@@ -1191,7 +1219,7 @@ def test_github_delete_event(self, sync_repository_task, trigger_build):
11911219
self.assertEqual(resp.data['versions'], [LATEST])
11921220
trigger_build.assert_not_called()
11931221
latest_version = self.project.versions.get(slug=LATEST)
1194-
sync_repository_task.delay.assert_called_with(latest_version.pk)
1222+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
11951223

11961224
def test_github_parse_ref(self, trigger_build):
11971225
wh = GitHubWebhookView()
@@ -1399,7 +1427,7 @@ def test_gitlab_push_hook_creation(
13991427
self.assertEqual(resp.data['versions'], [LATEST])
14001428
trigger_build.assert_not_called()
14011429
latest_version = self.project.versions.get(slug=LATEST)
1402-
sync_repository_task.delay.assert_called_with(latest_version.pk)
1430+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
14031431

14041432
@mock.patch('readthedocs.core.views.hooks.sync_repository_task')
14051433
def test_gitlab_push_hook_deletion(
@@ -1421,7 +1449,7 @@ def test_gitlab_push_hook_deletion(
14211449
self.assertEqual(resp.data['versions'], [LATEST])
14221450
trigger_build.assert_not_called()
14231451
latest_version = self.project.versions.get(slug=LATEST)
1424-
sync_repository_task.delay.assert_called_with(latest_version.pk)
1452+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
14251453

14261454
@mock.patch('readthedocs.core.views.hooks.sync_repository_task')
14271455
def test_gitlab_tag_push_hook_creation(
@@ -1444,7 +1472,7 @@ def test_gitlab_tag_push_hook_creation(
14441472
self.assertEqual(resp.data['versions'], [LATEST])
14451473
trigger_build.assert_not_called()
14461474
latest_version = self.project.versions.get(slug=LATEST)
1447-
sync_repository_task.delay.assert_called_with(latest_version.pk)
1475+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
14481476

14491477
@mock.patch('readthedocs.core.views.hooks.sync_repository_task')
14501478
def test_gitlab_tag_push_hook_deletion(
@@ -1467,7 +1495,7 @@ def test_gitlab_tag_push_hook_deletion(
14671495
self.assertEqual(resp.data['versions'], [LATEST])
14681496
trigger_build.assert_not_called()
14691497
latest_version = self.project.versions.get(slug=LATEST)
1470-
sync_repository_task.delay.assert_called_with(latest_version.pk)
1498+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
14711499

14721500
def test_gitlab_invalid_webhook(self, trigger_build):
14731501
"""GitLab webhook unhandled event."""
@@ -1919,7 +1947,7 @@ def test_bitbucket_push_hook_creation(
19191947
self.assertEqual(resp.data['versions'], [LATEST])
19201948
trigger_build.assert_not_called()
19211949
latest_version = self.project.versions.get(slug=LATEST)
1922-
sync_repository_task.delay.assert_called_with(latest_version.pk)
1950+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
19231951

19241952
@mock.patch('readthedocs.core.views.hooks.sync_repository_task')
19251953
def test_bitbucket_push_hook_deletion(
@@ -1938,7 +1966,7 @@ def test_bitbucket_push_hook_deletion(
19381966
self.assertEqual(resp.data['versions'], [LATEST])
19391967
trigger_build.assert_not_called()
19401968
latest_version = self.project.versions.get(slug=LATEST)
1941-
sync_repository_task.delay.assert_called_with(latest_version.pk)
1969+
sync_repository_task.apply_async.assert_called_with((latest_version.pk,))
19421970

19431971
def test_bitbucket_invalid_webhook(self, trigger_build):
19441972
"""Bitbucket webhook unhandled event."""

readthedocs/rtd_tests/tests/test_build_notifications.py

+24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# -*- coding: utf-8 -*-
22
"""Notifications sent after build is completed."""
33

4+
import json
5+
46
import django_dynamic_fixture as fixture
57
from django.core import mail
68
from django.test import TestCase
@@ -38,6 +40,28 @@ def test_send_webhook_notification(self):
3840

3941
self.assertEqual(len(mail.outbox), 0)
4042

43+
def test_send_webhook_notification_has_content_type_header(self):
44+
hook = fixture.get(WebHook, project=self.project)
45+
data = json.dumps({
46+
'name': self.project.name,
47+
'slug': self.project.slug,
48+
'build': {
49+
'id': self.build.id,
50+
'commit': self.build.commit,
51+
'state': self.build.state,
52+
'success': self.build.success,
53+
'date': self.build.date.strftime('%Y-%m-%d %H:%M:%S'),
54+
},
55+
})
56+
with patch('readthedocs.projects.tasks.requests.post') as mock:
57+
mock.return_value = None
58+
send_notifications(self.version.pk, self.build.pk)
59+
mock.assert_called_once_with(
60+
hook.url,
61+
data=data,
62+
headers={'content-type': 'application/json'}
63+
)
64+
4165
def test_send_email_notification(self):
4266
fixture.get(EmailHook, project=self.project)
4367
send_notifications(self.version.pk, self.build.pk, email=True)

0 commit comments

Comments
 (0)