Why: Preserve a single Dev entry and independent repository ownership while allowing active feature branches to migrate without losing source or Git state. Evidence: SELF 29 real Git/Go workspace tests and ordinary component evidence/lock tests passed; detailed RED/GREEN runs are recorded by make evidence in the task card. Impact: Add fixed cloning, selected-version baselines, development overlays, npm source resolution, and independently preserved legacy snapshots. No other active worktree is modified. Refs: Versioned dependencies U2/U5; R15; feature/dependency-workspace
37 lines
1.5 KiB
Python
37 lines
1.5 KiB
Python
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()
|