Files
home-network-k3s-impl/ci
jakman85andClaude Opus 4.8 bb48f675ee ci: bake pinned test toolchain into devcontainer + fix conformance layer/spec-immutability seams
Bake the conformance harness toolchain into infra/devcontainer so `make -C ci gate`
runs identically here and in CI:
- Containerfile: install kyverno CLI (1.13.4), kyverno-json, chainsaw (0.2.12),
  yamllint (1.35.1), check-jsonschema (0.29.4), yq (4.44.6) via new pinned RUN block;
  add diffutils + python3-pip; drop stale LIBVIRT_DEFAULT_URI; refresh header for the
  Proxmox(bpg)+conformance toolbox; extend /etc/dev-toolchain-versions provenance.
- versions.env: add KYVERNO/KYVERNO_JSON/CHAINSAW/YAMLLINT/YQ/CHECK_JSONSCHEMA;
  align KUBECTL 1.32.0 -> 1.31.4. dev.sh build(): pass the new --build-args.
- ci/versions.env: pin CHECK_JSONSCHEMA_VERSION=0.29.4 (single source of truth).

Fix harness seams so the gate can run:
- dev.sh run(): mount the sibling spec repo (home-network-k3s) read-only at
  /home-network-k3s for ci/spec-immutability.sh (R-TEST-06); conditional so the
  container still runs without it.
- conformance/lib/common.sh conf_dirs_for_layer(): accept a comma/space list of
  layers and union the dirs, so ci/conformance.sh `run.sh --layer l2,l3` selects
  both layers in one pass (backward compatible for single layers).

NOTE: ci/versions.env pins KYVERNO_JSON_VERSION=0.0.5, which has no upstream release
(max tag is v0.0.3; platform/kyverno/scripts/run-l0.sh already defaults to v0.0.3).
Baked v0.0.3 and flagged for PM to correct ci/versions.env. kyverno-json is not used
by any static gate stage.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-25 15:43:19 -05:00
..

ci/ — portable merge gate (runnable today, without Gitea)

Task T-0.2 · R-TEST-02 (reproducible, single-command) · Owner WS-GITOPS (CI).

The same merge gate the Gitea Actions workflow runs (.gitea/workflows/), as plain scripts + a Makefile so the gates are runnable now, before any Gitea host or cluster exists. CI and a laptop call the identical scripts, so make -C ci gate == the CI merge gate.

Quick start

make -C ci gate        # the single command: run every stage + the R-TEST-03 decision
make -C ci help        # list targets
make -C ci tools       # which pinned tools are installed locally
make -C ci touched     # print the touched-requirement subset for this branch vs main

Individual stages:

make -C ci lint        # yaml/lint                          (no cluster; live now)
make -C ci l0          # ServiceDefinition schema + Kyverno  (no cluster; live now)
make -C ci l1          # L1 rendering checks                 (no cluster; live now)
make -C ci conformance # L2/L3 in-cluster Chainsaw Job       (SKIPS unless CLUSTER_AVAILABLE=true)

Exit-code contract

Every stage script uses: 0 = pass · 1 = fail (blocks merge) · 3 = SKIPPED (cluster-gated with no cluster, or an unwired seam / missing tool). A 3 is never counted as green — that is how the gate stays honest in Phase 0 (no fake green).

Reproducibility (R-TEST-02)

  • Version-pinned: all tool versions live in versions.env — the single source of truth shared by the local scripts and the CI installer. Bumping a version is a normal feature-branch → main change.
  • Single command, environment-agnostic: make -C ci gate on any machine with the pinned tools.
  • Deterministic: no cluster state is assumed; L2/L3 SKIP unless a cluster is explicitly declared.

Files

ci/
  Makefile                 targets: lint · l0 · l1 · conformance · gate · touched · tools · versions
  versions.env             PINNED tool versions (R-TEST-02) — single source of truth
  lib.sh                   shared helpers: logging, tool checks, cluster detection, artifact paths
  yamllint.yaml            lint config for the yaml/lint stage
  lint.sh                  Stage: yaml/lint
  l0.sh                    Stage: ServiceDefinition schema validation + Kyverno L0 policy gate
  l1.sh                    Stage: L1 rendering checks (Chainsaw L1 via the conformance runner)
  conformance.sh           Stage: L2/L3 in-cluster Chainsaw Job (trigger + collect)
  touched-requirements.sh  compute the R-* subset a change touches (drives the R-TEST-03 filter)
  gate.sh                  run all stages + the merge decision (the single command)
  README.md                this file

Integration seams — entrypoints owned by OTHER agents

This gate is an orchestrator. It does not implement schema validation, Kyverno policies, or the conformance runner — those are other workstreams' deliverables. Each seam is env-overridable so the owning agent / PM can reconcile the real entrypoint without editing these scripts. Defaults below are this workstream's assumptions — flagged for the PM to verify at integration.

Seam Owner / task Env override Assumed default Assumed interface
ServiceDefinition schema validator WS-GITOPS · T-0.3 (schemas/) SCHEMA_VALIDATE_CMD schemas/validate.sh validate.sh catalog/*.yaml → exit 0/1; --version supported
Kyverno L0 policies + tests WS-TEST · T-0.5 (platform/kyverno/) KYVERNO_POLICY_DIR platform/kyverno contains kyverno-test.yaml files discoverable by kyverno test <dir>
Conformance runner (Chainsaw) WS-TEST · T-0.4 (conformance/) CONFORMANCE_CMD conformance/run.sh run.sh --layer <l1|l2,l3> [--requirements R-A,R-B] [--trigger in-cluster-job] --junit <file> --coverage <dir>
Catalog (ServiceDefinitions) WS-APPS CATALOG_DIR catalog *.yaml ServiceDefinition files

Artifact locations (git-ignored): JUnit → results/junit/*.xml; coverage → reports/coverage/**. Override with JUNIT_DIR / COVERAGE_DIR if the runner writes elsewhere.

A seam that is missing or not executable reports SKIPPED (exit 3), not a false pass — so during parallel Phase 0 development the gate is green-honest, and each seam goes live automatically the moment its owner delivers a runnable entrypoint.

Cluster gating

CLUSTER_AVAILABLE=true (env locally, vars.CLUSTER_AVAILABLE in Gitea) makes the L2/L3 stage run and be required; anything else makes it SKIP (Phase 0). REQUIRE_CLUSTER_TESTS=true (Phase 1+) turns any unverified cluster-needing touched requirement into a hard block.