130 lines
4.1 KiB
Python
130 lines
4.1 KiB
Python
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
import re
|
|
import sys
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
LOCAL_DEMO_DEPLOY_DIR = "/root/deploy/ai-ict"
|
|
LOCAL_DEMO_COMPOSE_PROJECT = "ai-ict"
|
|
LOCAL_DEMO_CONTAINER = "ai-ict"
|
|
LOCAL_DEMO_COMPOSE_FILE = f"{LOCAL_DEMO_DEPLOY_DIR}/docker-compose.yml"
|
|
LOCAL_DEMO_COMPOSE_PREFIX = (
|
|
f"docker compose -p {LOCAL_DEMO_COMPOSE_PROJECT} -f {LOCAL_DEMO_COMPOSE_FILE}"
|
|
)
|
|
LOCAL_DEMO_DOCS = (
|
|
ROOT / "README.md",
|
|
ROOT / "RUNBOOK.md",
|
|
)
|
|
LOCAL_DEMO_REBUILD_COMMANDS = (
|
|
f"{LOCAL_DEMO_COMPOSE_PREFIX} down --remove-orphans",
|
|
f"{LOCAL_DEMO_COMPOSE_PREFIX} up -d --build",
|
|
)
|
|
LOCAL_DEMO_SMOKE_COMMANDS = (
|
|
f"{LOCAL_DEMO_COMPOSE_PREFIX} ps",
|
|
"test \"$(docker ps --filter label=com.docker.compose.project=ai-ict --format '{{.Names}}' | wc -l | tr -d ' ')\" = \"1\"",
|
|
"test \"$(docker ps --filter name=^/ai-ict$ --format '{{.Names}}' | wc -l | tr -d ' ')\" = \"1\"",
|
|
"test \"$(docker inspect -f '{{.State.Health.Status}}' ai-ict)\" = \"healthy\"",
|
|
)
|
|
LOCAL_DEMO_REQUIRED_SNIPPETS = (
|
|
"single-set local Docker deployment",
|
|
f"deploy directory `{LOCAL_DEMO_DEPLOY_DIR}`",
|
|
f"compose project `{LOCAL_DEMO_COMPOSE_PROJECT}`",
|
|
f"container `{LOCAL_DEMO_CONTAINER}`",
|
|
"exactly one active deployment set",
|
|
f"one active `{LOCAL_DEMO_CONTAINER}` container",
|
|
"do not create a second local stack",
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class LocalDemoReadinessReport:
|
|
checked_docs: tuple[Path, ...]
|
|
command_lines_by_doc: tuple[tuple[Path, tuple[str, ...]], ...]
|
|
errors: tuple[str, ...]
|
|
|
|
|
|
def check_local_demo_readiness(
|
|
docs: tuple[Path, ...] = LOCAL_DEMO_DOCS,
|
|
) -> LocalDemoReadinessReport:
|
|
expected_commands = LOCAL_DEMO_REBUILD_COMMANDS + LOCAL_DEMO_SMOKE_COMMANDS
|
|
command_lines_by_doc = []
|
|
errors = []
|
|
|
|
for path in docs:
|
|
if not path.is_file():
|
|
command_lines_by_doc.append((path, ()))
|
|
errors.append(f"{_display_path(path)} is missing")
|
|
continue
|
|
|
|
text = path.read_text(encoding="utf-8")
|
|
try:
|
|
section = _local_docker_deployment_section(text)
|
|
except ValueError as exc:
|
|
command_lines_by_doc.append((path, ()))
|
|
errors.append(f"{_display_path(path)} {exc}")
|
|
continue
|
|
|
|
command_lines = _local_docker_command_lines(section)
|
|
command_lines_by_doc.append((path, command_lines))
|
|
|
|
for snippet in LOCAL_DEMO_REQUIRED_SNIPPETS:
|
|
if snippet not in section:
|
|
errors.append(f"{_display_path(path)} missing local demo snippet: {snippet}")
|
|
if command_lines != expected_commands:
|
|
errors.append(
|
|
f"{_display_path(path)} local demo commands drifted from the locked single-set shape"
|
|
)
|
|
|
|
return LocalDemoReadinessReport(
|
|
checked_docs=tuple(docs),
|
|
command_lines_by_doc=tuple(command_lines_by_doc),
|
|
errors=tuple(errors),
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
report = check_local_demo_readiness()
|
|
if report.errors:
|
|
print("Local demo readiness check failed")
|
|
for error in report.errors:
|
|
print(f"- {error}")
|
|
return 1
|
|
|
|
command_count = len(LOCAL_DEMO_REBUILD_COMMANDS) + len(LOCAL_DEMO_SMOKE_COMMANDS)
|
|
print(
|
|
"Local demo readiness check passed: "
|
|
f"{len(report.checked_docs)} docs, {command_count} locked commands."
|
|
)
|
|
return 0
|
|
|
|
|
|
def _local_docker_deployment_section(text: str) -> str:
|
|
match = re.search(
|
|
r"^## Local Docker [Dd]eployment\n(?P<section>.*?)(?:\n## |\Z)",
|
|
text,
|
|
re.DOTALL | re.MULTILINE,
|
|
)
|
|
if match is None:
|
|
raise ValueError("is missing the Local Docker deployment section")
|
|
return match.group("section")
|
|
|
|
|
|
def _local_docker_command_lines(text: str) -> tuple[str, ...]:
|
|
return tuple(
|
|
line.strip()
|
|
for line in text.splitlines()
|
|
if line.strip().startswith(("docker ", "test "))
|
|
)
|
|
|
|
|
|
def _display_path(path: Path) -> str:
|
|
try:
|
|
return str(path.relative_to(ROOT))
|
|
except ValueError:
|
|
return str(path)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|