From 61b31e4ebf5ccc9e5a861e3645e4143a36e79c6b Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 15 May 2026 14:46:21 +0200 Subject: [PATCH] Add container smoke acceptance --- .custodian-brief.md | 7 ++- Containerfile | 1 + docs/CONTAINER.md | 29 +++++++++++ scripts/container_smoke.sh | 52 +++++++++++++++++++ src/guide_board/schema.py | 17 +++++- ...-WP-0002-assessment-operations-baseline.md | 10 +++- 6 files changed, 109 insertions(+), 7 deletions(-) create mode 100755 scripts/container_smoke.sh diff --git a/.custodian-brief.md b/.custodian-brief.md index 28cce0b..0e61474 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -1,17 +1,16 @@ # Custodian Brief — guide-board -**Domain:** markitect -**Last synced:** 2026-05-15 12:41 UTC +**Domain:** markitect +**Last synced:** 2026-05-15 12:43 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams ### Assessment Operations Baseline -Progress: 4/6 done | workstream_id: `fc5b1573-91b2-4a19-b6a9-dd4d17057d9b` +Progress: 5/6 done | workstream_id: `fc5b1573-91b2-4a19-b6a9-dd4d17057d9b` **Open tasks:** -- ► D2.5 - Container Smoke Acceptance `9e2e7fa7` - · D2.6 - External Extension Acceptance Path `65fbf1df` --- diff --git a/Containerfile b/Containerfile index 3bc351a..1b25a20 100644 --- a/Containerfile +++ b/Containerfile @@ -4,6 +4,7 @@ LABEL org.opencontainers.image.title="guide-board-core" LABEL org.opencontainers.image.description="Guide Board certification and compliance preparation CLI core." ENV PYTHONUNBUFFERED=1 +ENV GUIDE_BOARD_SCHEMA_DIR=/opt/guide-board/docs/schemas WORKDIR /opt/guide-board diff --git a/docs/CONTAINER.md b/docs/CONTAINER.md index 75679b1..3d5ecc7 100644 --- a/docs/CONTAINER.md +++ b/docs/CONTAINER.md @@ -48,6 +48,35 @@ podman run --rm \ The run output remains on the host under `runs/sample-noop`. +## Smoke Check + +Run the reproducible smoke check from the repository root: + +```sh +scripts/container_smoke.sh +``` + +The script: + +- detects `podman` or `docker`, +- builds `guide-board-core:smoke`, +- mounts a host output directory at `/runs`, +- runs the bundled sample assessment, +- verifies that the expected run artifacts are present on the host. + +Override the runtime, image name, or output directory when needed: + +```sh +CONTAINER_RUNTIME=docker \ +GUIDE_BOARD_SMOKE_IMAGE=guide-board-core:local \ +GUIDE_BOARD_SMOKE_RUNS_DIR=/tmp/guide-board-container-smoke \ +scripts/container_smoke.sh +``` + +The smoke check uses only bundled sample profiles and the dependency-light core +image. Restricted harness material and extension-specific runtime dependencies +must stay in extension-specific images or explicit mounts. + ## External Profiles Mount project-specific profiles read-only: diff --git a/scripts/container_smoke.sh b/scripts/container_smoke.sh new file mode 100755 index 0000000..f57526d --- /dev/null +++ b/scripts/container_smoke.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +set -eu + +ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)" +IMAGE="${GUIDE_BOARD_SMOKE_IMAGE:-guide-board-core:smoke}" +RUNS_DIR="${GUIDE_BOARD_SMOKE_RUNS_DIR:-${TMPDIR:-/tmp}/guide-board-container-smoke-$$}" +RUNTIME="${CONTAINER_RUNTIME:-}" + +if [ -z "$RUNTIME" ]; then + if command -v podman >/dev/null 2>&1; then + RUNTIME=podman + elif command -v docker >/dev/null 2>&1; then + RUNTIME=docker + else + echo "ERROR: podman or docker is required for the container smoke check." >&2 + exit 127 + fi +fi + +mkdir -p "$RUNS_DIR" + +echo "==> Building $IMAGE with $RUNTIME" +"$RUNTIME" build -t "$IMAGE" -f "$ROOT_DIR/Containerfile" "$ROOT_DIR" + +echo "==> Running bundled sample assessment" +"$RUNTIME" run --rm \ + -v "$RUNS_DIR:/runs" \ + "$IMAGE" \ + --root /opt/guide-board run \ + --target /opt/guide-board/profiles/targets/sample-repository.json \ + --assessment /opt/guide-board/profiles/assessments/sample-noop.json \ + --output-dir /runs/sample-noop + +echo "==> Verifying mounted run artifacts" +for path in \ + "$RUNS_DIR/sample-noop/run.json" \ + "$RUNS_DIR/sample-noop/plan.json" \ + "$RUNS_DIR/sample-noop/retention-summary.json" \ + "$RUNS_DIR/sample-noop/normalized/evidence.json" \ + "$RUNS_DIR/sample-noop/normalized/findings.json" \ + "$RUNS_DIR/sample-noop/normalized/mappings.json" \ + "$RUNS_DIR/sample-noop/reports/assessment-package.json" \ + "$RUNS_DIR/sample-noop/reports/report.md" +do + if [ ! -f "$path" ]; then + echo "ERROR: expected artifact missing: $path" >&2 + exit 1 + fi +done + +echo "Container smoke check passed." +echo "Run artifacts: $RUNS_DIR/sample-noop" diff --git a/src/guide_board/schema.py b/src/guide_board/schema.py index 8362e4b..e5dcf90 100644 --- a/src/guide_board/schema.py +++ b/src/guide_board/schema.py @@ -7,6 +7,7 @@ project's own draft contracts. from __future__ import annotations +import os from pathlib import Path from typing import Any @@ -14,11 +15,23 @@ from guide_board.errors import ValidationError from guide_board.io import load_json -SCHEMA_DIR = Path(__file__).resolve().parents[2] / "docs" / "schemas" +SOURCE_SCHEMA_DIR = Path(__file__).resolve().parents[2] / "docs" / "schemas" def load_schema(schema_name: str) -> dict[str, Any]: - return load_json(SCHEMA_DIR / f"{schema_name}.schema.json") + return load_json(_schema_dir() / f"{schema_name}.schema.json") + + +def _schema_dir() -> Path: + env_dir = os.environ.get("GUIDE_BOARD_SCHEMA_DIR") + candidates = [] + if env_dir: + candidates.append(Path(env_dir).expanduser()) + candidates.extend([SOURCE_SCHEMA_DIR, Path.cwd() / "docs" / "schemas"]) + for candidate in candidates: + if (candidate / "target-profile.schema.json").is_file(): + return candidate + return candidates[0] def validate_document(document: Any, schema: dict[str, Any], path: str = "$") -> list[str]: diff --git a/workplans/GUIDE-BOARD-WP-0002-assessment-operations-baseline.md b/workplans/GUIDE-BOARD-WP-0002-assessment-operations-baseline.md index 2329ce8..170a497 100644 --- a/workplans/GUIDE-BOARD-WP-0002-assessment-operations-baseline.md +++ b/workplans/GUIDE-BOARD-WP-0002-assessment-operations-baseline.md @@ -150,7 +150,7 @@ Progress: ```task id: GUIDE-BOARD-WP-0002-T005 -status: todo +status: done priority: medium state_hub_task_id: "9e2e7fa7-8a97-4f07-9925-e637e0366003" ``` @@ -162,6 +162,14 @@ Acceptance: - Verify mounted output directories receive run artifacts. - Keep restricted or extension-specific harness assets outside the core image. +Progress: + +- Added `scripts/container_smoke.sh`. +- Documented the smoke check in `docs/CONTAINER.md`. +- Verified the smoke check with Docker; mounted outputs received run metadata, + normalized evidence, mappings, findings, assessment package, and Markdown + report. + ## D2.6 - External Extension Acceptance Path ```task