Some checks failed
Check / eval (push) Has been cancelled
nomarchy-what-changed wraps nvd into "N added, M removed, K updated" summaries. nomarchy-rebuild / nomarchy-home print full nvd and toast the one-liner; System › What changed? offers toast + floating report. checks.what-changed fixtures nvd via NOMARCHY_NVD. Verified: V2 — flake check --no-build; checks.what-changed green; local smoke on real HM generations.
179 lines
5.4 KiB
Bash
179 lines
5.4 KiB
Bash
# nomarchy-what-changed — plain-language "what changed last rebuild".
|
||
# Wraps nvd into a short summary (for toasts / menu) and optional full
|
||
# report. In-checkout state is not involved; this only reads Nix profiles.
|
||
#
|
||
# usage:
|
||
# nomarchy-what-changed # system + home (last two gens each)
|
||
# nomarchy-what-changed system|home # one layer
|
||
# nomarchy-what-changed --summary … # one line per layer (toast body)
|
||
# nomarchy-what-changed --diff PATH1 PATH2
|
||
# nomarchy-what-changed --summary --diff PATH1 PATH2
|
||
#
|
||
# NOMARCHY_NVD overrides the nvd binary (tests inject a fixture).
|
||
|
||
set -euo pipefail
|
||
|
||
nvd_bin="${NOMARCHY_NVD:-nvd}"
|
||
summary_only=0
|
||
mode="" # empty | system | home | diff
|
||
diff_a="" diff_b=""
|
||
|
||
usage() {
|
||
echo "usage: nomarchy-what-changed [--summary] [system|home]" >&2
|
||
echo " nomarchy-what-changed [--summary] --diff <before> <after>" >&2
|
||
exit 64
|
||
}
|
||
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--summary) summary_only=1; shift ;;
|
||
--diff)
|
||
mode="diff"
|
||
shift
|
||
[ $# -ge 2 ] || usage
|
||
diff_a=$1; diff_b=$2; shift 2
|
||
;;
|
||
system|home) mode="$1"; shift ;;
|
||
-h|--help) usage ;;
|
||
*) usage ;;
|
||
esac
|
||
done
|
||
|
||
# One-line plain-language summary of an nvd --color never report.
|
||
# Prefer package row counts ([A.]/[R.]/[C.]); fall back to closure path delta.
|
||
summarize_report() {
|
||
local label=$1 report=$2
|
||
local added removed changed
|
||
if printf '%s\n' "$report" | grep -q 'No version or selection state changes'; then
|
||
printf '%s: no package changes\n' "$label"
|
||
return
|
||
fi
|
||
# Count package rows (nvd tags: A added, R removed, C version/selection change).
|
||
added=$(printf '%s\n' "$report" | grep -c '\[A\.' || true)
|
||
removed=$(printf '%s\n' "$report" | grep -c '\[R\.' || true)
|
||
changed=$(printf '%s\n' "$report" | grep -c '\[C\.' || true)
|
||
# Strip trailing newline noise from grep -c on empty (already || true → 0).
|
||
added=${added:-0}; removed=${removed:-0}; changed=${changed:-0}
|
||
|
||
if [ "$added" -eq 0 ] && [ "$removed" -eq 0 ] && [ "$changed" -eq 0 ]; then
|
||
# Closure-only deltas (sources, etc. without package names).
|
||
local paths_a paths_r
|
||
paths_a=$(printf '%s\n' "$report" \
|
||
| sed -n 's/.*(\([0-9]*\) paths added,.*/\1/p' | head -1)
|
||
paths_r=$(printf '%s\n' "$report" \
|
||
| sed -n 's/.*paths added, \([0-9]*\) paths removed.*/\1/p' | head -1)
|
||
paths_a=${paths_a:-0}; paths_r=${paths_r:-0}
|
||
if [ "$paths_a" -eq 0 ] && [ "$paths_r" -eq 0 ]; then
|
||
printf '%s: no package changes\n' "$label"
|
||
else
|
||
printf '%s: %s paths added, %s removed\n' "$label" "$paths_a" "$paths_r"
|
||
fi
|
||
return
|
||
fi
|
||
|
||
local parts=()
|
||
[ "$added" -gt 0 ] && parts+=("$added added")
|
||
[ "$removed" -gt 0 ] && parts+=("$removed removed")
|
||
[ "$changed" -gt 0 ] && parts+=("$changed updated")
|
||
local joined
|
||
joined=$(printf '%s, ' "${parts[@]}")
|
||
joined=${joined%, }
|
||
printf '%s: %s\n' "$label" "$joined"
|
||
}
|
||
|
||
run_diff() {
|
||
local before=$1 after=$2 label=$3
|
||
if [ ! -e "$before" ] || [ ! -e "$after" ]; then
|
||
printf '%s: no previous generation to compare\n' "$label"
|
||
return 0
|
||
fi
|
||
if [ "$(readlink -f "$before" 2>/dev/null || echo "$before")" \
|
||
= "$(readlink -f "$after" 2>/dev/null || echo "$after")" ]; then
|
||
printf '%s: no package changes\n' "$label"
|
||
return 0
|
||
fi
|
||
local report
|
||
report=$("$nvd_bin" --color never diff "$before" "$after" 2>/dev/null) || {
|
||
printf '%s: could not diff (is nvd installed?)\n' "$label"
|
||
return 0
|
||
}
|
||
if [ "$summary_only" -eq 1 ]; then
|
||
summarize_report "$label" "$report"
|
||
else
|
||
printf '── %s ──\n' "$label"
|
||
printf '%s\n' "$report"
|
||
echo
|
||
summarize_report "$label" "$report"
|
||
fi
|
||
}
|
||
|
||
# Resolve last two links for a profile stem (…/system or …/home-manager).
|
||
last_two() {
|
||
local stem=$1
|
||
# shellcheck disable=SC2012
|
||
ls -d "${stem}"-[0-9]*-link 2>/dev/null | sort -V | tail -n 2
|
||
}
|
||
|
||
system_pair() {
|
||
last_two /nix/var/nix/profiles/system
|
||
}
|
||
|
||
home_pair() {
|
||
local d
|
||
d="${XDG_STATE_HOME:-$HOME/.local/state}/nix/profiles"
|
||
if ls -d "$d"/home-manager-[0-9]*-link >/dev/null 2>&1; then
|
||
last_two "$d/home-manager"
|
||
return
|
||
fi
|
||
d="/nix/var/nix/profiles/per-user/${USER:-$(id -un)}"
|
||
if ls -d "$d"/home-manager-[0-9]*-link >/dev/null 2>&1; then
|
||
last_two "$d/home-manager"
|
||
return
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
pair_to_before_after() {
|
||
# stdin: 0–2 paths; sets before/after (before may be empty).
|
||
local -a links=()
|
||
while IFS= read -r line; do
|
||
[ -n "$line" ] && links+=("$line")
|
||
done
|
||
before=""; after=""
|
||
if [ "${#links[@]}" -ge 2 ]; then
|
||
before=$(readlink -f "${links[0]}")
|
||
after=$(readlink -f "${links[1]}")
|
||
elif [ "${#links[@]}" -eq 1 ]; then
|
||
after=$(readlink -f "${links[0]}")
|
||
fi
|
||
}
|
||
|
||
if [ "$mode" = diff ]; then
|
||
run_diff "$diff_a" "$diff_b" "Diff"
|
||
exit 0
|
||
fi
|
||
|
||
if [ -z "$mode" ] || [ "$mode" = system ]; then
|
||
before=""; after=""
|
||
pair_to_before_after < <(system_pair)
|
||
if [ -n "$after" ] && [ -n "$before" ]; then
|
||
run_diff "$before" "$after" "System"
|
||
elif [ -n "$after" ]; then
|
||
printf 'System: only one generation on disk — rebuild once more to compare\n'
|
||
else
|
||
printf 'System: no NixOS profile found\n'
|
||
fi
|
||
fi
|
||
|
||
if [ -z "$mode" ] || [ "$mode" = home ]; then
|
||
before=""; after=""
|
||
pair_to_before_after < <(home_pair)
|
||
if [ -n "$after" ] && [ -n "$before" ]; then
|
||
run_diff "$before" "$after" "Desktop"
|
||
elif [ -n "$after" ]; then
|
||
printf 'Desktop: only one generation on disk — switch once more to compare\n'
|
||
else
|
||
printf 'Desktop: no Home Manager profile found\n'
|
||
fi
|
||
fi
|