Files
home-network-k3s-impl/ci/touched-requirements.sh
T
jakman85andClaude Opus 4.8 8c1368977e Phase 0 clarification follow-ups + reconcile CI integration seams
Adopt the spec's Phase-0 clarifications (spec now at pin a7fc19f) into the impl:
- schema v1.1: enforce exposure:public => auth.mode!=none (positive+negative tested).
- Kyverno: add longhorn=>size and longhorn=>NOT-reacquirable invariants (R-STATE-02/03).
- Ratify the homelab.keithjackson.dev/* label-key convention (now in spec docs/03).
- R-TEST-06 spec-immutability gate: ci/SPEC_PIN + ci/spec-immutability.sh + gate.sh
  stage + Makefile target + a Gitea job (checks spec repo clean & at pin).
- touched-requirements.sh: conformance tags are the authoritative source.

Reconcile 3 CI seams the CI worker flagged (gate was RED, now GREEN):
- create schemas/validate.sh (SCHEMA_VALIDATE_CMD entrypoint) — L0 schema gate runs.
- fix l1.sh/conformance.sh to run.sh's real interface (fixed _artifacts/ paths; no
  --junit/--coverage) and collect artifacts.
- exec bits on new scripts.

make -C ci gate => PASS (l0 skips locally w/o kyverno binary; green in CI).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 00:42:43 -05:00

87 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# ci/touched-requirements.sh — compute the requirement subset a change "touches".
#
# R-TEST-03 gates a merge on the conformance subset tagged with the requirement
# IDs the change touches. This script derives that subset from the diff so the
# conformance runner can be filtered to it (conformance.sh --requirements ...).
#
# Output (stdout): newline-separated, sorted, unique R-* IDs. Nothing else goes
# to stdout, so callers can `mapfile`/`$(...)` it directly.
#
# SOURCE OF TRUTH (reconciled, PM 2026-08-16): the conformance requirement TAGS are
# authoritative. Each Chainsaw test declares the requirements it verifies via
# `conformance.home.k3s/<R-ID>` labels; the coverage report (conformance/_artifacts/
# coverage/coverage.json) is generated from those tags. This script's job is only to
# select which tagged subset a change TOUCHES, then hand those IDs to
# `conformance.sh --requirements ...` which runs exactly the tests carrying those tags.
#
# Detection = union of:
# 1. R-* tokens literally present in the diff of changed files — including the
# authoritative `conformance.home.k3s/R-*` tags on changed tests, and the R-IDs
# that policies/ServiceDefinitions self-declare (docs/14 + ADR-0011).
# 2. A coarse path->requirement fallback for changes that don't self-declare IDs.
# The coarse map (2) NEVER overrides an explicit tag; it only widens coverage for
# untagged changes so nothing silently escapes the gate.
set -euo pipefail
CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "${CI_DIR}/lib.sh"
# Base ref to diff against. In Gitea/GitHub Actions PR runs this is the target
# branch (main). Locally, default to main; override with BASE_REF.
: "${BASE_REF:=}"
resolve_base() {
if [ -n "$BASE_REF" ]; then echo "$BASE_REF"; return; fi
if [ -n "${GITHUB_BASE_REF:-}" ]; then echo "origin/${GITHUB_BASE_REF}"; return; fi
if git -C "$REPO_ROOT" rev-parse --verify -q origin/main >/dev/null; then echo "origin/main"; return; fi
if git -C "$REPO_ROOT" rev-parse --verify -q main >/dev/null; then echo "main"; return; fi
echo "HEAD~1" # last resort
}
changed_files() {
local base; base="$(resolve_base)"
# Three-dot: changes on this branch since it diverged from base.
if git -C "$REPO_ROOT" diff --name-only "${base}...HEAD" 2>/dev/null; then return; fi
git -C "$REPO_ROOT" diff --name-only "${base}" 2>/dev/null || true
}
# Coarse path -> requirement map. Deliberately conservative; extend as subtrees
# fill in. Left side is a path prefix (relative to repo root).
path_requirements() {
case "$1" in
catalog/*) echo "R-GITOPS-02" ;;
schemas/*) echo "R-GITOPS-02" ;;
platform/kyverno/*) echo "R-TEST-01 R-NET-01 R-SEC-01" ;;
platform/cilium/*) echo "R-NET-01 R-NET-02 R-NET-05" ;;
platform/longhorn/*) echo "R-STATE-01 R-STATE-02" ;;
platform/cert-manager/*) echo "R-TLS-01 R-TLS-02" ;;
platform/externaldns/*) echo "R-DNS-02" ;;
platform/velero/*) echo "R-STATE-03" ;;
platform/monitoring/*) echo "R-OBS-01 R-OBS-02" ;;
platform/image-automation/*) echo "R-UPD-01 R-UPD-02" ;;
conformance/*) echo "R-TEST-01 R-TEST-02 R-TEST-03 R-TEST-04" ;;
clusters/*) echo "R-GITOPS-01 R-ENV-02" ;;
.gitea/*|ci/*) echo "R-GITOPS-01 R-TEST-02 R-TEST-03" ;;
*) : ;;
esac
}
main() {
local files reqs=""
files="$(changed_files)"
[ -n "$files" ] || { log "no changed files vs base; empty touched-requirement set"; exit 0; }
while IFS= read -r f; do
[ -n "$f" ] || continue
reqs+=" $(path_requirements "$f")"
# Scan file contents for self-declared R-IDs (only if it still exists).
if [ -f "${REPO_ROOT}/${f}" ]; then
reqs+=" $(grep -hoE 'R-[A-Z]+-[0-9]+' "${REPO_ROOT}/${f}" 2>/dev/null | tr '\n' ' ' || true)"
fi
done <<< "$files"
# Normalize: split, unique, sort. Emit to stdout.
printf '%s\n' $reqs | sed '/^$/d' | sort -u
}
main "$@"