feat(devkit): add resumable workspace automation and UI review tooling
Dependency Audit / dependency-audit (push) Successful in 1m45s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m30s

Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
2026-09-09 02:03:17 +02:00
parent 14b19fbead
commit 2ffdb23f69
67 changed files with 17306 additions and 94 deletions
+271
View File
@@ -0,0 +1,271 @@
"""Resolve local tools once, without installation, network access or server startup."""
from __future__ import annotations
import hashlib
from itertools import islice
import os
from pathlib import Path
import shutil
import stat
import subprocess
import sys
from .common import digest
from .workspace import Project
from .process import require_capture
MAX_DISCOVERY_CHILDREN = 4096
def resolve_tools(workspace_root: Path, project: Project) -> dict[str, str]:
configured = project.config.get("tools", {})
if not isinstance(configured, dict) or set(configured) - {"python", "node", "npm"}:
raise ValueError("Project tools must configure only python, node and npm")
tools = {}
python_env = (
Path(
os.environ.get("GOVOPLAN_VENV_ROOT", str(workspace_root / "govoplan/.venv"))
)
/ "bin/python"
)
for name in ("python", "node", "npm"):
default = (
str(python_env)
if name == "python" and python_env.is_file()
else sys.executable
if name == "python"
else name
)
value = configured.get(name) or os.environ.get(name.upper()) or default
if not isinstance(value, str) or not value or "\0" in value:
raise ValueError(f"Invalid executable configuration for {name}")
executable = shutil.which(value)
if executable:
# Keep venv executable symlinks: resolving them loses Python's environment.
tools[name] = os.path.abspath(executable)
else:
tools[name] = value
return tools
def execution_environment(
workspace_root: Path, project: Project, tools: dict[str, str]
) -> dict[str, str]:
env = dict(os.environ)
if (
project.config.get("organization") == "GovOPlaN"
and "schema_version" not in project.config
):
# Managed GovOPlaN checks must not inherit another checkout's scope.
env["GOVOPLAN_WORKSPACE_ROOT"] = str(workspace_root.resolve())
core = next(
repo.path for repo in project.repositories if repo.name == "govoplan-core"
)
env["GOVOPLAN_CORE_ROOT"] = str(core)
env["GOVOPLAN_CORE_SOURCE_ROOT"] = str(core)
directories = [
str(Path(tools[name]).parent)
for name in ("python", "node", "npm")
if Path(tools[name]).is_absolute()
]
core_bins = workspace_root / "govoplan-core/webui/node_modules/.bin"
if core_bins.is_dir():
directories.insert(0, str(core_bins))
env["PATH"] = os.pathsep.join([*directories, env.get("PATH", "")])
env.update({name.upper(): value for name, value in tools.items()})
sources = [
str(repo.path / "src")
for repo in project.repositories
if (repo.path / "src").is_dir()
]
if sources:
env["PYTHONPATH"] = os.pathsep.join(
sources + ([env["PYTHONPATH"]] if env.get("PYTHONPATH") else [])
)
env["GIT_OPTIONAL_LOCKS"] = "0"
env["PYTHONDONTWRITEBYTECODE"] = "1"
return env
def tool_version(executable: str, env: dict[str, str]) -> str:
try:
result = require_capture(
[executable, "--version"],
timeout=15,
max_stdout=4096,
max_stderr=4096,
env=env,
)
return (
(result.stdout or result.stderr).decode(errors="replace").strip()[:256]
if result.returncode == 0
else "unavailable"
)
except (OSError, subprocess.TimeoutExpired, ValueError):
return "unavailable"
def _file_identity(metadata: os.stat_result) -> tuple[int, ...]:
return (
metadata.st_dev,
metadata.st_ino,
metadata.st_mode,
metadata.st_size,
metadata.st_ctime_ns,
metadata.st_mtime_ns,
)
def _environment_file_hash(path: Path, maximum: int) -> str | None:
"""Read a stable, bounded regular target without changing executable spelling.
Venv executables and package directories may legitimately be symlinks. Only
the read target is resolved; both names and the open descriptor are verified
again afterwards. Missing optional metadata stays absent, but a present
malformed or concurrently changing entry cannot certify an environment.
"""
try:
original = path.lstat()
except FileNotFoundError:
return None
try:
target = path.resolve(strict=True)
target_metadata = target.lstat()
descriptor = os.open(
target,
os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0),
)
with os.fdopen(descriptor, "rb") as handle:
before = os.fstat(handle.fileno())
if not stat.S_ISREG(before.st_mode) or before.st_size > maximum:
raise ValueError("Environment input must be a bounded regular file")
identity = _file_identity(before)
if identity != _file_identity(target_metadata):
raise ValueError("Environment input changed before fingerprinting")
hasher, size = hashlib.sha256(), 0
for chunk in iter(
lambda: handle.read(min(1024 * 1024, maximum - size + 1)), b""
):
size += len(chunk)
if size > maximum:
raise ValueError("Environment input grew beyond its size bound")
hasher.update(chunk)
if (
_file_identity(os.fstat(handle.fileno())) != identity
or _file_identity(target.lstat()) != identity
or _file_identity(path.lstat()) != _file_identity(original)
or path.resolve(strict=True) != target
):
raise ValueError("Environment input changed during fingerprinting")
return hasher.hexdigest()
except (OSError, RuntimeError) as exc:
raise ValueError("Environment input cannot be fingerprinted safely") from exc
def _discovery_path_shape(path: Path) -> dict:
"""Directory membership, not timestamps changed by ordinary build outputs."""
try:
metadata = path.lstat()
except (FileNotFoundError, NotADirectoryError):
return {"kind": "missing"}
try:
resolved = path.resolve()
try:
target_kind = stat.S_IFMT(resolved.lstat().st_mode)
except (FileNotFoundError, NotADirectoryError):
target_kind = "missing"
if target_kind == stat.S_IFLNK:
# Newer pathlib versions can retain a loop with strict=False.
raise ValueError("Native source discovery cannot be resolved safely")
return {
"kind": stat.S_IFMT(metadata.st_mode),
"resolved": str(resolved),
"target_kind": target_kind,
}
except (OSError, RuntimeError) as exc:
raise ValueError("Native source discovery cannot be resolved safely") from exc
def _native_discovery_fingerprint(workspace_root: Path, project: Project) -> str:
"""Bind glob discovery and registered ownership omitted by narrow Git scopes."""
children = list(islice(workspace_root.iterdir(), MAX_DISCOVERY_CHILDREN + 1))
if len(children) > MAX_DISCOVERY_CHILDREN:
raise ValueError("Native source discovery exceeds its bounded ownership audit")
def shape(path):
return {
name: _discovery_path_shape(path / name if name else path)
for name in ("", "src", "webui")
}
return digest(
{
"version": 1,
"siblings": [
{"name": child.name, "shape": shape(child)}
for child in sorted(children)
if child.name.startswith("govoplan")
],
"registered": [
{"name": repo.name, "path": str(repo.path), "shape": shape(repo.path)}
for repo in sorted(project.repositories, key=lambda item: item.name)
],
}
)
def environment_fingerprint(
workspace_root: Path, project: Project, tools: dict[str, str], env: dict[str, str]
) -> str:
identity = {
"environment": {
key: value
for key, value in env.items()
if key not in {"_", "SHLVL", "PWD", "OLDPWD"}
},
"tools": {},
}
if (
project.config.get("organization") == "GovOPlaN"
and "schema_version" not in project.config
):
identity["native_source_discovery"] = _native_discovery_fingerprint(
workspace_root, project
)
for name, executable in tools.items():
binary_hash = _environment_file_hash(Path(executable), 512 * 1024 * 1024)
identity["tools"][name] = {
"path": executable,
"version": tool_version(executable, env),
"sha256": binary_hash,
}
installed = {}
for repo in project.repositories:
for suffix in (
"node_modules/.package-lock.json",
"webui/node_modules/.package-lock.json",
".venv/pyvenv.cfg",
):
path = repo.path / suffix
value = _environment_file_hash(path, 32 * 1024 * 1024)
if value is not None:
installed[str(path)] = value
try:
result = require_capture(
[
tools["python"],
"-c",
"import importlib.metadata,json; print(json.dumps(sorted((d.metadata['Name'],d.version) for d in importlib.metadata.distributions())))",
],
timeout=30,
max_stdout=1024 * 1024,
env=env,
)
if result.returncode:
raise ValueError("Cannot fingerprint installed Python distributions")
installed["python_distributions"] = hashlib.sha256(result.stdout).hexdigest()
except (OSError, subprocess.TimeoutExpired) as exc:
raise ValueError("Cannot fingerprint Python environment") from exc
identity["installed"] = installed
return digest(identity)