From 4befb3015c26810a68cfcf57e0cd8b062f56f1c5 Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Tue, 10 Dec 2013 00:29:39 +0700 Subject: [PATCH] Fixed #21581 -- Fixed a number of issues with collectstatic. When STATIC_ROOT wasn't set, collectstatic --clear would delete every files within the current directory and its descendants. This patch makes the following changes: Prevent collectstatic from running if STATIC_ROOT isn't set. Fixed an issue that prevented collectstatic from displaying the destination directory. Changed the warning header to notify when the command is run in dry-run mode. --- django/conf/global_settings.py | 2 +- .../management/commands/collectstatic.py | 40 +++++++++++-------- django/contrib/staticfiles/storage.py | 11 ++--- docs/ref/settings.txt | 2 +- docs/releases/1.6.2.txt | 5 +++ tests/staticfiles_tests/tests.py | 11 +++++ 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index d29dd5ec34..d86a4272cd 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -290,7 +290,7 @@ MEDIA_URL = '' # Absolute path to the directory static files should be collected to. # Example: "/var/www/example.com/static/" -STATIC_ROOT = '' +STATIC_ROOT = None # URL that handles the static files served from STATIC_ROOT. # Example: "http://example.com/static/", "http://static.example.com/" diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py index 67fa6229c4..9009b82c67 100644 --- a/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/django/contrib/staticfiles/management/commands/collectstatic.py @@ -137,32 +137,38 @@ class Command(NoArgsCommand): def handle_noargs(self, **options): self.set_options(**options) - # Warn before doing anything more. - if (isinstance(self.storage, FileSystemStorage) and + + message = ['\n'] + if self.dry_run: + message.append( + 'You have activated the --dry-run option so no files will be modified.\n\n' + ) + + message.append( + 'You have requested to collect static files at the destination\n' + 'location as specified in your settings' + ) + + if (isinstance(self.storage._wrapped, FileSystemStorage) and self.storage.location): destination_path = self.storage.location - destination_display = ':\n\n %s' % destination_path + message.append(':\n\n %s\n\n' % destination_path) else: destination_path = None - destination_display = '.' + message.append('.\n\n') if self.clear: - clear_display = 'This will DELETE EXISTING FILES!' + message.append('This will DELETE EXISTING FILES!\n') else: - clear_display = 'This will overwrite existing files!' + message.append('This will overwrite existing files!\n') - if self.interactive: - confirm = input(""" -You have requested to collect static files at the destination -location as specified in your settings%s + message.append( + 'Are you sure you want to do this?\n\n' + "Type 'yes' to continue, or 'no' to cancel: " + ) -%s -Are you sure you want to do this? - -Type 'yes' to continue, or 'no' to cancel: """ -% (destination_display, clear_display)) - if confirm != 'yes': - raise CommandError("Collecting static files cancelled.") + if self.interactive and input(''.join(message)) != 'yes': + raise CommandError("Collecting static files cancelled.") collected = self.collect() modified_count = len(collected['modified']) diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 5190a299e4..b5d05aa6b2 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -32,16 +32,13 @@ class StaticFilesStorage(FileSystemStorage): location = settings.STATIC_ROOT if base_url is None: base_url = settings.STATIC_URL - check_settings(base_url) - super(StaticFilesStorage, self).__init__(location, base_url, - *args, **kwargs) - - def path(self, name): - if not self.location: + if not location: raise ImproperlyConfigured("You're using the staticfiles app " "without having set the STATIC_ROOT " "setting to a filesystem path.") - return super(StaticFilesStorage, self).path(name) + check_settings(base_url) + super(StaticFilesStorage, self).__init__(location, base_url, + *args, **kwargs) class CachedFilesMixin(object): diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index e4dfa7fbfe..0fa463ebb9 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -2535,7 +2535,7 @@ Settings for :mod:`django.contrib.staticfiles`. STATIC_ROOT ----------- -Default: ``''`` (Empty string) +Default: ``None`` The absolute path to the directory where :djadmin:`collectstatic` will collect static files for deployment. diff --git a/docs/releases/1.6.2.txt b/docs/releases/1.6.2.txt index 41bc86e906..6043d21815 100644 --- a/docs/releases/1.6.2.txt +++ b/docs/releases/1.6.2.txt @@ -14,3 +14,8 @@ Bug fixes * Fixed a crash when executing the :djadmin:`changepassword` command when the user object representation contained non-ASCII characters (#21627). + +* The :djadmin:`collectstatic` command will raise an error rather than + default to using the current working directory if :setting:`STATIC_ROOT` is + not set. Combined with the ``--clear`` option, the previous behavior could + wipe anything below the current working directory. diff --git a/tests/staticfiles_tests/tests.py b/tests/staticfiles_tests/tests.py index 19c244e353..1de9f9ed84 100644 --- a/tests/staticfiles_tests/tests.py +++ b/tests/staticfiles_tests/tests.py @@ -224,6 +224,17 @@ class TestFindStatic(CollectionTestCase, TestDefaults): self.assertIn('apps', force_text(lines[1])) +class TestConfiguration(StaticFilesTestCase): + def test_location_empty(self): + err = six.StringIO() + for root in ['', None]: + with override_settings(STATIC_ROOT=root): + with six.assertRaisesRegex( + self, ImproperlyConfigured, + 'without having set the STATIC_ROOT setting to a filesystem path'): + call_command('collectstatic', interactive=False, verbosity=0, stderr=err) + + class TestCollection(CollectionTestCase, TestDefaults): """ Test ``collectstatic`` management command.