Skip to content

Commit aa65396

Browse files
committed
Skip builds when project is not active
Allow to make the check extensible from output by defining an `is_active` method in the QuerySet. Also, this commit fixes different places where the `None` returned when `project.skip` was failing.
1 parent 507f1c4 commit aa65396

File tree

5 files changed

+55
-13
lines changed

5 files changed

+55
-13
lines changed

readthedocs/builds/views.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import logging
1313

1414
from builtins import object
15+
from django.contrib import messages
1516
from django.contrib.auth.decorators import login_required
1617
from django.urls import reverse
1718
from django.http import (
@@ -63,7 +64,18 @@ def post(self, request, project_slug):
6364
slug=version_slug,
6465
)
6566

66-
_, build = trigger_build(project=project, version=version)
67+
update_docs_task, build = trigger_build(project=project, version=version)
68+
if (update_docs_task, build) == (None, None):
69+
# Build was skipped
70+
messages.add_message(
71+
request,
72+
messages.WARNING,
73+
"This project is currently disabled and can't trigger new builds.",
74+
)
75+
return HttpResponseRedirect(
76+
reverse('builds_project_list', args=[project.slug]),
77+
)
78+
6779
return HttpResponseRedirect(
6880
reverse('builds_detail', args=[project.slug, build.pk]),
6981
)

readthedocs/core/utils/__init__.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from readthedocs.builds.constants import LATEST, BUILD_STATE_TRIGGERED
2121
from readthedocs.doc_builder.constants import DOCKER_LIMITS
2222

23+
2324
log = logging.getLogger(__name__)
2425

2526
SYNC_USER = getattr(settings, 'SYNC_USER', getpass.getuser())
@@ -87,18 +88,22 @@ def prepare_build(
8788
:param record: whether or not record the build in a new Build object
8889
:param force: build the HTML documentation even if the files haven't changed
8990
:param immutable: whether or not create an immutable Celery signature
90-
:returns: Celery signature of update_docs_task to be executed
91+
:returns: Celery signature of update_docs_task and Build instance
92+
:rtype: tuple
9193
"""
9294
# Avoid circular import
93-
from readthedocs.projects.tasks import update_docs_task
9495
from readthedocs.builds.models import Build
96+
from readthedocs.projects.models import Project
97+
from readthedocs.projects.tasks import update_docs_task
98+
99+
build = None
95100

96-
if project.skip:
97-
log.info(
98-
'Build not triggered because Project.skip=True: project=%s',
101+
if not Project.objects.is_active(project):
102+
log.warning(
103+
'Build not triggered because Project is not active: project=%s',
99104
project.slug,
100105
)
101-
return None
106+
return (None, None)
102107

103108
if not version:
104109
version = project.versions.get(slug=LATEST)
@@ -158,7 +163,8 @@ def trigger_build(project, version=None, record=True, force=False):
158163
:param version: version of the project to be built. Default: ``latest``
159164
:param record: whether or not record the build in a new Build object
160165
:param force: build the HTML documentation even if the files haven't changed
161-
:returns: A tuple (Celery AsyncResult promise, Task Signature from ``prepare_build``)
166+
:returns: Celery AsyncResult promise and Build instance
167+
:rtype: tuple
162168
"""
163169
update_docs_task, build = prepare_build(
164170
project,
@@ -168,9 +174,9 @@ def trigger_build(project, version=None, record=True, force=False):
168174
immutable=True,
169175
)
170176

171-
if update_docs_task is None:
172-
# Current project is skipped
173-
return None
177+
if (update_docs_task, build) == (None, None):
178+
# Build was skipped
179+
return (None, None)
174180

175181
return (update_docs_task.apply_async(), build)
176182

readthedocs/projects/querysets.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ def private(self, user=None):
5454
return self._add_user_repos(queryset, user)
5555
return queryset
5656

57+
def is_active(self, project):
58+
"""
59+
Check if the project is active.
60+
61+
The check consists on,
62+
* the Project shouldn't be marked as skipped.
63+
64+
:param project: project to be checked
65+
:type project: readthedocs.projects.models.Project
66+
67+
:returns: whether or not the project is active
68+
:rtype: bool
69+
"""
70+
if project.skip:
71+
return False
72+
73+
return True
74+
5775
# Aliases
5876

5977
def dashboard(self, user=None):

readthedocs/projects/views/private.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ def done(self, form_list, **kwargs):
287287
def trigger_initial_build(self, project):
288288
"""Trigger initial build."""
289289
update_docs, build = prepare_build(project)
290+
if (update_docs, build) == (None, None):
291+
return None
292+
290293
task_promise = chain(
291294
attach_webhook.si(project.pk, self.request.user.pk),
292295
update_docs,

readthedocs/restapi/views/integrations.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import six
1515
from django.shortcuts import get_object_or_404
16-
from rest_framework import permissions
16+
from rest_framework import permissions, status
1717
from rest_framework.exceptions import NotFound, ParseError
1818
from rest_framework.renderers import JSONRenderer
1919
from rest_framework.response import Response
@@ -55,11 +55,14 @@ def post(self, request, project_slug):
5555
"""Set up webhook post view with request and project objects."""
5656
self.request = request
5757
self.project = None
58+
self.data = self.get_data()
5859
try:
5960
self.project = self.get_project(slug=project_slug)
61+
if not Project.objects.is_active(self.project):
62+
resp = {'detail': 'This project is currently disabled'}
63+
return Response(resp, status=status.HTTP_406_NOT_ACCEPTABLE)
6064
except Project.DoesNotExist:
6165
raise NotFound('Project not found')
62-
self.data = self.get_data()
6366
resp = self.handle_webhook()
6467
if resp is None:
6568
log.info('Unhandled webhook event')

0 commit comments

Comments
 (0)