fix(kc_format): eliminate duplicate silo/manifest.json entries in .kc files
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Two code paths were appending silo/manifest.json to the ZIP without removing the previous entry, causing Python's zipfile module to warn about duplicate names: 1. slotFinishSaveDocument() re-injected the cached manifest from entries, then the modified_at update branch wrote a second copy. 2. update_manifest_fields() opened the ZIP in append mode and wrote an updated manifest without removing the old one. Fix slotFinishSaveDocument() by preparing the final manifest (with updated modified_at) in the entries dict before writing, so only one copy is written to the ZIP. Fix update_manifest_fields() by rewriting the ZIP atomically via a temp file, deduplicating any pre-existing duplicate entries in the process.
This commit is contained in:
@@ -162,34 +162,28 @@ class _KcFormatObserver:
|
||||
f"kc_format: pre_reinject hook failed: {exc}\n"
|
||||
)
|
||||
try:
|
||||
# Ensure silo/manifest.json exists in entries and update modified_at.
|
||||
# All manifest mutations happen here so only one copy is written.
|
||||
if "silo/manifest.json" in entries:
|
||||
try:
|
||||
manifest = json.loads(entries["silo/manifest.json"])
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
manifest = _default_manifest()
|
||||
else:
|
||||
manifest = _default_manifest()
|
||||
manifest["modified_at"] = datetime.now(timezone.utc).strftime(
|
||||
"%Y-%m-%dT%H:%M:%SZ"
|
||||
)
|
||||
entries["silo/manifest.json"] = (
|
||||
json.dumps(manifest, indent=2) + "\n"
|
||||
).encode("utf-8")
|
||||
|
||||
with zipfile.ZipFile(filename, "a") as zf:
|
||||
existing = set(zf.namelist())
|
||||
# Re-inject cached silo/ entries
|
||||
if entries:
|
||||
for name, data in entries.items():
|
||||
if name not in existing:
|
||||
zf.writestr(name, data)
|
||||
existing.add(name)
|
||||
# Ensure silo/manifest.json exists
|
||||
if "silo/manifest.json" not in existing:
|
||||
manifest = _default_manifest()
|
||||
zf.writestr(
|
||||
"silo/manifest.json",
|
||||
json.dumps(manifest, indent=2) + "\n",
|
||||
)
|
||||
else:
|
||||
# Update modified_at timestamp
|
||||
raw = zf.read("silo/manifest.json")
|
||||
manifest = json.loads(raw)
|
||||
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
if manifest.get("modified_at") != now:
|
||||
manifest["modified_at"] = now
|
||||
# ZipFile append mode can't overwrite; write new entry
|
||||
# (last duplicate wins in most ZIP readers)
|
||||
zf.writestr(
|
||||
"silo/manifest.json",
|
||||
json.dumps(manifest, indent=2) + "\n",
|
||||
)
|
||||
for name, data in entries.items():
|
||||
if name not in existing:
|
||||
zf.writestr(name, data)
|
||||
existing.add(name)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"kc_format: failed to update .kc silo/ entries: {e}\n"
|
||||
@@ -209,17 +203,36 @@ def update_manifest_fields(filename, updates):
|
||||
return
|
||||
if not os.path.isfile(filename):
|
||||
return
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
try:
|
||||
with zipfile.ZipFile(filename, "a") as zf:
|
||||
if "silo/manifest.json" not in zf.namelist():
|
||||
return
|
||||
raw = zf.read("silo/manifest.json")
|
||||
manifest = json.loads(raw)
|
||||
manifest.update(updates)
|
||||
zf.writestr(
|
||||
"silo/manifest.json",
|
||||
json.dumps(manifest, indent=2) + "\n",
|
||||
)
|
||||
fd, tmp = tempfile.mkstemp(suffix=".kc", dir=os.path.dirname(filename))
|
||||
os.close(fd)
|
||||
try:
|
||||
with (
|
||||
zipfile.ZipFile(filename, "r") as zin,
|
||||
zipfile.ZipFile(tmp, "w", compression=zipfile.ZIP_DEFLATED) as zout,
|
||||
):
|
||||
found = False
|
||||
for item in zin.infolist():
|
||||
if item.filename == "silo/manifest.json":
|
||||
if found:
|
||||
continue # skip duplicate entries
|
||||
found = True
|
||||
raw = zin.read(item.filename)
|
||||
manifest = json.loads(raw)
|
||||
manifest.update(updates)
|
||||
zout.writestr(
|
||||
item.filename,
|
||||
json.dumps(manifest, indent=2) + "\n",
|
||||
)
|
||||
else:
|
||||
zout.writestr(item, zin.read(item.filename))
|
||||
shutil.move(tmp, filename)
|
||||
except BaseException:
|
||||
os.unlink(tmp)
|
||||
raise
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"kc_format: failed to update manifest: {e}\n")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user