my-homelab-configs/scripts/impact

105 lines
2.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
RULES_FILE="${HOMELAB_IMPACT_RULES:-${REPO_ROOT}/infra/jeannie-impact/rules.tsv}"
usage() {
cat <<'EOF'
Usage: ./jeannie impact [--since REF] [PATH...]
Explain which homelab areas are touched by changed files or explicit paths.
Default mode analyzes the current working tree diff.
EOF
}
since=""
paths=()
while [ "$#" -gt 0 ]; do
case "$1" in
--since)
since="${2:-}"
shift 2
;;
-h|--help|help)
usage
exit 0
;;
*)
paths+=("$1")
shift
;;
esac
done
if [ ! -s "$RULES_FILE" ]; then
echo "Missing impact rules: $RULES_FILE" >&2
exit 1
fi
if [ "${#paths[@]}" -eq 0 ]; then
if [ -n "$since" ]; then
mapfile -t paths < <(git -C "$REPO_ROOT" diff --name-only "$since"...HEAD)
else
mapfile -t paths < <(git -C "$REPO_ROOT" status --short | awk '{print $NF}')
fi
fi
if [ "${#paths[@]}" -eq 0 ]; then
echo "No changed files or paths to analyze."
exit 0
fi
python3 - "$RULES_FILE" "${paths[@]}" <<'PY'
import csv
import sys
rules_file = sys.argv[1]
paths = [path.strip() for path in sys.argv[2:] if path.strip()]
with open(rules_file, encoding="utf-8", newline="") as handle:
rules = list(csv.DictReader(handle, delimiter="\t"))
matches = []
for path in paths:
normalized = path.removeprefix("./")
for rule in rules:
pattern = rule["pattern"]
if normalized == pattern.rstrip("/") or normalized.startswith(pattern):
matches.append((normalized, rule))
print("Jeannie Impact")
print("==============")
print("Files:")
for path in paths:
print(f" {path}")
if not matches:
print("\nNo specific impact rule matched.")
print("Validate:")
print(" ./jeannie validate")
raise SystemExit(0)
seen = set()
print("\nImpacted Areas")
for path, rule in matches:
key = (rule["area"], rule["risk"])
if key in seen:
continue
seen.add(key)
print(f"\n- {rule['area']}")
print(f" risk: {rule['risk']}")
print(" touches:")
for item in rule["touches"].split(";"):
print(f" - {item}")
print(" validate:")
for command in rule["validate"].split(";"):
print(f" {command}")
if rule["apply"] != "none":
print(" apply path:")
for command in rule["apply"].split(";"):
print(f" {command}")
else:
print(" apply path: none")
PY