try some differnt stuff for utils
This commit is contained in:
parent
1d0cb5e08f
commit
e70046ede1
2 changed files with 134 additions and 44 deletions
|
|
@ -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 \
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue