try some differnt stuff for utils

This commit is contained in:
RingOfStorms (Joshua Bell) 2025-12-17 14:21:46 -06:00
parent 1d0cb5e08f
commit e70046ede1
2 changed files with 134 additions and 44 deletions

View file

@ -45,7 +45,14 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
environment.systemPackages = [ bcacheImpermanenceBin ]; environment.systemPackages = [
bcacheImpermanenceBin
pkgs.coreutils
pkgs.findutils
pkgs.diffutils
pkgs.bcachefs-tools
pkgs.fzf
];
systemd.services."bcache-impermanence-gc" = lib.mkIf cfg.gc.enable { systemd.services."bcache-impermanence-gc" = lib.mkIf cfg.gc.enable {
description = "Garbage collect bcachefs impermanence snapshots"; description = "Garbage collect bcachefs impermanence snapshots";
@ -53,7 +60,6 @@ in
after = [ "multi-user.target" ]; after = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
Environment = "PATH=${lib.makeBinPath [ pkgs.coreutils pkgs.findutils pkgs.diffutils pkgs.bcachefs-tools ]}:/run/current-system/sw/bin";
}; };
script = '' script = ''
exec ${bcacheImpermanenceBin}/bin/bcache-impermanence gc \ exec ${bcacheImpermanenceBin}/bin/bcache-impermanence gc \

View file

