Fixed #27658 -- Prevented collectstatic from overwriting newer files in remote storages.
Thanks revimi for the initial patch.
This commit is contained in:
parent
f60d4e704d
commit
c85831e4b7
|
@ -274,12 +274,24 @@ class Command(BaseCommand):
|
|||
# The full path of the target file
|
||||
if self.local:
|
||||
full_path = self.storage.path(prefixed_path)
|
||||
# If it's --link mode and the path isn't a link (i.e.
|
||||
# the previous collectstatic wasn't with --link) or if
|
||||
# it's non-link mode and the path is a link (i.e. the
|
||||
# previous collectstatic was with --link), the old
|
||||
# links/files must be deleted so it's not safe to skip
|
||||
# unmodified files.
|
||||
can_skip_unmodified_files = not (self.symlink ^ os.path.islink(full_path))
|
||||
else:
|
||||
full_path = None
|
||||
# Skip the file if the source file is younger
|
||||
# In remote storages, skipping is only based on the
|
||||
# modified times since symlinks aren't relevant.
|
||||
can_skip_unmodified_files = True
|
||||
# Avoid sub-second precision (see #14665, #19540)
|
||||
if (target_last_modified.replace(microsecond=0) >= source_last_modified.replace(microsecond=0) and
|
||||
full_path and not (self.symlink ^ os.path.islink(full_path))):
|
||||
file_is_unmodified = (
|
||||
target_last_modified.replace(microsecond=0) >=
|
||||
source_last_modified.replace(microsecond=0)
|
||||
)
|
||||
if file_is_unmodified and can_skip_unmodified_files:
|
||||
if prefixed_path not in self.unmodified_files:
|
||||
self.unmodified_files.append(prefixed_path)
|
||||
self.log("Skipping '%s' (not modified)" % path)
|
||||
|
|
|
@ -17,3 +17,6 @@ Bugfixes
|
|||
|
||||
* Fixed a regression in the ``timesince`` and ``timeuntil`` filters that caused
|
||||
incorrect results for dates in a leap year (:ticket:`27637`).
|
||||
|
||||
* Fixed a regression where ``collectstatic`` overwrote newer files in remote
|
||||
storages (:ticket:`27658`).
|
||||
|
|
|
@ -82,7 +82,8 @@ class CollectionTestCase(BaseStaticFilesMixin, SimpleTestCase):
|
|||
super(CollectionTestCase, self).tearDown()
|
||||
|
||||
def run_collectstatic(self, **kwargs):
|
||||
call_command('collectstatic', interactive=False, verbosity=0,
|
||||
verbosity = kwargs.pop('verbosity', 0)
|
||||
call_command('collectstatic', interactive=False, verbosity=verbosity,
|
||||
ignore_patterns=['*.ignoreme'], **kwargs)
|
||||
|
||||
def _get_file(self, filepath):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import errno
|
||||
import os
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles.storage import CachedStaticFilesStorage
|
||||
|
@ -59,6 +59,14 @@ class PathNotImplementedStorage(storage.Storage):
|
|||
raise NotImplementedError
|
||||
|
||||
|
||||
class NeverCopyRemoteStorage(PathNotImplementedStorage):
|
||||
"""
|
||||
Return a future modified time for all files so that nothing is collected.
|
||||
"""
|
||||
def get_modified_time(self, name):
|
||||
return datetime.now() + timedelta(days=30)
|
||||
|
||||
|
||||
class QueryStringStorage(storage.Storage):
|
||||
def url(self, path):
|
||||
return path + '?a=b&c=d'
|
||||
|
|
|
@ -399,6 +399,23 @@ class TestCollectionNonLocalStorage(TestNoFilesCreated, CollectionTestCase):
|
|||
storage.path('name')
|
||||
|
||||
|
||||
class TestCollectionNeverCopyStorage(CollectionTestCase):
|
||||
|
||||
@override_settings(STATICFILES_STORAGE='staticfiles_tests.storage.NeverCopyRemoteStorage')
|
||||
def test_skips_newer_files_in_remote_storage(self):
|
||||
"""
|
||||
collectstatic skips newer files in a remote storage.
|
||||
run_collectstatic() in setUp() copies the static files, then files are
|
||||
always skipped after NeverCopyRemoteStorage is activated since
|
||||
NeverCopyRemoteStorage.get_modified_time() returns a datetime in the
|
||||
future to simulate an unmodified file.
|
||||
"""
|
||||
stdout = six.StringIO()
|
||||
self.run_collectstatic(stdout=stdout, verbosity=2)
|
||||
output = force_text(stdout.getvalue())
|
||||
self.assertIn("Skipping 'test.txt' (not modified)", output)
|
||||
|
||||
|
||||
@unittest.skipUnless(symlinks_supported(), "Must be able to symlink to run this test.")
|
||||
class TestCollectionLinks(TestDefaults, CollectionTestCase):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue