Files
sar/.claude/skills/bmad-eval-runner/scripts/docker_setup.py
julian 17c08e6392 chore: initial monorepo scaffold + WDS Phase 1+2 artifacts
- Nx 22.7 monorepo (pnpm 11.1, TypeScript 5.9, Node 24)
- apps/api: NestJS 11 (CJS conforme CODING-RULES.md PGD-DB-004)
- apps/web: React 19 + Vite 8 (ESM)
- libs/shared/api-interface: Zod contract base
- Docker Compose dev: Postgres 18, Valkey 8, MinIO, Mailpit
- WDS artifacts:
  - design-artifacts/A-Product-Brief/ (5 docs canônicos + 16 dialogs)
  - design-artifacts/B-Trigger-Map/ (hub + 4 personas + feature impact)
- Stack canon: STACK.md v2.2 + CODING-RULES.md v2.0 + brand.md
- AGENTS.md + README.md como entrada para devs/agentes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:34:20 +00:00

116 lines
3.6 KiB
Python

#!/usr/bin/env python3
# /// script
# requires-python = ">=3.9"
# ///
"""Detect Docker and build the bmad-eval-runner image when needed.
Usage:
python3 docker_setup.py --check # exit 0 if image is ready, 1 otherwise
python3 docker_setup.py --build # build the image (no-op if present)
python3 docker_setup.py --rebuild # force rebuild
"""
from __future__ import annotations
import argparse
import json
import shutil
import subprocess
import sys
from pathlib import Path
IMAGE_TAG = "bmad-eval-runner:latest"
SCRIPT_DIR = Path(__file__).resolve().parent
DOCKERFILE = SCRIPT_DIR.parent / "assets" / "Dockerfile"
def docker_available() -> tuple[bool, str]:
if shutil.which("docker") is None:
return False, "docker CLI not found on PATH"
try:
result = subprocess.run(
["docker", "info"],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode != 0:
return False, f"`docker info` failed: {result.stderr.strip().splitlines()[-1] if result.stderr.strip() else 'unknown'}"
return True, "ok"
except subprocess.TimeoutExpired:
return False, "`docker info` timed out"
except Exception as e:
return False, f"docker check error: {e}"
def image_present(tag: str = IMAGE_TAG) -> bool:
try:
result = subprocess.run(
["docker", "image", "inspect", tag],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=10,
)
return result.returncode == 0
except Exception:
return False
def build_image(tag: str = IMAGE_TAG, force: bool = False, verbose: bool = True) -> int:
if not DOCKERFILE.is_file():
print(f"Dockerfile missing at {DOCKERFILE}", file=sys.stderr)
return 2
cmd = ["docker", "build", "-t", tag, "-f", str(DOCKERFILE), str(DOCKERFILE.parent)]
if force:
cmd.insert(2, "--no-cache")
if verbose:
print(f"Building {tag} from {DOCKERFILE} ...", file=sys.stderr)
proc = subprocess.run(cmd, stdout=sys.stderr if verbose else subprocess.DEVNULL, stderr=sys.stderr)
return proc.returncode
def main() -> int:
parser = argparse.ArgumentParser(description="Manage the bmad-eval-runner Docker image")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--check", action="store_true", help="Report status as JSON; exit 0 if image is ready")
group.add_argument("--build", action="store_true", help="Build the image (no-op if already present)")
group.add_argument("--rebuild", action="store_true", help="Force rebuild")
parser.add_argument("--quiet", action="store_true")
args = parser.parse_args()
available, reason = docker_available()
present = image_present() if available else False
if args.check:
print(json.dumps({
"docker_available": available,
"docker_reason": reason,
"image_present": present,
"image_tag": IMAGE_TAG,
}, indent=2))
return 0 if (available and present) else 1
if not available:
print(f"Docker is not available: {reason}", file=sys.stderr)
return 3
if args.rebuild:
return build_image(force=True, verbose=not args.quiet)
if args.build:
if present:
if not args.quiet:
print(f"{IMAGE_TAG} already present; skipping build (use --rebuild to force).", file=sys.stderr)
return 0
return build_image(force=False, verbose=not args.quiet)
return 0
if __name__ == "__main__":
sys.exit(main())