@ -13,13 +13,13 @@ bcache-impermanence - tools for managing impermanence snapshots
Usage: Usage:
bcache-impermanence gc [--snapshot-root DIR] [--keep-per-month N] [--keep-recent-weeks N] [--keep-recent-count N] [--dry-run] bcache-impermanence gc [--snapshot-root DIR] [--keep-per-month N] [--keep-recent-weeks N] [--keep-recent-count N] [--dry-run]
bcache-impermanence ls [-n1] [--snapshot-root DIR] bcache-impermanence ls [-nN] [--snapshot-root DIR]
bcache-impermanence diff [-s SNAPSHOT] [--snapshot-root DIR] [PATH_PREFIX...] bcache-impermanence diff [-s SNAPSHOT] [--snapshot-root DIR] [PATH_PREFIX...]
Subcommands: Subcommands:
gc Run garbage collection on old root snapshots. gc Run garbage collection on old root snapshots.
ls List snapshots (newest first). With -n1 prints only latest. ls List snapshots (newest first). With -nN prints N latest.
diff Show diff between current system and a snapshot. diff Browse and diff files/dirs between current system and a snapshot.
Options: Options:
--snapshot-root DIR Override snapshot root directory (default: /.snapshots/old_roots). --snapshot-root DIR Override snapshot root directory (default: /.snapshots/old_roots).
@ -30,15 +30,6 @@ Options:
EOF EOF
} }
ensure_deps() {
for cmd in date sort basename diff bcachefs; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Missing required command: $cmd" >&2
exit 1
fi
done
}
list_snapshots_desc() { list_snapshots_desc() {
if [ ! -d "$SNAPSHOT_ROOT" ]; then if [ ! -d "$SNAPSHOT_ROOT" ]; then
return 0 return 0
@ -54,11 +45,16 @@ latest_snapshot_name() {
} }
cmd_ls() { cmd_ls() {
local n1=0 local count=0
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
case "$1" in case "$1" in
-n1) -n*)
n1=1 # Accept -nN where N is integer; default to 1 if empty.
local n="${1#-n}"
if [ -z "$n" ]; then
n=1
fi
count="$n"
;; ;;
--snapshot-root) --snapshot-root)
shift shift
@ -66,7 +62,7 @@ cmd_ls() {
SNAPSHOT_ROOT="$1" SNAPSHOT_ROOT="$1"
;; ;;
--help|-h) --help|-h)
echo "Usage: bcache-impermanence ls [-n1] [--snapshot-root DIR]" >&2 echo "Usage: bcache-impermanence ls [-nN] [--snapshot-root DIR]" >&2
exit 0 exit 0
;; ;;
*) *)
@ -85,9 +81,9 @@ cmd_ls() {
exit 1 exit 1
fi fi
if [ "$n1" -eq 1 ]; then if [ "$count" -gt 0 ] 2>/dev/null; then
printf '%s printf '%s
' "$snaps" | head -n1 ' "$snaps" | head -n "$count"
else else
printf '%s printf '%s
' "$snaps" ' "$snaps"
@ -117,7 +113,6 @@ build_keep_set() {
# Per-month: keep up to KEEP_PER_MONTH newest per month. # Per-month: keep up to KEEP_PER_MONTH newest per month.
if [ "$KEEP_PER_MONTH" -gt 0 ]; then if [ "$KEEP_PER_MONTH" -gt 0 ]; then
# Iterate newest -> oldest.
while read -r snap; do while read -r snap; do
[ -n "$snap" ] || continue [ -n "$snap" ] || continue
local month local month
@ -310,37 +305,128 @@ cmd_diff() {
set -- / set -- /
fi fi
local rc=0 local prefixes=("$@")
while [ "$#" -gt 0 ]; do local tmp
local path tmp=$(mktemp)
path="$1"
shift
case "$path" in for prefix in "${prefixes[@]}"; do
case "$prefix" in
/*) : ;; /*) : ;;
*) *)
echo "Path prefix must be absolute: $path" >&2 echo "Path prefix must be absolute: $prefix" >&2
rc=2
continue continue
;; ;;
esac esac
local from local rel
local to rel="${prefix#/}"
from="$snapshot_dir$path" [ -z "$rel" ] && rel="."
to="$path"
echo "--- Diff for $path (snapshot $snapshot_name) ---" (
if ! diff -ru --new-file "$from" "$to"; then cd "$snapshot_dir" && find "$rel" -mindepth 1 -print 2>/dev/null || true
local diff_rc=$? ) | sed "s/^/A /" >>"$tmp"
if [ "$diff_rc" -gt 1 ]; then
echo "Error running diff for $path" >&2 (
rc=$diff_rc cd / && find "$rel" -mindepth 1 -print 2>/dev/null || true
fi ) | sed "s/^/B /" >>"$tmp"
fi
done done
exit "$rc" if [ ! -s "$tmp" ]; then
echo "No files found under specified prefixes" >&2
rm -f "$tmp"
exit 1
fi
local paths
paths=$(cut -d' ' -f2- "$tmp" | sort -u)
local diff_list
diff_list=$(mktemp)
while read -r rel; do
[ -n "$rel" ] || continue
local a_path b_path
a_path="$snapshot_dir/$rel"
b_path="/$rel"
local status
if [ ! -e "$a_path" ] && [ -e "$b_path" ]; then
status="added"
elif [ -e "$a_path" ] && [ ! -e "$b_path" ]; then
status="removed"
else
if [ -d "$a_path" ] && [ -d "$b_path" ]; then
if ! diff -rq "$a_path" "$b_path" >/dev/null 2>&1; then
status="changed-dir"
else
continue
fi
else
if ! diff -q "$a_path" "$b_path" >/dev/null 2>&1; then
status="changed"
else
continue
fi
fi
fi
echo "$status $rel" >>"$diff_list"
done <<<"$paths"
rm -f "$tmp"
if [ ! -s "$diff_list" ]; then
echo "No differences found between snapshot $snapshot_name and current system" >&2
rm -f "$diff_list"
exit 0
fi
if ! command -v fzf >/dev/null 2>&1; then
echo "fzf is required for diff browsing" >&2
rm -f "$diff_list"
exit 1
fi
FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS:-} --ansi --preview-window=right:70%:wrap" \
fzf --prompt="[bcache-impermanence diff] " --preview '
status="$(echo {} | cut -d" " -f1)"
rel="$(echo {} | cut -d" " -f2-)"
snap_dir="'$snapshot_dir'"
a_path="$snap_dir/$rel"
b_path="/$rel"
case "$status" in
added)
echo "[ADDED] $rel"; echo
if [ -d "$b_path" ]; then
(cd / && find "${rel}" -maxdepth 3 -print 2>/dev/null || true)
else
diff -u /dev/null "$b_path" 2>/dev/null || cat "$b_path" 2>/dev/null || true
fi
;;
removed)
echo "[REMOVED] $rel"; echo
if [ -d "$a_path" ]; then
(cd "$snap_dir" && find "${rel}" -maxdepth 3 -print 2>/dev/null || true)
else
diff -u "$a_path" /dev/null 2>/dev/null || cat "$a_path" 2>/dev/null || true
fi
;;
changed-dir)
echo "[CHANGED DIR] $rel"; echo
diff -ru "$a_path" "$b_path" 2>/dev/null || true
;;
changed)
echo "[CHANGED] $rel"; echo
diff -u "$a_path" "$b_path" 2>/dev/null || true
;;
*)
echo "Unknown status: $status";
;;
esac
' <"$diff_list"
rm -f "$diff_list"
} }
main() { main() {
@ -349,8 +435,6 @@ main() {
exit 1 exit 1
fi fi
ensure_deps
local cmd local cmd
cmd="$1" cmd="$1"
shift || true shift || true