mirror of
https://github.com/rommapp/romm.git
synced 2026-06-27 22:35:57 +00:00
Cleanup pass on save-sync addressing three independent failure modes that interact in production data: content_hash drift between client and server, null-slot archival saves leaking into sync flows, and content-hash dedupe collapsing legitimately-distinct slots. Bug fixes - compute_content_hash dispatched on zipfile.is_zipfile(relative_path), which silently returned False whenever the process's CWD wasn't ASSETS_BASE_PATH. Every zip save fell through to the raw-MD5 branch, persisting hashes that disagreed with clients computing the intended per-entry zip-hash. Resolve to a full path before the dispatch. - _build_negotiate_plan, sync_push_pull_task, and sync_watcher all treated null-slot saves as sync-eligible. Null-slot saves represent web-UI / archival uploads; including them in negotiate plans matched them against device pushes by filename and overwrote archival data. Filter null-slot saves at all three call sites. - get_save_by_content_hash matched on (rom_id, user_id, content_hash) only, so identical bytes uploaded to different slots collapsed into one record. Scope the lookup by slot when provided so clone-save- to-new-slot creates a distinct row per slot. - get_save_by_filename matched on (rom_id, user_id, file_name) only. When two uploads to different slots happened in the same wall-clock second (the datetime tag is per-second), the second upload UPDATED the first record's slot instead of creating a distinct row. Scope the filename lookup by slot too. One-shot recovery - New recompute_save_content_hashes manual task walks every Save row, recomputes via the fixed dispatch, and updates rows whose values differ. Idempotent; safe to re-run. - Backend startup runs a COUNT(content_hash IS NULL) query and, if any rows exist, enqueues the recompute task on the low-priority RQ queue. The API process moves on; the worker handles the recompute out-of-band. Subsequent restarts find zero NULL hashes and skip. Admins can also trigger the task manually. Test infrastructure - Added tests/_zipfile_shim.reload_zipfile() mirroring the pattern from utils/zip_cache.py for the same zipfile-inflate64 + CPython 3.13.5 incompatibility. Test fixtures that build ZIPs call it immediately before opening the archive.
20 lines
647 B
Python
20 lines
647 B
Python
"""Restore the stdlib zipfile module before writing fixture ZIPs.
|
|
|
|
Mirrors the pattern from `backend/utils/zip_cache.py`: a third-party
|
|
package (`zipfile-inflate64`) in the import chain replaces
|
|
`zipfile._get_compressor` with an incompatible signature on
|
|
CPython 3.13.5, breaking `ZipFile.write()` / `ZipFile.writestr()`.
|
|
Reloading restores the original implementation.
|
|
|
|
Tests that build fixture ZIPs should call `reload_zipfile()` immediately
|
|
before opening the archive.
|
|
"""
|
|
|
|
import importlib
|
|
import zipfile
|
|
|
|
|
|
def reload_zipfile() -> None:
|
|
"""Restore stdlib zipfile internals overridden by zipfile-inflate64."""
|
|
importlib.reload(zipfile)
|