Skip to content
2 changes: 2 additions & 0 deletions readthedocs/builds/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@

BUILD_STATUS_NORMAL = 'normal'
BUILD_STATUS_DUPLICATED = 'duplicated'
BUILD_STATUS_RETRIGGERED = 'retriggered'
BUILD_STATUS_CHOICES = (
(BUILD_STATUS_NORMAL, 'Normal'),
(BUILD_STATUS_DUPLICATED, 'Duplicated'),
(BUILD_STATUS_RETRIGGERED, 'Retriggered'),
)
21 changes: 20 additions & 1 deletion readthedocs/builds/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,33 @@ def post(self, request, project_slug):
return HttpResponseForbidden()

version_slug = request.POST.get('version_slug')
commit = request.POST.get('commit')
build_pk = request.POST.get('build_pk')

version = get_object_or_404(
self._get_versions(project),
# Don't filter by internal/external here so we can build all versions
Version.objects.public(self.request.user),
slug=version_slug,
)

# Set either the build or None
build = version.builds.filter(pk=build_pk).first()

if build:

log.info(
'Rebuilding build. project=%s version=%s commit=%s build=%s',
project.slug,
version.slug,
commit,
build.pk
)

update_docs_task, build = trigger_build(
project=project,
version=version,
build=build,
commit=commit,
)
if (update_docs_task, build) == (None, None):
# Build was skipped
Expand Down
33 changes: 22 additions & 11 deletions readthedocs/core/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Common utilty functions."""

from datetime import datetime
import errno
import logging
import os
Expand All @@ -16,8 +17,8 @@
BUILD_STATE_FINISHED,
BUILD_STATUS_PENDING,
EXTERNAL,
BUILD_STATUS_RETRIGGERED
)
from readthedocs.doc_builder.constants import DOCKER_LIMITS
from readthedocs.projects.constants import CELERY_LOW, CELERY_MEDIUM, CELERY_HIGH
from readthedocs.doc_builder.exceptions import BuildMaxConcurrencyError, DuplicatedBuildError

Expand Down Expand Up @@ -64,6 +65,7 @@ def broadcast(type, task, args, kwargs=None, callback=None): # pylint: disable=
def prepare_build(
project,
version=None,
build=None,
commit=None,
record=True,
force=False,
Expand Down Expand Up @@ -93,8 +95,6 @@ def prepare_build(
send_notifications,
)

build = None

if not Project.objects.is_active(project):
log.warning(
'Build not triggered because Project is not active: project=%s',
Expand All @@ -112,7 +112,16 @@ def prepare_build(
'commit': commit,
}

if record:
if build:
build.state = BUILD_STATE_TRIGGERED
build.status = BUILD_STATUS_RETRIGGERED
build.success = True
build.commit = commit
build.commands.all().delete()
build.save()
kwargs['build_pk'] = build.pk

if record and not build:
build = Build.objects.create(
project=project,
version=version,
Expand Down Expand Up @@ -234,7 +243,7 @@ def prepare_build(
)


def trigger_build(project, version=None, commit=None, record=True, force=False):
def trigger_build(project, version=None, build=None, commit=None, record=True, force=False):
"""
Trigger a Build.

Expand All @@ -250,17 +259,19 @@ def trigger_build(project, version=None, commit=None, record=True, force=False):
:rtype: tuple
"""
log.info(
'Triggering build. project=%s version=%s commit=%s',
'Triggering build. project=%s version=%s commit=%s build=%s',
project.slug,
version.slug if version else None,
commit,
build.pk if build else None
)
update_docs_task, build = prepare_build(
project,
version,
commit,
record,
force,
project=project,
version=version,
build=build,
commit=commit,
record=record,
force=force,
immutable=True,
)

Expand Down
16 changes: 16 additions & 0 deletions readthedocs/rtd_tests/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,22 @@ def test_build_list_includes_external_versions(self):
self.assertEqual(response.status_code, 200)
self.assertIn(external_version_build, response.context['build_qs'])

@mock.patch('readthedocs.projects.tasks.update_docs_task')
def test_build_commit_external_version(self, mock):
ver = self.pip.versions.first()
ver.commit = 'asd324653546'
ver.type = 'external'
ver.save()
build = get(Build, version=ver, project=self.pip)
build.save()
r = self.client.post('/projects/pip/builds/',
{'version_slug': ver.slug, 'commit': ver.commit, 'build_pk': build.pk}
)
self.assertEqual(r.status_code, 302)
self.assertEqual(
r._headers['location'][1],
'/projects/pip/builds/%s/' % build.pk,
)

class TestSearchAnalyticsView(TestCase):

Expand Down
16 changes: 16 additions & 0 deletions readthedocs/templates/builds/build_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@
</a>
</li>
</div>


{% if request.user|is_admin:project %}
<div data-bind="visible: finished()">
<form method="post" name="rebuild_commit" action="{% url "builds_project_list" project.slug %}">
{% csrf_token %}
<a href="#" onclick="document.forms['rebuild_commit'].submit();">
Rebuild this build
</a>
<input type="hidden" name="version_slug" value="{{ build.version.slug }}">
<input type="hidden" name="commit" value="{{ build.commit }}">
<input type="hidden" name="build_pk" value="{{ build.pk }}">
</form>
</div>
{% endif %}

</ul>

<div class="build-id">
Expand Down