Add Jeannie change impact analysis

This commit is contained in:
juvdiaz 2026-06-29 20:44:49 -06:00
parent 791c9d5822
commit 31723307d5
6 changed files with 143 additions and 0 deletions

View File

@ -274,6 +274,7 @@ Run a read-only health snapshot from the Debian server with:
./jeannie capacity ./jeannie capacity
./jeannie recover-plan ./jeannie recover-plan
./jeannie recover-power --dry-run ./jeannie recover-power --dry-run
./jeannie impact --since HEAD~1
./jeannie scorecard ./jeannie scorecard
./jeannie explain scorecard ./jeannie explain scorecard
./jeannie gitops-status ./jeannie gitops-status
@ -330,6 +331,10 @@ edge verification.
Debian runtime, Gitea, RPi DNS, Pimox workers, Kubernetes, GitOps/apps, and Debian runtime, Gitea, RPi DNS, Pimox workers, Kubernetes, GitOps/apps, and
edge/public checks. Use `recover-power --dry-run` to preview the sequence. edge/public checks. Use `recover-power --dry-run` to preview the sequence.
`impact` explains what a change may touch before you apply it. Use
`./jeannie impact --since HEAD~1` for the last commit or pass paths
like `bootstrap/edge` and `apps/website`.
`scorecard` rolls up backup readiness, GitOps health, DNS/RPi health, cluster `scorecard` rolls up backup readiness, GitOps health, DNS/RPi health, cluster
health, edge health, capacity pressure, security posture, public certificates, health, edge health, capacity pressure, security posture, public certificates,
inventory, and docs freshness into a compact pass/warn/fail report. inventory, and docs freshness into a compact pass/warn/fail report.

View File

@ -274,6 +274,7 @@ Run a read-only health snapshot from the Debian server with:
./{{ main_script }} capacity ./{{ main_script }} capacity
./{{ main_script }} recover-plan ./{{ main_script }} recover-plan
./{{ main_script }} recover-power --dry-run ./{{ main_script }} recover-power --dry-run
./{{ main_script }} impact --since HEAD~1
./{{ main_script }} scorecard ./{{ main_script }} scorecard
./{{ main_script }} explain scorecard ./{{ main_script }} explain scorecard
./{{ main_script }} gitops-status ./{{ main_script }} gitops-status
@ -330,6 +331,10 @@ edge verification.
Debian runtime, Gitea, RPi DNS, Pimox workers, Kubernetes, GitOps/apps, and Debian runtime, Gitea, RPi DNS, Pimox workers, Kubernetes, GitOps/apps, and
edge/public checks. Use `recover-power --dry-run` to preview the sequence. edge/public checks. Use `recover-power --dry-run` to preview the sequence.
`impact` explains what a change may touch before you apply it. Use
`./{{ main_script }} impact --since HEAD~1` for the last commit or pass paths
like `bootstrap/edge` and `apps/website`.
`scorecard` rolls up backup readiness, GitOps health, DNS/RPi health, cluster `scorecard` rolls up backup readiness, GitOps health, DNS/RPi health, cluster
health, edge health, capacity pressure, security posture, public certificates, health, edge health, capacity pressure, security posture, public certificates,
inventory, and docs freshness into a compact pass/warn/fail report. inventory, and docs freshness into a compact pass/warn/fail report.

View File

@ -104,6 +104,10 @@ and DuckDNS-to-OCI edge IP drift from the canonical inventory.
state, workload images, public URL status, and latest Gitea/OpenTofu backup state, workload images, public URL status, and latest Gitea/OpenTofu backup
pointers. pointers.
`impact [--since REF] [PATH...]`
: Explain which homelab areas are affected by changed files or explicit paths,
including risk, touched services, validation commands, and apply path.
`backup-status` `backup-status`
: Report freshness for Gitea backups, OpenTofu state backups, restore drill : Report freshness for Gitea backups, OpenTofu state backups, restore drill
reports, and repo-managed Pi-hole restore inputs. The main `status` cascade reports, and repo-managed Pi-hole restore inputs. The main `status` cascade

View File

