From c42c756b4a73c789fc0a23dac0255a38d86d8d88 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Tue, 20 Oct 2026 02:14:14 +0200 Subject: [PATCH] fix: group packs on everything that shapes them group_identical_platforms fingerprinted each file by destination, sha1 and md5. Two platforms whose lists differed only in required, zipped_file, size or crc32, or in how the frontend checks a file, would have shared the representative's pack and verify result. The fingerprint now carries every field of every entry and the platform's verification_mode and hash_rule. No current platform is regrouped. --- scripts/common.py | 18 +++++++++++------- tests/test_pack_grouping.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/scripts/common.py b/scripts/common.py index 3d3d6baa..f2e98204 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -1938,13 +1938,17 @@ def group_identical_platforms( inherits[platform] = False continue - entries = [] - for sys_id, system in sorted(config.get("systems", {}).items()): - for fe in system.get("files", []): - dest = fe.get("destination", fe.get("name", "")) - sha1 = fe.get("sha1", "") - md5 = fe.get("md5", "") - entries.append(f"{dest}|{sha1}|{md5}") + # Every field of every entry: the builder reads required, zipped_file, + # size and crc32 too, so two lists that differ only there are two + # packs. So is the way the frontend checks a file. + entries = [ + json.dumps(fe, sort_keys=True, default=str) + for _sys_id, system in sorted(config.get("systems", {}).items()) + for fe in system.get("files", []) + ] + entries.append( + f"mode={config.get('verification_mode', '')}|rule={config.get('hash_rule', '')}" + ) fp = hashlib.sha1("|".join(sorted(entries)).encode()).hexdigest() cores = config.get("cores") diff --git a/tests/test_pack_grouping.py b/tests/test_pack_grouping.py index e60125fd..d24674c4 100644 --- a/tests/test_pack_grouping.py +++ b/tests/test_pack_grouping.py @@ -75,5 +75,33 @@ class VariantGroupSpansSystems(unittest.TestCase): self.assertNotIn(name, names) +class AFrontendCheckSeparatesPacks(unittest.TestCase): + """Two lists of the same files under different checks are two packs: a + hash platform drops a file whose hash disagrees, an existence one ships + it. The fingerprint read destination, sha1 and md5 only.""" + + def test_verification_mode_and_entry_fields_split_groups(self): + import tempfile + + import yaml + + import common + + files = [{"name": "a.bin", "destination": "a.bin", "md5": "0" * 32}] + with tempfile.TemporaryDirectory() as tmp: + for name, mode, extra in (("one", "existence", {}), ("two", "md5", {}), + ("three", "existence", {"required": False})): + entry = {**files[0], **extra} + Path(tmp, f"{name}.yml").write_text(yaml.dump({ + "platform": name, "verification_mode": mode, + "systems": {"s": {"files": [entry]}}, + })) + Path(tmp, "_registry.yml").write_text(yaml.dump({"platforms": {}})) + common._platform_config_cache.clear() + self.addCleanup(common._platform_config_cache.clear) + groups = common.group_identical_platforms(["one", "two", "three"], tmp) + self.assertEqual(sorted(g for g, _ in groups), [["one"], ["three"], ["two"]]) + + if __name__ == "__main__": unittest.main()