fix: publish the games index whole or not at all

install/games/, the event schema, the launcher index and wiki/installer.md were truncated at their final name, so an interrupted run left a committed-looking partial JSON document. They now go through write_text_atomic.
This commit is contained in:
Abdessamad Derraz committed 2026-11-05 19:16:59 +01:00
1 parent 80f736c167
commit 891741433b
5 files changed
+31 -4

No files matched your search

+3 -1
View File
@@ -13,6 +13,8 @@ import json
import sys
from pathlib import Path
from artifacts import write_text_atomic
ROOT = Path(__file__).resolve().parent.parent
OUTPUT = ROOT / "schemas" / "install-events.schema.json"
@@ -105,7 +107,7 @@ def main(argv: list[str] | None = None) -> int:
print(f"{args.output} differs from EVENT_KEYS: run "
"python scripts/generate_event_schema.py", file=sys.stderr)
return 1
args.output.write_text(text, encoding="utf-8")
write_text_atomic(str(args.output), text)
print(f"Wrote {args.output}")
return 0
+2 -1
View File
@@ -59,6 +59,7 @@ from romfolders import ( # noqa: E402
folder_names,
load_setups,
)
from artifacts import write_text_atomic # noqa: E402
from safeparse import yaml_load # noqa: E402
import install # noqa: E402
@@ -635,7 +636,7 @@ def main() -> int:
path.unlink()
for path in changed:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(files[path], encoding="utf-8")
write_text_atomic(str(path), files[path])
print(f"{len(changed)} written, {len(stale)} removed")
return 1 if failures else 0
+3 -1
View File
@@ -45,6 +45,8 @@ import zipfile
from pathlib import Path, PurePosixPath
from typing import Callable
from artifacts import write_text_atomic
ROOT = Path(__file__).resolve().parent.parent
PAGE = ROOT / "wiki" / "installer.md"
INSTALLER = ROOT / "install.py"
@@ -643,7 +645,7 @@ def main(argv: list[str] | None = None) -> int:
print(f"{args.page} differs from install.py and install/*.json: run "
"python scripts/generate_installer_doc.py", file=sys.stderr)
return 1
args.page.write_text(text, encoding="utf-8")
write_text_atomic(str(args.page), text)
print(f"Wrote {args.page}")
return 0
+2 -1
View File
@@ -22,6 +22,7 @@ import json
import sys
from pathlib import Path
from artifacts import write_text_atomic
from build_port import (
BATOCERA_ZIP,
MUOS_APP,
@@ -121,7 +122,7 @@ def main(argv: list[str] | None = None) -> int:
"python scripts/generate_launcher_index.py", file=sys.stderr)
return 1
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(text, encoding="utf-8")
write_text_atomic(str(args.output), text)
print(f"Wrote {args.output}")
return 0
+21
View File
@@ -83,6 +83,27 @@ class EveryWriterGoesThroughIt(unittest.TestCase):
]
self.assertEqual(offenders, [])
def test_generators_publish_whole_files(self):
"""install/games/ (12 MB, read by install.py --for-games), the event
schema, the launcher index and wiki/installer.md were truncated at
their final name: an interrupted run left a committed-looking file
holding part of a JSON document. The installer doc's sandbox writes
throwaway fixtures under tmp/ and is not published."""
import ast
sandbox = {"write_small_list", "Sandbox"}
offenders = []
for path in sorted((REPO_ROOT / "scripts").glob("generate_*.py")):
tree = ast.parse(path.read_text(encoding="utf-8"))
for top in tree.body:
if path.name == "generate_installer_doc.py" and getattr(top, "name", "") in sandbox:
continue
for node in ast.walk(top):
if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)
and node.func.attr in ("write_text", "write_bytes")):
offenders.append(f"{path.name}:{node.lineno}")
self.assertEqual(offenders, [])
def test_the_text_is_utf8_whatever_the_locale(self):
import tempfile