@ -0,0 +1,17 @@
pattern area risk touches validate apply
bootstrap/edge/ Edge/high public routing high OCI edge nginx;public website route;Gitea public route;Tailscale backend path ./jeannie doctor-edge;./jeannie cert-check;curl -I https://lab2025.duckdns.org/ ./jeannie plan edge
bootstrap/cluster/ Kubernetes cluster high kubeadm control plane;worker joins;CNI;cluster state ./jeannie doctor-cluster;./jeannie status ./jeannie plan cluster
bootstrap/platform/ Platform services medium Argo CD;monitoring;registry;Traefik dependencies ./jeannie status;./jeannie gitops-status;./jeannie grafana-dashboards list ./jeannie plan platform
bootstrap/provisioning/ Pimox provisioning high Pimox template;worker VMs;preseed/PXE ./jeannie preflight;./jeannie doctor-cluster ./jeannie plan provisioning
apps/ Applications medium GitOps apps;website;security lab;dashboards ./jeannie gitops-status;./jeannie status ./jeannie apps
apps/website/ Public website medium website deployment;public route;image build;Redis/Ollama dependencies ./jeannie status;./jeannie cert-check ./jeannie apps
infra/rpi-services/ RPi DNS services medium Pi-hole;Unbound;Uptime Kuma;RPi Docker ./jeannie doctor-rpi;./jeannie status ./jeannie rpi-services
infra/gitea/ Gitea medium Gitea Docker stack;repo bootstrap;public /git route ./jeannie doctor-gitea;./jeannie doctor-edge ./jeannie deploy-gitea
infra/arr-stack/ ARR stack medium Debian Docker ARR services;volumes under /data/arr ./jeannie status manual docker compose in infra/arr-stack
infra/tailscale/ Tailscale policy medium Tailnet ACLs;routes;peer access ./jeannie tailnet-policy-check;./jeannie doctor-edge manual tailnet policy apply
infra/resource-budgets.yml Capacity policy low resource budget reports;capacity warnings ./jeannie resource-budget;./jeannie capacity none
homelab.yml Canonical inventory high all rendered docs;scripts;edge/Gitea/RPi/Pimox IPs ./jeannie inventory-check;./jeannie validate;./jeannie preflight none
jeannie Main operator script high all Jeannie commands and pipeline flow bash -n jeannie;shellcheck jeannie;./jeannie help none
scripts/ Jeannie helper scripts medium reporting;doctors;automation helpers bash -n scripts/*;shellcheck scripts/* none
README.md.tmpl Generated docs low README.md;operator documentation scripts/render-docs --check scripts/render-docs
docs/ Documentation low runbooks;manual pages scripts/render-docs --check none
1 pattern area risk touches validate apply
2 bootstrap/edge/ Edge/high public routing high OCI edge nginx;public website route;Gitea public route;Tailscale backend path ./jeannie doctor-edge;./jeannie cert-check;curl -I https://lab2025.duckdns.org/ ./jeannie plan edge
3 bootstrap/cluster/ Kubernetes cluster high kubeadm control plane;worker joins;CNI;cluster state ./jeannie doctor-cluster;./jeannie status ./jeannie plan cluster
4 bootstrap/platform/ Platform services medium Argo CD;monitoring;registry;Traefik dependencies ./jeannie status;./jeannie gitops-status;./jeannie grafana-dashboards list ./jeannie plan platform
5 bootstrap/provisioning/ Pimox provisioning high Pimox template;worker VMs;preseed/PXE ./jeannie preflight;./jeannie doctor-cluster ./jeannie plan provisioning
6 apps/ Applications medium GitOps apps;website;security lab;dashboards ./jeannie gitops-status;./jeannie status ./jeannie apps
7 apps/website/ Public website medium website deployment;public route;image build;Redis/Ollama dependencies ./jeannie status;./jeannie cert-check ./jeannie apps
8 infra/rpi-services/ RPi DNS services medium Pi-hole;Unbound;Uptime Kuma;RPi Docker ./jeannie doctor-rpi;./jeannie status ./jeannie rpi-services
9 infra/gitea/ Gitea medium Gitea Docker stack;repo bootstrap;public /git route ./jeannie doctor-gitea;./jeannie doctor-edge ./jeannie deploy-gitea
10 infra/arr-stack/ ARR stack medium Debian Docker ARR services;volumes under /data/arr ./jeannie status manual docker compose in infra/arr-stack
11 infra/tailscale/ Tailscale policy medium Tailnet ACLs;routes;peer access ./jeannie tailnet-policy-check;./jeannie doctor-edge manual tailnet policy apply
12 infra/resource-budgets.yml Capacity policy low resource budget reports;capacity warnings ./jeannie resource-budget;./jeannie capacity none
13 homelab.yml Canonical inventory high all rendered docs;scripts;edge/Gitea/RPi/Pimox IPs ./jeannie inventory-check;./jeannie validate;./jeannie preflight none
14 jeannie Main operator script high all Jeannie commands and pipeline flow bash -n jeannie;shellcheck jeannie;./jeannie help none
15 scripts/ Jeannie helper scripts medium reporting;doctors;automation helpers bash -n scripts/*;shellcheck scripts/* none
16 README.md.tmpl Generated docs low README.md;operator documentation scripts/render-docs --check scripts/render-docs
17 docs/ Documentation low runbooks;manual pages scripts/render-docs --check none

View File

@ -5871,6 +5871,10 @@ ask_homelab() {
"${REPO_ROOT}/scripts/ask" "${@:2}" "${REPO_ROOT}/scripts/ask" "${@:2}"
} }
impact() {
"${REPO_ROOT}/scripts/impact" "${@:2}"
}
security_scan() { security_scan() {
require_debian_server "security-scan" require_debian_server "security-scan"
"${REPO_ROOT}/scripts/security-scan" all "${REPO_ROOT}/scripts/security-scan" all
@ -6125,6 +6129,7 @@ Operator View And Reports
capacity Compact capacity and placement report. capacity Compact capacity and placement report.
recover-plan Print disaster recovery order and prerequisites. recover-plan Print disaster recovery order and prerequisites.
recover-power [--dry-run] Run or preview post-outage recovery. recover-power [--dry-run] Run or preview post-outage recovery.
impact [--since REF] [PATH...] Explain affected lab areas before changes.
map [--dot] Print the homelab dependency map. map [--dot] Print the homelab dependency map.
change-journal {list|path} Show risky-command journal entries. change-journal {list|path} Show risky-command journal entries.
release-snapshot Write a pre-change release snapshot. release-snapshot Write a pre-change release snapshot.
@ -6389,6 +6394,9 @@ case "${1:-}" in
ask) ask)
ask_homelab "$@" ask_homelab "$@"
;; ;;
impact)
impact "$@"
;;
security-scan) security-scan)
security_scan security_scan
;; ;;

104
scripts/impact Executable file
View File

@ -0,0 +1,104 @@
#!/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