Skip to content

Commit 8b1af68

Browse files
committed
Merge pull request #1584 from rtfd/remove-project-artifacts-from-all-web
Remove project artifacts from all web servers on project delete
2 parents f70e4a5 + fa95102 commit 8b1af68

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

readthedocs/projects/tasks.py

+13
Original file line numberDiff line numberDiff line change
@@ -825,3 +825,16 @@ def clear_htmlzip_artifacts(version):
825825

826826
def clear_html_artifacts(version):
827827
run_on_app_servers('rm -rf %s' % version.project.rtd_build_path(version=version.slug))
828+
829+
830+
@task(queue='web')
831+
def remove_path_from_web(path):
832+
"""
833+
Remove the given path from the web servers file system.
834+
"""
835+
# Santity check for spaces in the path since spaces would result in
836+
# deleting unpredictable paths with "rm -rf".
837+
assert ' ' not in path, "No spaces allowed in path"
838+
839+
# TODO: We need some proper escaping here for the given path.
840+
run_on_app_servers('rm -rf {path}'.format(path=path))

readthedocs/projects/views/private.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
RedirectForm, WebHookForm)
3535
from readthedocs.projects.models import Project, EmailHook, WebHook
3636
from readthedocs.projects import constants, tasks
37+
from readthedocs.projects.tasks import remove_path_from_web
3738

3839

3940
from readthedocs.projects.signals import project_import
@@ -214,7 +215,8 @@ def project_delete(request, project_slug):
214215

215216
if request.method == 'POST':
216217
# Remove the repository checkout
217-
shutil.rmtree(project.doc_path, ignore_errors=True)
218+
remove_path_from_web.delay(path=project.doc_path)
219+
218220
# Delete the project and everything related to it
219221
project.delete()
220222
messages.success(request, _('Project deleted'))

readthedocs/rtd_tests/tests/test_project_views.py

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import re
2-
31
from django.contrib.auth.models import User
42
from django.contrib.messages import constants as message_const
3+
from django_dynamic_fixture import get
4+
from django_dynamic_fixture import new
5+
from mock import patch
56

67
from readthedocs.rtd_tests.base import WizardTestCase, MockBuildTestCase
78
from readthedocs.projects.models import Project
@@ -185,19 +186,38 @@ def test_import_demo_imported_duplicate(self):
185186

186187

187188
class TestPrivateViews(MockBuildTestCase):
188-
fixtures = ['test_data', 'eric']
189-
190189
def setUp(self):
190+
self.user = new(User, username='eric')
191+
self.user.set_password('test')
192+
self.user.save()
191193
self.client.login(username='eric', password='test')
192194

193195
def test_versions_page(self):
196+
pip = get(Project, slug='pip', users=[self.user])
197+
pip.versions.create(verbose_name='1.0')
198+
194199
response = self.client.get('/projects/pip/versions/')
195200
self.assertEqual(response.status_code, 200)
196201

197202
# Test if the versions page works with a version that contains a slash.
198203
# That broke in the past, see issue #1176.
199-
pip = Project.objects.get(slug='pip')
200204
pip.versions.create(verbose_name='1.0/with-slash')
201205

202206
response = self.client.get('/projects/pip/versions/')
203207
self.assertEqual(response.status_code, 200)
208+
209+
def test_delete_project(self):
210+
project = get(Project, slug='pip', users=[self.user])
211+
212+
response = self.client.get('/dashboard/pip/delete/')
213+
self.assertEqual(response.status_code, 200)
214+
215+
patcher = patch(
216+
'readthedocs.projects.views.private.remove_path_from_web')
217+
with patcher as remove_path_from_web:
218+
response = self.client.post('/dashboard/pip/delete/')
219+
self.assertEqual(response.status_code, 302)
220+
221+
self.assertFalse(Project.objects.filter(slug='pip').exists())
222+
remove_path_from_web.delay.assert_called_with(
223+
path=project.doc_path)

0 commit comments

Comments
 (0)