from contextlib import contextmanager import os from .common import WorkspaceError, read_json, safe_path, write_json class State: def __init__(self, root): self.root = root self.area = safe_path(root, ".workspace") self.path = safe_path(root, ".workspace/state.json") self.data = read_json(self.path) if self.path.exists() else {"schema": 1, "components": {}} if not isinstance(self.data, dict) or self.data.get("schema") != 1 or not isinstance(self.data.get("components"), dict): raise WorkspaceError("unsupported workspace state") for name, entry in self.data["components"].items(): if not isinstance(entry, dict) or entry.get("owner_root") != str(root): raise WorkspaceError(f"workspace owner mismatch: {name}") safe_path(root, entry.get("path", "")) def save(self): write_json(self.path, self.data) @contextmanager def operation(self): self.area.mkdir(exist_ok=True) path = safe_path(self.root, ".workspace/.operation.lock") try: descriptor = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600) except FileExistsError as error: raise WorkspaceError("workspace has another writer (.operation.lock); preserve the owner lock") from error try: with os.fdopen(descriptor, "w") as stream: stream.write(str(os.getpid()) + "\n") yield finally: path.unlink()