feat(skills): store immutable plugin upstream snapshots
This commit is contained in:
48
app-instance/backend/beaver/plugins/transaction.py
Normal file
48
app-instance/backend/beaver/plugins/transaction.py
Normal file
@ -0,0 +1,48 @@
|
||||
"""Same-filesystem staging for plugin skill writes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import filecmp
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
class PluginSkillTransaction:
|
||||
def __init__(self, workspace: str | Path) -> None:
|
||||
self.workspace = Path(workspace)
|
||||
self.transaction_id = uuid4().hex
|
||||
self.root = self.workspace / ".beaver" / "staging" / "plugin-skills" / self.transaction_id
|
||||
self.root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def stage_upstream_snapshot(self, skill_name: str, source_id: str, tree_hash: str) -> Path:
|
||||
path = self.root / "upstreams" / skill_name / source_id / tree_hash
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def stage_skill_version(self, skill_name: str, version: str) -> Path:
|
||||
path = self.root / "versions" / skill_name / version
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def promote_directory(self, staged: Path, final: Path) -> None:
|
||||
if final.exists():
|
||||
if _directories_identical(staged, final):
|
||||
return
|
||||
raise ValueError(f"Immutable directory already exists with different content: {final}")
|
||||
final.parent.mkdir(parents=True, exist_ok=True)
|
||||
os.replace(staged, final)
|
||||
|
||||
def cleanup(self) -> None:
|
||||
shutil.rmtree(self.root, ignore_errors=True)
|
||||
|
||||
|
||||
def _directories_identical(left: Path, right: Path) -> bool:
|
||||
comparison = filecmp.dircmp(left, right)
|
||||
if comparison.left_only or comparison.right_only or comparison.funny_files:
|
||||
return False
|
||||
for filename in comparison.common_files:
|
||||
if not filecmp.cmp(left / filename, right / filename, shallow=False):
|
||||
return False
|
||||
return all(_directories_identical(left / name, right / name) for name in comparison.common_dirs)
|
||||
Reference in New Issue
Block a user