diff --git a/readthedocs/builds/constants.py b/readthedocs/builds/constants.py index 9f0f7a1d6bc..499be07e515 100644 --- a/readthedocs/builds/constants.py +++ b/readthedocs/builds/constants.py @@ -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'), ) diff --git a/readthedocs/builds/views.py b/readthedocs/builds/views.py index 3167570752f..88e2007cf70 100644 --- a/readthedocs/builds/views.py +++ b/readthedocs/builds/views.py @@ -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 diff --git a/readthedocs/core/utils/__init__.py b/readthedocs/core/utils/__init__.py index 0c2229ba4ed..25955a6f26c 100644 --- a/readthedocs/core/utils/__init__.py +++ b/readthedocs/core/utils/__init__.py @@ -1,5 +1,6 @@ """Common utilty functions.""" +from datetime import datetime import errno import logging import os @@ -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 @@ -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, @@ -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', @@ -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, @@ -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. @@ -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, ) diff --git a/readthedocs/rtd_tests/tests/test_views.py b/readthedocs/rtd_tests/tests/test_views.py index f9fbb5e8159..cb063ac850f 100644 --- a/readthedocs/rtd_tests/tests/test_views.py +++ b/readthedocs/rtd_tests/tests/test_views.py @@ -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): diff --git a/readthedocs/templates/builds/build_detail.html b/readthedocs/templates/builds/build_detail.html index 4124600f3ed..eec4d1de93c 100644 --- a/readthedocs/templates/builds/build_detail.html +++ b/readthedocs/templates/builds/build_detail.html @@ -69,6 +69,22 @@ + + + {% if request.user|is_admin:project %} +