diff --git a/README.md b/README.md index dd893c5..38863ad 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,7 @@ Run a read-only health snapshot from the Debian server with: ./jeannie release-snapshot ./jeannie backup-status ./jeannie workers list +./jeannie change-journal list ./jeannie map ``` @@ -326,6 +327,12 @@ restore drill reports, and repo-managed Pi-hole restore inputs. The main Use `recreate-plan ` before replacing one broken Pimox worker so the cluster drain, VM stop, and `./jeannie up` path stay ordered. +`change-journal` lists local journal entries written before risky commands such +as `up`, `apps`, `deploy-gitea`, `rpi-services`, `stop-cluster`, +`start-cluster`, `rebuild-cluster`, `release-snapshot`, and `nuke`. Entries live +under `${HOMELAB_STATE_DIR}/change-journal` and record Git revision, branch, +dirty file count, and command details. + `map` prints a dependency map from the canonical inventory, covering public entry, GitOps automation, cluster foundation, DNS, observability, and security learning paths. `./jeannie map --dot` emits Graphviz DOT. diff --git a/README.md.tmpl b/README.md.tmpl index 1528aa7..4f98808 100644 --- a/README.md.tmpl +++ b/README.md.tmpl @@ -277,6 +277,7 @@ Run a read-only health snapshot from the Debian server with: ./{{ main_script }} release-snapshot ./{{ main_script }} backup-status ./{{ main_script }} workers list +./{{ main_script }} change-journal list ./{{ main_script }} map ``` @@ -326,6 +327,12 @@ restore drill reports, and repo-managed Pi-hole restore inputs. The main Use `recreate-plan ` before replacing one broken Pimox worker so the cluster drain, VM stop, and `./{{ main_script }} up` path stay ordered. +`change-journal` lists local journal entries written before risky commands such +as `up`, `apps`, `deploy-gitea`, `rpi-services`, `stop-cluster`, +`start-cluster`, `rebuild-cluster`, `release-snapshot`, and `nuke`. Entries live +under `${HOMELAB_STATE_DIR}/change-journal` and record Git revision, branch, +dirty file count, and command details. + `map` prints a dependency map from the canonical inventory, covering public entry, GitOps automation, cluster foundation, DNS, observability, and security learning paths. `./{{ main_script }} map --dot` emits Graphviz DOT. diff --git a/docs/jeannie.1.md b/docs/jeannie.1.md index e4b9951..663cf34 100644 --- a/docs/jeannie.1.md +++ b/docs/jeannie.1.md @@ -85,6 +85,10 @@ includes this as a warning signal. : Manage Kubernetes/Pimox worker lifecycle operations. Commands include `list`, `start`, `stop`, `drain`, `uncordon`, `recreate-plan`, and `rebalance`. +`change-journal {list|path}` +: List local journal entries written before risky commands, or print the journal +directory path. + `map` : Print a dependency map from the canonical inventory. Use `map --dot` for a Graphviz DOT graph. diff --git a/jeannie b/jeannie index bc99ac9..fd075e7 100755 --- a/jeannie +++ b/jeannie @@ -5997,24 +5997,39 @@ workers_manage() { "${REPO_ROOT}/scripts/workers" "${@:2}" } +record_change_journal() { + local command_name="$1" + shift || true + + "${REPO_ROOT}/scripts/change-journal" append "${command_name}" "$*" || true +} + +change_journal() { + "${REPO_ROOT}/scripts/change-journal" "${@:2}" +} + homelab_map() { "${REPO_ROOT}/scripts/homelab-map" "${@:2}" } case "${1:-}" in up) + record_change_journal "up" "$@" up ;; plan) plan_homelab "${2:-all}" ;; rebuild-cluster) + record_change_journal "rebuild-cluster" "$@" rebuild_cluster ;; stop-cluster) + record_change_journal "stop-cluster" "$@" stop_cluster ;; start-cluster) + record_change_journal "start-cluster" "$@" start_cluster ;; status) @@ -6033,6 +6048,7 @@ case "${1:-}" in cert_check ;; release-snapshot) + record_change_journal "release-snapshot" "$@" release_snapshot ;; backup-status) @@ -6041,6 +6057,9 @@ case "${1:-}" in workers) workers_manage "$@" ;; + change-journal) + change_journal "$@" + ;; map) homelab_map "$@" ;; @@ -6054,6 +6073,7 @@ case "${1:-}" in kubeconfig_readonly ;; apps) + record_change_journal "apps" "$@" require_debian_server "apps" jeannie_log_start "apps" jeannie_step_plan 1 @@ -6070,9 +6090,11 @@ case "${1:-}" in ollama_setup ;; deploy-gitea) + record_change_journal "deploy-gitea" "$@" deploy_gitea ;; rpi-services) + record_change_journal "rpi-services" "$@" deploy_rpi_services ;; bootstrap-gitea-repo) @@ -6181,6 +6203,7 @@ case "${1:-}" in openwrt ;; nuke) + record_change_journal "nuke" "$@" require_debian_server "nuke" jeannie_log_start "nuke" jeannie_step_plan 1 @@ -6188,7 +6211,7 @@ case "${1:-}" in echo "Log: ${JEANNIE_LOG_FILE}" ;; *) - echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|capacity|recover-plan|gitops-status|cert-check|release-snapshot|backup-status|workers |map [--dot]|validate|access-audit|kubeconfig-readonly|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-restore|drill-gitea-restore|drill-pihole-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-runtime|security-attack-path|openwrt|nuke}" + echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|capacity|recover-plan|gitops-status|cert-check|release-snapshot|backup-status|workers |change-journal {list|path}|map [--dot]|validate|access-audit|kubeconfig-readonly|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-restore|drill-gitea-restore|drill-pihole-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-runtime|security-attack-path|openwrt|nuke}" exit 1 ;; esac diff --git a/scripts/change-journal b/scripts/change-journal new file mode 100755 index 0000000..e99c4e0 --- /dev/null +++ b/scripts/change-journal @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +HOMELAB_STATE_DIR="${HOMELAB_STATE_DIR:-${XDG_DATA_HOME:-${HOME}/.local/share}/homelab}" +JOURNAL_DIR="${HOMELAB_CHANGE_JOURNAL_DIR:-${HOMELAB_STATE_DIR}/change-journal}" +ACTION="${1:-list}" + +mkdir -p "$JOURNAL_DIR" + +timestamp() { + date -u +%Y%m%dT%H%M%SZ +} + +latest_entries() { + local count="${1:-20}" + + find "$JOURNAL_DIR" -maxdepth 1 -type f -name '*.log' -print 2>/dev/null | + sort | + tail -n "$count" | + while IFS= read -r file; do + printf '\n== %s ==\n' "$(basename "$file")" + sed -n '1,80p' "$file" + done +} + +append_entry() { + local command_name="${2:-unknown}" + local detail="${3:-}" + local now + local file + + now="$(timestamp)" + file="${JOURNAL_DIR}/${now}-${command_name//[^A-Za-z0-9_.-]/_}.log" + + { + printf 'created_utc=%s\n' "$now" + printf 'command=%s\n' "$command_name" + printf 'detail=%s\n' "$detail" + printf 'host=%s\n' "$(hostname 2>/dev/null || echo unknown)" + printf 'repo=%s\n' "$REPO_ROOT" + printf 'branch=%s\n' "$(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)" + printf 'revision=%s\n' "$(git -C "$REPO_ROOT" rev-parse HEAD 2>/dev/null || echo unknown)" + printf 'dirty_files=%s\n' "$(git -C "$REPO_ROOT" status --short 2>/dev/null | wc -l | tr -d ' ')" + printf '\n-- git status --short --\n' + git -C "$REPO_ROOT" status --short 2>/dev/null || true + } >"$file" + + printf 'Recorded change journal entry: %s\n' "$file" +} + +case "$ACTION" in + append) + append_entry "$@" + ;; + list) + latest_entries "${2:-20}" + ;; + path) + printf '%s\n' "$JOURNAL_DIR" + ;; + *) + echo "Usage: ./jeannie change-journal {list [count]|path}" >&2 + echo "Internal: scripts/change-journal append [detail]" >&2 + exit 1 + ;; +esac