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:
@@ -295,10 +295,16 @@ how to override it. Items marked ✓ are shipped.
|
|||||||
right-click toggles DND). Turning DND off confirms with a toast; turning
|
right-click toggles DND). Turning DND off confirms with a toast; turning
|
||||||
it on is silent (notifications are suppressed — the bell-off glyph is the
|
it on is silent (notifications are suppressed — the bell-off glyph is the
|
||||||
cue).
|
cue).
|
||||||
- **Snapshot browse/restore UX:** snapper already takes BTRFS timeline
|
- ✓ **Snapshot browse/restore UX:** `nomarchy-menu snapshot` (in the SUPER+M
|
||||||
snapshots (`nomarchy.system.snapper`); surface them from the desktop — a
|
picker, self-gated on snapper) launches **btrfs-assistant** — the
|
||||||
rofi menu (or btrfs-assistant) to browse/diff/restore and boot-from-
|
desktop browse/diff/restore/rollback UI, elevating via polkit (the safe
|
||||||
snapshot — so rollback isn't CLI-only.
|
choice for a destructive, root-only op vs a fat-fingerable rofi rollback).
|
||||||
|
Shipped system-side gated on `nomarchy.system.snapper`. Also extended
|
||||||
|
snapper to snapshot **/home** by default (hourly 5 / daily 7 / weekly 4)
|
||||||
|
when it's its own BTRFS subvolume; a boot oneshot creates the required
|
||||||
|
`/home/.snapshots` subvolume if missing (covers existing installs, no disko
|
||||||
|
change). Remaining (optional): a keyboard-driven rofi browse for quick
|
||||||
|
glances; boot-from-snapshot needs a systemd-boot equivalent of grub-btrfs.
|
||||||
- **Update awareness:** updates are manual today (`sys-update`/`home-update`);
|
- **Update awareness:** updates are manual today (`sys-update`/`home-update`);
|
||||||
add a Waybar indicator / notification when flake inputs are stale or a new
|
add a Waybar indicator / notification when flake inputs are stale or a new
|
||||||
nixpkgs rev is available (optionally with a pending-change count). Augments,
|
nixpkgs rev is available (optionally with a pending-change count). Augments,
|
||||||
|
|||||||
@@ -172,6 +172,14 @@ let
|
|||||||
&& notify-send "Do Not Disturb off" "Notifications resumed."
|
&& notify-send "Do Not Disturb off" "Notifications resumed."
|
||||||
exit 0 ;;
|
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=(
|
entries=(
|
||||||
" Apps"
|
" Apps"
|
||||||
@@ -188,6 +196,8 @@ let
|
|||||||
" Ask Claude"
|
" Ask Claude"
|
||||||
" Do Not Disturb"
|
" 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)
|
# Power-profile picker only when this is a laptop (battery present)
|
||||||
# running power-profiles-daemon — matches the Waybar indicator's
|
# running power-profiles-daemon — matches the Waybar indicator's
|
||||||
# self-gating; desktops and the TLP backend don't show it.
|
# self-gating; desktops and the TLP backend don't show it.
|
||||||
@@ -211,12 +221,13 @@ let
|
|||||||
*Keybindings*) exec "$0" keybinds ;;
|
*Keybindings*) exec "$0" keybinds ;;
|
||||||
*Ask*) exec "$0" ask ;;
|
*Ask*) exec "$0" ask ;;
|
||||||
*"Do Not Disturb"*) exec "$0" dnd ;;
|
*"Do Not Disturb"*) exec "$0" dnd ;;
|
||||||
|
*Snapshots*) exec "$0" snapshot ;;
|
||||||
*"Power profile"*) exec "$0" power-profile ;;
|
*"Power profile"*) exec "$0" power-profile ;;
|
||||||
*Power*) exec "$0" power ;;
|
*Power*) exec "$0" power ;;
|
||||||
esac ;;
|
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 ;;
|
exit 64 ;;
|
||||||
esac
|
esac
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ in
|
|||||||
# is a clean no-op rather than a failing timer.
|
# is a clean no-op rather than a failing timer.
|
||||||
services.snapper.configs = lib.mkIf
|
services.snapper.configs = lib.mkIf
|
||||||
(cfg.snapper.enable && (config.fileSystems."/".fsType or "") == "btrfs")
|
(cfg.snapper.enable && (config.fileSystems."/".fsType or "") == "btrfs")
|
||||||
{
|
({
|
||||||
root = {
|
root = {
|
||||||
SUBVOLUME = "/";
|
SUBVOLUME = "/";
|
||||||
TIMELINE_CREATE = true;
|
TIMELINE_CREATE = true;
|
||||||
@@ -196,6 +196,39 @@ in
|
|||||||
TIMELINE_LIMIT_MONTHLY = "0";
|
TIMELINE_LIMIT_MONTHLY = "0";
|
||||||
TIMELINE_LIMIT_YEARLY = "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 ────────────────────────────────────────────────────────
|
# ── Fonts ────────────────────────────────────────────────────────
|
||||||
@@ -285,7 +318,10 @@ in
|
|||||||
--cleanup-algorithm number
|
--cleanup-algorithm number
|
||||||
echo "Rebuilding..."
|
echo "Rebuilding..."
|
||||||
nixos-rebuild switch --flake /etc/nixos#default "$@"
|
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.
|
# Don't let boot entries fill the ESP over the years.
|
||||||
boot.loader.systemd-boot.configurationLimit = lib.mkDefault 10;
|
boot.loader.systemd-boot.configurationLimit = lib.mkDefault 10;
|
||||||
|
|||||||
Reference in New Issue
Block a user