Files
molecule-ai-workspace-runtime/scripts/check_consumer_runtime_drift.py
sdk-lead f9188156b0
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 15s
ci / lint (pull_request) Successful in 56s
ci / unit-tests (pull_request) Successful in 1m8s
ci / smoke-install (pull_request) Successful in 1m13s
ci / build (pull_request) Successful in 1m30s
chore: remove unmaintained runtime paths
2026-05-24 19:22:40 -07:00

200 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Fail if runtime consumers vendor editable runtime source.
The standalone molecule-ai-workspace-runtime repo is the SSOT for
``molecule_runtime``. Template repos and molecule-core may pin/install the
package, but they must not carry their own editable copy of the runtime package
or resurrect the old top-level ``workspace/`` runtime tree.
"""
from __future__ import annotations
import argparse
import os
import shutil
import subprocess
import sys
import tempfile
from dataclasses import dataclass
from pathlib import Path
from urllib.parse import quote, urlsplit
DEFAULT_CONSUMERS = (
"molecule-ai-workspace-template-claude-code",
"molecule-ai-workspace-template-hermes",
"molecule-ai-workspace-template-openclaw",
"molecule-ai-workspace-template-codex",
"molecule-core",
)
SKIP_DIRS = {
".git",
".mypy_cache",
".pytest_cache",
".ruff_cache",
".tox",
".venv",
"__pycache__",
"build",
"dist",
"node_modules",
"venv",
}
@dataclass(frozen=True)
class DriftFinding:
repo: str
path: str
reason: str
def find_runtime_drift(repo_name: str, repo_path: Path) -> list[DriftFinding]:
findings: list[DriftFinding] = []
workspace_dir = repo_path / "workspace"
if workspace_dir.is_dir():
findings.append(
DriftFinding(
repo=repo_name,
path="workspace/",
reason="top-level workspace/ runtime tree is forbidden; use the runtime package",
)
)
for root, dirs, _files in os.walk(repo_path):
dirs[:] = [name for name in dirs if name not in SKIP_DIRS]
current = Path(root)
for dirname in list(dirs):
if dirname != "molecule_runtime":
continue
rel = (current / dirname).relative_to(repo_path).as_posix() + "/"
findings.append(
DriftFinding(
repo=repo_name,
path=rel,
reason="vendored molecule_runtime/ package is forbidden; import the SSOT package",
)
)
return findings
def clone_consumers(
workdir: Path,
repos: tuple[str, ...],
*,
gitea_url: str,
token: str,
) -> dict[str, Path]:
if not token:
raise RuntimeError("GITEA_TOKEN is required when --root is not provided")
paths: dict[str, Path] = {}
parsed_url = urlsplit(gitea_url)
if parsed_url.scheme not in {"http", "https"} or not parsed_url.netloc:
raise RuntimeError(f"invalid Gitea URL: {gitea_url}")
safe_token = quote(token, safe="")
base_url = f"{parsed_url.scheme}://x-access-token:{safe_token}@{parsed_url.netloc}"
for repo in repos:
dest = workdir / repo
clone_url = f"{base_url}/molecule-ai/{repo}.git"
result = subprocess.run(
["git", "clone", "--depth", "1", clone_url, str(dest)],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
if result.returncode != 0:
stderr = result.stderr.replace(token, "<redacted>").replace(safe_token, "<redacted>")
raise RuntimeError(f"failed to clone {repo}: {stderr.strip()}")
paths[repo] = dest
return paths
def consumer_paths_from_root(root: Path, repos: tuple[str, ...]) -> dict[str, Path]:
paths: dict[str, Path] = {}
missing: list[str] = []
for repo in repos:
path = root / repo
if path.is_dir():
paths[repo] = path
else:
missing.append(repo)
if missing:
raise RuntimeError(f"missing consumer checkout(s) under {root}: {', '.join(missing)}")
return paths
def format_findings(findings: list[DriftFinding]) -> str:
lines = ["Runtime SSOT drift detected:"]
for finding in findings:
lines.append(f"- {finding.repo}:{finding.path} - {finding.reason}")
return "\n".join(lines)
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--root",
type=Path,
help="Directory containing checked-out consumer repos; skips cloning when set.",
)
parser.add_argument(
"--repo",
action="append",
dest="repos",
help="Consumer repo to check. May be repeated. Defaults to all canonical consumers.",
)
parser.add_argument(
"--gitea-url",
default=os.environ.get("GITEA_URL", "https://git.moleculesai.app"),
help="Gitea base URL used for cloning when --root is omitted.",
)
parser.add_argument(
"--token-env",
default="GITEA_TOKEN",
help="Environment variable containing a read token for cloning.",
)
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
args = parse_args(argv or sys.argv[1:])
repos = tuple(args.repos or DEFAULT_CONSUMERS)
tempdir: Path | None = None
try:
if args.root:
paths = consumer_paths_from_root(args.root, repos)
else:
tempdir = Path(tempfile.mkdtemp(prefix="runtime-consumer-drift-"))
paths = clone_consumers(
tempdir,
repos,
gitea_url=args.gitea_url,
token=os.environ.get(args.token_env, ""),
)
findings: list[DriftFinding] = []
for repo, path in paths.items():
findings.extend(find_runtime_drift(repo, path))
if findings:
print(format_findings(findings), file=sys.stderr)
return 1
print(f"Runtime SSOT drift guard passed for {len(paths)} consumer repo(s).")
return 0
except RuntimeError as exc:
print(f"error: {exc}", file=sys.stderr)
return 2
finally:
if tempdir:
shutil.rmtree(tempdir, ignore_errors=True)
if __name__ == "__main__":
raise SystemExit(main())