Add Jeannie change impact analysis
This commit is contained in:
parent
791c9d5822
commit
31723307d5
|
|
@ -274,6 +274,7 @@ Run a read-only health snapshot from the Debian server with:
|
|||
./jeannie capacity
|
||||
./jeannie recover-plan
|
||||
./jeannie recover-power --dry-run
|
||||
./jeannie impact --since HEAD~1
|
||||
./jeannie scorecard
|
||||
./jeannie explain scorecard
|
||||
./jeannie gitops-status
|
||||
|
|
@ -330,6 +331,10 @@ edge verification.
|
|||
Debian runtime, Gitea, RPi DNS, Pimox workers, Kubernetes, GitOps/apps, and
|
||||
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
|
||||
health, edge health, capacity pressure, security posture, public certificates,
|
||||
inventory, and docs freshness into a compact pass/warn/fail report.
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ Run a read-only health snapshot from the Debian server with:
|
|||
./{{ main_script }} capacity
|
||||
./{{ main_script }} recover-plan
|
||||
./{{ main_script }} recover-power --dry-run
|
||||
./{{ main_script }} impact --since HEAD~1
|
||||
./{{ main_script }} scorecard
|
||||
./{{ main_script }} explain scorecard
|
||||
./{{ main_script }} gitops-status
|
||||
|
|
@ -330,6 +331,10 @@ edge verification.
|
|||
Debian runtime, Gitea, RPi DNS, Pimox workers, Kubernetes, GitOps/apps, and
|
||||
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
|
||||
health, edge health, capacity pressure, security posture, public certificates,
|
||||
inventory, and docs freshness into a compact pass/warn/fail report.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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`
|
||||
: Report freshness for Gitea backups, OpenTofu state backups, restore drill
|
||||
reports, and repo-managed Pi-hole restore inputs. The main `status` cascade
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
8
jeannie
8
jeannie
|
|
@ -5871,6 +5871,10 @@ ask_homelab() {
|
|||
"${REPO_ROOT}/scripts/ask" "${@:2}"
|
||||
}
|
||||
|
||||
impact() {
|
||||
"${REPO_ROOT}/scripts/impact" "${@:2}"
|
||||
}
|
||||
|
||||
security_scan() {
|
||||
require_debian_server "security-scan"
|
||||
"${REPO_ROOT}/scripts/security-scan" all
|
||||
|
|
@ -6125,6 +6129,7 @@ Operator View And Reports
|
|||
capacity Compact capacity and placement report.
|
||||
recover-plan Print disaster recovery order and prerequisites.
|
||||
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.
|
||||
change-journal {list|path} Show risky-command journal entries.
|
||||
release-snapshot Write a pre-change release snapshot.
|
||||
|
|
@ -6389,6 +6394,9 @@ case "${1:-}" in
|
|||
ask)
|
||||
ask_homelab "$@"
|
||||
;;
|
||||
impact)
|
||||
impact "$@"
|
||||
;;
|
||||
security-scan)
|
||||
security_scan
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Reference in New Issue