Skip to content

Add ability to rebuild a specific build #6995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 9, 2021
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
39 changes: 27 additions & 12 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 @@ -17,7 +18,6 @@
BUILD_STATUS_PENDING,
EXTERNAL,
)
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

Expand Down Expand Up @@ -64,6 +64,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 All @@ -85,16 +86,14 @@ def prepare_build(
:rtype: tuple
"""
# Avoid circular import
from readthedocs.builds.models import Build
from readthedocs.builds.models import Build, BuildCommandResult
from readthedocs.projects.models import Project, Feature
from readthedocs.projects.tasks import (
update_docs_task,
send_external_build_status,
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 +111,21 @@ def prepare_build(
'commit': commit,
}

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

# Show a step in the UI that the build is being rebuilt
BuildCommandResult.objects.create(
build=build, command='Rebuilding build', exit_code=0,
start_time=datetime.utcnow(), end_time=datetime.utcnow(),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably remove this entirely, I don't think it's worth introducing UI confusion in the command output. The best place to solve this would be either popping up a confirmation box for the user on click, or simply revert the state back to show the UI as "loading". Either would communicate to the user that the build is restarting

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm more worried about other folks looking at it and being confused about why it was running again after it had already built. I think we don't do a great job of showing history on builds -- we hit this issue with failures & retries as well, so we need some kind of pattern for it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated this to use the new build status, which is a better solution 👍


if record and not build:
build = Build.objects.create(
project=project,
version=version,
Expand Down Expand Up @@ -197,7 +210,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 @@ -213,17 +226,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