fix: make skills manifest writes atomic
Uses temp file + fsync + os.replace() to avoid corruption if the process crashes mid-write. Cleans up temp file on failure, logs errors at debug level. Based on PR #335 by @aydnOktay — adapted for the current v2 manifest format (name:hash).
This commit is contained in:
parent
af67ea8800
commit
4608a7fe4e
1 changed files with 29 additions and 3 deletions
|
|
@ -69,10 +69,36 @@ def _read_manifest() -> Dict[str, str]:
|
||||||
|
|
||||||
|
|
||||||
def _write_manifest(entries: Dict[str, str]):
|
def _write_manifest(entries: Dict[str, str]):
|
||||||
"""Write the manifest file in v2 format (name:hash)."""
|
"""Write the manifest file atomically in v2 format (name:hash).
|
||||||
|
|
||||||
|
Uses a temp file + os.replace() to avoid corruption if the process
|
||||||
|
crashes or is interrupted mid-write.
|
||||||
|
"""
|
||||||
|
import tempfile
|
||||||
|
|
||||||
MANIFEST_FILE.parent.mkdir(parents=True, exist_ok=True)
|
MANIFEST_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||||
lines = [f"{name}:{hash_val}" for name, hash_val in sorted(entries.items())]
|
data = "\n".join(f"{name}:{hash_val}" for name, hash_val in sorted(entries.items())) + "\n"
|
||||||
MANIFEST_FILE.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
||||||
|
try:
|
||||||
|
fd, tmp_path = tempfile.mkstemp(
|
||||||
|
dir=str(MANIFEST_FILE.parent),
|
||||||
|
prefix=".bundled_manifest_",
|
||||||
|
suffix=".tmp",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||||
|
f.write(data)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno())
|
||||||
|
os.replace(tmp_path, MANIFEST_FILE)
|
||||||
|
except BaseException:
|
||||||
|
try:
|
||||||
|
os.unlink(tmp_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug("Failed to write skills manifest %s: %s", MANIFEST_FILE, e, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
def _discover_bundled_skills(bundled_dir: Path) -> List[Tuple[str, Path]]:
|
def _discover_bundled_skills(bundled_dir: Path) -> List[Tuple[str, Path]]:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue