Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
82 lines
3.6 KiB
Bash
Executable File
82 lines
3.6 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.
|
|
#
|
|
# Detection = union of two signals:
|
|
# 1. R-* tokens literally present in the diff of changed files (tests, policies,
|
|
# and ServiceDefinitions carry their R-IDs per docs/14 + ADR-0011).
|
|
# 2. A coarse path->requirement map for changes that don't self-declare IDs.
|
|
#
|
|
# ASSUMPTION (flag for PM): the authoritative change->requirement mapping is
|
|
# really WS-TEST's coverage tooling (tests tag their own R-IDs). This script is a
|
|
# CI-side approximation good enough to *filter* the subset; at integration it
|
|
# should defer to / cross-check the coverage report emitted by conformance/.
|
|
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 "$@"
|