mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-21 03:09:08 -05:00
fix: write each export as its bytes and mode
write_text translated line ends on Windows, so FirmwareDatabase.cs's CRLF would have come out CR CR LF; it replaced the published file in place, so a run stopped mid-write left it truncated; and it dropped the mode, while rocknix-systems is 100755 in ROCKNIX's tree. Files are written atomically as UTF-8 bytes, and an exporter states the mode of any file the platform does not publish as 0644.
This commit is contained in:
1 parent
cbe769e74c
commit
85c2433c98
4 files changed
+55
-5
No files matched your search
@@ -562,10 +562,7 @@ def export_platform(
|
||||
|
||||
platform_dir = output_dir / platform
|
||||
_remove_stale(platform_dir, set(produced))
|
||||
for relative, content in produced.items():
|
||||
path = platform_dir / relative
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(content, encoding="utf-8")
|
||||
_write_produced(platform_dir, produced, exporter.file_modes())
|
||||
|
||||
changes = landed_changes(exporter, systems)
|
||||
applied, filled = changes.applied, changes.filled
|
||||
@@ -663,6 +660,21 @@ def export_platform(
|
||||
return True, messages
|
||||
|
||||
|
||||
def _write_produced(root: Path, produced: dict[str, str], modes: dict[str, int]) -> None:
|
||||
"""Write each file as its bytes, whole or not at all, with its mode.
|
||||
|
||||
write_text translated line ends on Windows, so FirmwareDatabase.cs's
|
||||
CRLF would have come out CR CR LF, and a run stopped mid-write left a
|
||||
truncated file in exports/. The scratch file mkstemp makes is 0600: the
|
||||
mode is set from what the platform's own file has.
|
||||
"""
|
||||
for relative, content in produced.items():
|
||||
path = root / relative
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
write_bytes_atomic(str(path), content.encode("utf-8"))
|
||||
path.chmod(modes.get(relative, 0o644))
|
||||
|
||||
|
||||
def write_report(path: Path, report: dict) -> None:
|
||||
"""What one platform's export changed, for the page that publishes it.
|
||||
|
||||
|
||||
@@ -126,6 +126,11 @@ class BaseExporter(ABC):
|
||||
"""
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def file_modes() -> dict[str, int]:
|
||||
"""The mode of each file the platform publishes other than 0644."""
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def keys_by_system() -> bool:
|
||||
"""Whether the platform finds a file through its system's own name.
|
||||
|
||||
@@ -77,6 +77,11 @@ class Exporter(BatoceraExporter):
|
||||
def native_filename() -> str:
|
||||
return "rocknix-systems"
|
||||
|
||||
@staticmethod
|
||||
def file_modes() -> dict[str, int]:
|
||||
# 100755 in ROCKNIX's tree at the pinned tag (git/trees 20261001).
|
||||
return {"rocknix-systems": 0o755}
|
||||
|
||||
@staticmethod
|
||||
def native_sources() -> dict[str, str]:
|
||||
return {"rocknix-systems": SOURCE_URL, EMULATORS_MK: EMULATORS_URL}
|
||||
+29
-1
@@ -1153,7 +1153,35 @@ class AnExportLeavesNoStaleFile(unittest.TestCase):
|
||||
def test_the_export_clears_before_it_writes(self):
|
||||
source = (REPO_ROOT / "scripts" / "export_native.py").read_text()
|
||||
body = source[source.index("def export_platform("):]
|
||||
self.assertLess(body.index("_remove_stale("), body.index(".write_text("))
|
||||
self.assertLess(body.index("_remove_stale("), body.index("_write_produced("))
|
||||
|
||||
|
||||
class AnExportIsWrittenAsItsBytes(unittest.TestCase):
|
||||
"""write_text translated line ends on Windows (FirmwareDatabase.cs is
|
||||
CRLF, it would have become CR CR LF), replaced the published file in
|
||||
place, and dropped the mode: rocknix-systems is 100755 upstream."""
|
||||
|
||||
def test_bytes_mode_and_atomicity(self):
|
||||
import stat # noqa: PLC0415
|
||||
|
||||
import export_native # noqa: PLC0415
|
||||
|
||||
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp:
|
||||
root = Path(tmp)
|
||||
export_native._write_produced(root, {
|
||||
"FirmwareDatabase.cs": "a\r\nb\r\n",
|
||||
"rocknix-systems": "#!/usr/bin/python\n",
|
||||
}, {"rocknix-systems": 0o755})
|
||||
self.assertEqual((root / "FirmwareDatabase.cs").read_bytes(), b"a\r\nb\r\n")
|
||||
mode = lambda name: stat.S_IMODE((root / name).stat().st_mode) # noqa: E731
|
||||
self.assertEqual(mode("rocknix-systems"), 0o755)
|
||||
self.assertEqual(mode("FirmwareDatabase.cs"), 0o644)
|
||||
self.assertEqual(sorted(p.name for p in root.iterdir()),
|
||||
["FirmwareDatabase.cs", "rocknix-systems"])
|
||||
|
||||
def test_rocknix_says_its_script_is_executable(self):
|
||||
self.assertEqual(discover_exporters()["rocknix"].file_modes(),
|
||||
{"rocknix-systems": 0o755})
|
||||
|
||||
|
||||
class AnExportWithoutTruthIsRefused(unittest.TestCase):
|
||||
|
||||
Reference in new issue
Block a user