feat(snapshots): desktop browse/restore + snapshot /home by default
Surface snapper snapshots from the desktop: `nomarchy-menu snapshot` (in the SUPER+M picker, self-gated) launches btrfs-assistant, the polkit-aware browse/diff/restore/rollback UI -- safer than a fat-fingerable rofi rollback for a destructive root-only op. Shipped system-side gated on nomarchy.system.snapper. Also snapshot /home by default (hourly 5 / daily 7 / weekly 4) when it's its own BTRFS subvolume -- the installer only set up snapper for /. A boot oneshot creates the required /home/.snapshots subvolume if missing, so already-installed machines get it too (no disko change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -172,6 +172,14 @@ let
|
||||
&& notify-send "Do Not Disturb off" "Notifications resumed."
|
||||
exit 0 ;;
|
||||
|
||||
snapshot)
|
||||
# btrfs-assistant: snapshot browse / diff / restore / rollback over
|
||||
# snapper, elevating via polkit. Shipped system-side with
|
||||
# nomarchy.system.snapper; self-gate so the entry no-ops elsewhere.
|
||||
command -v btrfs-assistant >/dev/null 2>&1 \
|
||||
|| { notify-send "Snapshots" "btrfs-assistant isn't installed (BTRFS snapshots off?)."; exit 0; }
|
||||
exec btrfs-assistant ;;
|
||||
|
||||
"")
|
||||
entries=(
|
||||
" Apps"
|
||||
@@ -188,6 +196,8 @@ let
|
||||
" Ask Claude"
|
||||
" Do Not Disturb"
|
||||
)
|
||||
# Snapshots only when btrfs-assistant is installed (BTRFS + snapper).
|
||||
command -v btrfs-assistant >/dev/null 2>&1 && entries+=(" Snapshots")
|
||||
# Power-profile picker only when this is a laptop (battery present)
|
||||
# running power-profiles-daemon — matches the Waybar indicator's
|
||||
# self-gating; desktops and the TLP backend don't show it.
|
||||
@@ -211,12 +221,13 @@ let
|
||||
*Keybindings*) exec "$0" keybinds ;;
|
||||
*Ask*) exec "$0" ask ;;
|
||||
*"Do Not Disturb"*) exec "$0" dnd ;;
|
||||
*Snapshots*) exec "$0" snapshot ;;
|
||||
*"Power profile"*) exec "$0" power-profile ;;
|
||||
*Power*) exec "$0" power ;;
|
||||
esac ;;
|
||||
|
||||
*)
|
||||
echo "usage: nomarchy-menu [power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|capture|keybinds|ask|dnd]" >&2
|
||||
echo "usage: nomarchy-menu [power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|capture|keybinds|ask|dnd|snapshot]" >&2
|
||||
exit 64 ;;
|
||||
esac
|
||||
'';
|
||||
|
||||
@@ -185,7 +185,7 @@ in
|
||||
# is a clean no-op rather than a failing timer.
|
||||
services.snapper.configs = lib.mkIf
|
||||
(cfg.snapper.enable && (config.fileSystems."/".fsType or "") == "btrfs")
|
||||
{
|
||||
({
|
||||
root = {
|
||||
SUBVOLUME = "/";
|
||||
TIMELINE_CREATE = true;
|
||||
@@ -196,6 +196,39 @@ in
|
||||
TIMELINE_LIMIT_MONTHLY = "0";
|
||||
TIMELINE_LIMIT_YEARLY = "0";
|
||||
};
|
||||
}
|
||||
# /home is user data — snapshot it too when it's its own BTRFS subvolume
|
||||
# (the installer's @home). Keep the recent hourlies for oops-recovery
|
||||
# plus dailies, and a month of weeklies; "at least daily" by default.
|
||||
// lib.optionalAttrs ((config.fileSystems."/home".fsType or "") == "btrfs") {
|
||||
home = {
|
||||
SUBVOLUME = "/home";
|
||||
TIMELINE_CREATE = true;
|
||||
TIMELINE_CLEANUP = true;
|
||||
TIMELINE_LIMIT_HOURLY = "5";
|
||||
TIMELINE_LIMIT_DAILY = "7";
|
||||
TIMELINE_LIMIT_WEEKLY = "4";
|
||||
TIMELINE_LIMIT_MONTHLY = "0";
|
||||
TIMELINE_LIMIT_YEARLY = "0";
|
||||
};
|
||||
});
|
||||
|
||||
# snapper needs a `.snapshots` subvolume inside each tracked subvolume.
|
||||
# The installer made one for / (@snapshots → /.snapshots) but not /home,
|
||||
# so create a nested one there if missing (idempotent via the condition;
|
||||
# excluded from /home snapshots since BTRFS snapshots are non-recursive).
|
||||
# Runs at boot so it also fixes already-installed machines — no disko
|
||||
# change needed.
|
||||
systemd.services.nomarchy-home-snapshots-subvol = lib.mkIf
|
||||
(cfg.snapper.enable && (config.fileSystems."/home".fsType or "") == "btrfs") {
|
||||
description = "Create /home/.snapshots for snapper if missing";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "snapper-timeline.service" "snapper-cleanup.service" ];
|
||||
unitConfig.ConditionPathExists = "!/home/.snapshots";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${pkgs.btrfs-progs}/bin/btrfs subvolume create /home/.snapshots";
|
||||
};
|
||||
};
|
||||
|
||||
# ── Fonts ────────────────────────────────────────────────────────
|
||||
@@ -285,7 +318,10 @@ in
|
||||
--cleanup-algorithm number
|
||||
echo "Rebuilding..."
|
||||
nixos-rebuild switch --flake /etc/nixos#default "$@"
|
||||
'');
|
||||
'')
|
||||
# The desktop snapshot manager (browse / diff / restore / rollback over
|
||||
# snapper, elevating via polkit) — what `nomarchy-menu snapshot` launches.
|
||||
++ lib.optional cfg.snapper.enable pkgs.btrfs-assistant;
|
||||
|
||||
# Don't let boot entries fill the ESP over the years.
|
||||
boot.loader.systemd-boot.configurationLimit = lib.mkDefault 10;
|
||||
|
||||
Reference in New Issue
Block a user