mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
fix: restore release assets through a scratch file
This commit is contained in:
1 parent
6c9a41327b
commit
098b2f0b70
2 files changed
+40
-2
No files matched your search
+20
-2
@@ -15,6 +15,7 @@ import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
@@ -270,6 +271,24 @@ def _load_gitignored_large_files() -> set[str]:
|
||||
}
|
||||
|
||||
|
||||
def _restore(cached: str, path: str) -> None:
|
||||
"""Copy a fetched asset to its registered path, whole or not at all.
|
||||
|
||||
An interrupted copy at the final name is a truncated file the next scan
|
||||
hashes and publishes; a scratch beside it is swapped in only once full.
|
||||
"""
|
||||
directory = os.path.dirname(path)
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
fd, scratch = tempfile.mkstemp(dir=directory, prefix=".restore-")
|
||||
os.close(fd)
|
||||
try:
|
||||
shutil.copy2(cached, scratch)
|
||||
os.replace(scratch, path)
|
||||
finally:
|
||||
if os.path.exists(scratch):
|
||||
os.remove(scratch)
|
||||
|
||||
|
||||
def _preserve_large_file_entries(files: dict, db_path: str) -> int:
|
||||
"""Keep the entries of release assets the checkout does not hold.
|
||||
|
||||
@@ -309,8 +328,7 @@ def _preserve_large_file_entries(files: dict, db_path: str) -> int:
|
||||
expected_md5=entry.get("md5", ""),
|
||||
)
|
||||
if cached and not os.path.exists(path):
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
shutil.copy2(cached, path)
|
||||
_restore(cached, path)
|
||||
files[sha1] = entry
|
||||
count += 1
|
||||
return count
|
||||
|
||||
@@ -19,6 +19,7 @@ import threading
|
||||
import unittest
|
||||
import urllib.error
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
@@ -296,6 +297,25 @@ class PreservedLargeFileEntries(unittest.TestCase):
|
||||
self.assertEqual(files["b" * 40]["path"], "bios/Sony/PS3/FW.PUP")
|
||||
self.assertEqual((self.tmp / "bios/Sony/PS3/FW.PUP").read_bytes(), b"firmware")
|
||||
|
||||
def test_an_interrupted_restore_leaves_no_file_behind(self):
|
||||
cache = self.tmp / "cache" / "FW.PUP"
|
||||
cache.parent.mkdir()
|
||||
cache.write_bytes(b"firmware")
|
||||
common.fetch_large_file = lambda *_a, **_k: str(cache)
|
||||
db_path = self._write_db(
|
||||
{"b" * 40: {"name": "FW.PUP", "path": "bios/Sony/PS3/FW.PUP"}}
|
||||
)
|
||||
|
||||
def interrupted(_src, dst, *_a, **_k):
|
||||
Path(dst).write_bytes(b"firm")
|
||||
raise KeyboardInterrupt
|
||||
|
||||
with mock.patch.object(self.generate_db.shutil, "copy2", interrupted), \
|
||||
self.assertRaises(KeyboardInterrupt):
|
||||
self.generate_db._preserve_large_file_entries({}, db_path)
|
||||
directory = self.tmp / "bios/Sony/PS3"
|
||||
self.assertEqual(list(directory.iterdir()), [])
|
||||
|
||||
def test_a_file_sharing_only_the_name_is_not_preserved(self):
|
||||
common.fetch_large_file = lambda *_a, **_k: "/cache/large/FW.PUP"
|
||||
db_path = self._write_db(
|
||||
|
||||
Reference in new issue
Block a user