From dbd47886ce895e828b8fa85d77fece9d2200a8fa Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 14:49:00 +0200 Subject: [PATCH] fix: name release assets as GitHub serves them --- scripts/generate_pack.py | 12 +++++++++--- scripts/generate_site.py | 3 ++- scripts/largefiles.py | 5 +++++ tests/test_audit_regressions.py | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/scripts/generate_pack.py b/scripts/generate_pack.py index dfe198e4..9bd4d711 100644 --- a/scripts/generate_pack.py +++ b/scripts/generate_pack.py @@ -76,7 +76,7 @@ import slot as slot_mod import slots import split_pack from deterministic_zip import _FIXED_DATE_TIME, rebuild_zip_deterministic -from largefiles import asset_name, load_registered_paths +from largefiles import asset_name, load_registered_paths, published_asset_name from nativemode import ( digest_algorithm, hash_mismatch_excludes_file, @@ -3226,8 +3226,14 @@ def _repo_relative(local_path: str, repo_root: str) -> str: def _release_asset_name(local_path: str, repo_root: str) -> str: - """The large-files asset install.py downloads for a gitignored file.""" - return asset_name(_repo_relative(local_path, repo_root), sorted(_registered(repo_root))) + """The large-files asset install.py downloads for a gitignored file. + + The name GitHub serves, not the one uploaded: a space in the path came + back as a 404 for an installer asking for it verbatim. + """ + return published_asset_name( + asset_name(_repo_relative(local_path, repo_root), sorted(_registered(repo_root))) + ) def _get_repo_path(sha1: str, db: dict) -> str: diff --git a/scripts/generate_site.py b/scripts/generate_site.py index d232ce0a..d0f563fc 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -2018,11 +2018,12 @@ def _fetch_url(path: str) -> str: LARGE_FILES_RELEASE, asset_names, load_registered_paths, + published_asset_name, ) _RELEASE_ASSET_NAMES = { registered: f"{REPO_URL}/releases/download/{LARGE_FILES_RELEASE}/" - + urllib.parse.quote(name.replace(" ", ".")) + + urllib.parse.quote(published_asset_name(name)) for registered, name in asset_names(load_registered_paths()).items() } return _RELEASE_ASSET_NAMES.get(path) or ( diff --git a/scripts/largefiles.py b/scripts/largefiles.py index b0eb21b8..02af75f1 100644 --- a/scripts/largefiles.py +++ b/scripts/largefiles.py @@ -88,6 +88,11 @@ def asset_name(path: str, registered: Iterable[str]) -> str: return asset_names([*registered, path])[path] +def published_asset_name(name: str) -> str: + """The name GitHub serves an asset under: it publishes a space as a dot.""" + return name.replace(" ", ".") + + def asset_candidates(name: str, registered: Iterable[str]) -> list[str]: """Assets that may hold *name*, a registered path or a bare file name. diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 78a10cde..a764432b 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -1993,6 +1993,25 @@ class ReleaseAssetCriterion(unittest.TestCase): finally: generate_pack._registered.cache_clear() + def test_the_manifest_names_the_asset_as_github_serves_it(self): + # GitHub publishes a space as a dot; the site linked the dotted + # name while the manifest gave install.py the spaced one, a 404. + from scripts import generate_pack + + with tempfile.TemporaryDirectory(dir=TMP_ROOT) as directory: + root = self._repo(directory, "bios/MAME/MAME 0.174 Arcade XML.dat") + target = Path(root, "bios", "MAME", "MAME 0.174 Arcade XML.dat") + target.parent.mkdir(parents=True) + target.write_bytes(b"x") + generate_pack._registered.cache_clear() + try: + self.assertEqual( + generate_pack._release_asset_name(str(target), root), + "MAME.0.174.Arcade.XML.dat", + ) + finally: + generate_pack._registered.cache_clear() + def test_a_gitignored_file_is_served_by_the_release_whatever_its_size(self): from scripts import generate_pack