From 7feafd79a481216cdd85b4828e749fc5efacb8db Mon Sep 17 00:00:00 2001 From: Matthew Stell Date: Tue, 24 Jun 2025 07:27:52 +0100 Subject: [PATCH] Fixed #35846 -- Ensured consistent path ordering in ManifestStaticFilesStorage manifest files. This change reuses the existing sorting of `hashed_files` in `ManifestStaticFilesStorage.save_manifest` to also store a sorted `paths` mapping in the manifest file. This ensures stable manifest output that does not change unnecessarily. --- django/contrib/staticfiles/storage.py | 5 +++-- docs/releases/6.0.txt | 4 +++- tests/staticfiles_tests/test_storage.py | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 95e71e45fc..2ec107d55a 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -496,11 +496,12 @@ class ManifestFilesMixin(HashedFilesMixin): self.save_manifest() def save_manifest(self): + sorted_hashed_files = sorted(self.hashed_files.items()) self.manifest_hash = self.file_hash( - None, ContentFile(json.dumps(sorted(self.hashed_files.items())).encode()) + None, ContentFile(json.dumps(sorted_hashed_files).encode()) ) payload = { - "paths": self.hashed_files, + "paths": dict(sorted_hashed_files), "version": self.manifest_version, "hash": self.manifest_hash, } diff --git a/docs/releases/6.0.txt b/docs/releases/6.0.txt index 2e3e52444a..29912027ff 100644 --- a/docs/releases/6.0.txt +++ b/docs/releases/6.0.txt @@ -154,7 +154,9 @@ Minor features :mod:`django.contrib.staticfiles` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` now + ensures consistent path ordering in manifest files, making them more + reproducible and reducing unnecessary diffs. :mod:`django.contrib.syndication` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py index 35799f0ff7..9ef49491ff 100644 --- a/tests/staticfiles_tests/test_storage.py +++ b/tests/staticfiles_tests/test_storage.py @@ -568,6 +568,20 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase): self.assertEqual(manifest_hash, "") self.assertEqual(manifest_content, {"dummy.txt": "dummy.txt"}) + def test_manifest_file_consistent_content(self): + original_manifest_content = storage.staticfiles_storage.read_manifest() + hashed_files = storage.staticfiles_storage.hashed_files + # Force a change in the order of the hashed files. + with mock.patch.object( + storage.staticfiles_storage, + "hashed_files", + dict(reversed(hashed_files.items())), + ): + storage.staticfiles_storage.save_manifest() + manifest_file_content = storage.staticfiles_storage.read_manifest() + # The manifest file content should not change. + self.assertEqual(original_manifest_content, manifest_file_content) + @override_settings( STATIC_URL="/",