adding h002 back in
This commit is contained in:
parent
ea9340a612
commit
acfec76a79
11 changed files with 1014 additions and 131 deletions
|
|
@ -34,6 +34,4 @@
|
||||||
networkmanager
|
networkmanager
|
||||||
nvtopPackages.full
|
nvtopPackages.full
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "23.11";
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||||
# nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
home-manager.url = "github:rycee/home-manager/release-25.11";
|
||||||
|
|
||||||
# Use relative to get current version for testing
|
common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
|
||||||
# common.url = "path:../../common";
|
# de_plasma.url = "path:../../../../flakes/de_plasma";
|
||||||
common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles";
|
de_plasma.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/de_plasma";
|
||||||
|
|
||||||
ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim";
|
ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim";
|
||||||
|
|
||||||
|
impermanence.url = "github:nix-community/impermanence";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
|
|
@ -16,93 +18,123 @@
|
||||||
common,
|
common,
|
||||||
ros_neovim,
|
ros_neovim,
|
||||||
...
|
...
|
||||||
}:
|
}@inputs:
|
||||||
let
|
let
|
||||||
configuration_name = "h002";
|
configurationName = "h002";
|
||||||
lib = nixpkgs.lib;
|
primaryUser = "luser";
|
||||||
|
configLocation = "/home/${primaryUser}/.config/nixos-config/hosts/${configurationName}";
|
||||||
|
stateAndHomeVersion = "25.11";
|
||||||
|
# overlayIp = "100.64.0.14";
|
||||||
|
lib = inputs.nixpkgs.lib;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
nixosConfigurations = {
|
nixosConfigurations = {
|
||||||
"${configuration_name}" = (
|
"${configurationName}" = (
|
||||||
lib.nixosSystem {
|
lib.nixosSystem {
|
||||||
|
specialArgs = {
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
modules = [
|
modules = [
|
||||||
common.nixosModules.default
|
inputs.impermanence.nixosModules.impermanence
|
||||||
ros_neovim.nixosModules.default
|
inputs.home-manager.nixosModules.default
|
||||||
./configuration.nix
|
|
||||||
|
# TODO
|
||||||
|
# secrets.nixosModules.default
|
||||||
|
inputs.ros_neovim.nixosModules.default
|
||||||
|
({
|
||||||
|
ringofstorms-nvim.includeAllRuntimeDependencies = true;
|
||||||
|
})
|
||||||
|
|
||||||
|
inputs.common.nixosModules.essentials
|
||||||
|
inputs.common.nixosModules.git
|
||||||
|
inputs.common.nixosModules.tmux
|
||||||
|
inputs.common.nixosModules.boot_grub
|
||||||
|
({
|
||||||
|
boot.loader.grub.device = "/dev/sdb";
|
||||||
|
})
|
||||||
|
inputs.common.nixosModules.hardening
|
||||||
|
inputs.common.nixosModules.nix_options
|
||||||
|
inputs.common.nixosModules.no_sleep
|
||||||
|
inputs.common.nixosModules.timezone_auto
|
||||||
|
inputs.common.nixosModules.tty_caps_esc
|
||||||
|
inputs.common.nixosModules.zsh
|
||||||
|
# TODO
|
||||||
|
# common.nixosModules.tailnet
|
||||||
|
# beszel.nixosModules.agent
|
||||||
|
# (
|
||||||
|
# { ... }:
|
||||||
|
# {
|
||||||
|
# beszelAgent = {
|
||||||
|
# listen = "${overlayIp}:45876";
|
||||||
|
# token = "f8a54c41-486b-487a-a78d-a087385c317b";
|
||||||
|
# };
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
|
./hardware-mounts.nix
|
||||||
|
./impermanence.nix
|
||||||
|
./impermanence-tools.nix
|
||||||
(
|
(
|
||||||
{ config, pkgs, ... }:
|
|
||||||
{
|
{
|
||||||
environment.systemPackages = with pkgs; [
|
config,
|
||||||
lua
|
pkgs,
|
||||||
qdirstat
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
rec {
|
||||||
|
system.stateVersion = stateAndHomeVersion;
|
||||||
|
|
||||||
|
# Home Manager
|
||||||
|
home-manager = {
|
||||||
|
useUserPackages = true;
|
||||||
|
useGlobalPkgs = true;
|
||||||
|
backupFileExtension = "bak";
|
||||||
|
# add all normal users to home manager so it applies to them
|
||||||
|
users = lib.mapAttrs (name: user: {
|
||||||
|
home.stateVersion = stateAndHomeVersion;
|
||||||
|
programs.home-manager.enable = true;
|
||||||
|
}) (lib.filterAttrs (name: user: user.isNormalUser or false) users.users);
|
||||||
|
|
||||||
|
sharedModules = [
|
||||||
|
inputs.common.homeManagerModules.tmux
|
||||||
|
inputs.common.homeManagerModules.atuin
|
||||||
|
inputs.common.homeManagerModules.direnv
|
||||||
|
inputs.common.homeManagerModules.git
|
||||||
|
inputs.common.homeManagerModules.postgres_cli_options
|
||||||
|
inputs.common.homeManagerModules.starship
|
||||||
|
inputs.common.homeManagerModules.zoxide
|
||||||
|
inputs.common.homeManagerModules.zsh
|
||||||
];
|
];
|
||||||
|
|
||||||
ringofstorms_common = {
|
extraSpecialArgs = {
|
||||||
systemName = configuration_name;
|
inherit inputs;
|
||||||
boot.grub.enable = true;
|
|
||||||
secrets.enable = true;
|
|
||||||
desktopEnvironment.gnome.enable = true;
|
|
||||||
general = {
|
|
||||||
reporting.enable = true;
|
|
||||||
};
|
};
|
||||||
programs = {
|
|
||||||
qFlipper.enable = true;
|
|
||||||
rustDev.enable = true;
|
|
||||||
tailnet.enable = true;
|
|
||||||
ssh.enable = true;
|
|
||||||
docker.enable = true;
|
|
||||||
uhkAgent.enable = true;
|
|
||||||
};
|
};
|
||||||
users = {
|
|
||||||
admins = [ "luser" ]; # First admin is also the primary user owning nix config
|
# System configuration
|
||||||
users = {
|
networking.networkmanager.enable = true;
|
||||||
root = {
|
networking.hostName = configurationName;
|
||||||
openssh.authorizedKeys.keys = [
|
programs.nh.flake = configLocation;
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJie9OPheWn/EZWfXJSZ3S0DnISqI3ToCmOqhX/Tkwby nix2h002"
|
nixpkgs.config.allowUnfree = true;
|
||||||
];
|
# users.mutableUsers = false;
|
||||||
};
|
users.users = {
|
||||||
luser = {
|
"${primaryUser}" = {
|
||||||
openssh.authorizedKeys.keys = [
|
isNormalUser = true;
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJie9OPheWn/EZWfXJSZ3S0DnISqI3ToCmOqhX/Tkwby nix2h002"
|
# hashedPassword = ""; # Use if mutable users is false above
|
||||||
];
|
initialHashedPassword = "$y$j9T$v1QhXiZMRY1pFkPmkLkdp0$451GvQt.XFU2qCAi4EQNd1BEqjM/CH6awU8gjcULps6"; # "test" password
|
||||||
extraGroups = [
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
"networkmanager"
|
"networkmanager"
|
||||||
"video"
|
|
||||||
"input"
|
|
||||||
];
|
];
|
||||||
shell = pkgs.zsh;
|
openssh.authorizedKeys.keys = [
|
||||||
packages = with pkgs; [
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2KFSRkViT+asBTjCgA7LNP3SHnfNCW+jHbV08VUuIi nix2nix"
|
||||||
bitwarden
|
|
||||||
vaultwarden
|
|
||||||
google-chrome
|
|
||||||
firefox-esr
|
|
||||||
openscad
|
|
||||||
vlc
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
root.openssh.authorizedKeys.keys = [
|
||||||
};
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2KFSRkViT+asBTjCgA7LNP3SHnfNCW+jHbV08VUuIi nix2nix"
|
||||||
homeManager = {
|
|
||||||
users = {
|
|
||||||
luser = {
|
|
||||||
imports = with common.homeManagerModules; [
|
|
||||||
kitty
|
|
||||||
tmux
|
|
||||||
atuin
|
|
||||||
direnv
|
|
||||||
git
|
|
||||||
nix_deprecations
|
|
||||||
postgres
|
|
||||||
ssh
|
|
||||||
starship
|
|
||||||
zoxide
|
|
||||||
zsh
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
"ahci"
|
"ahci"
|
||||||
"xhci_pci"
|
"xhci_pci"
|
||||||
"firewire_ohci"
|
"firewire_ohci"
|
||||||
"usb_storage"
|
|
||||||
"usbhid"
|
"usbhid"
|
||||||
|
"usb_storage"
|
||||||
"sd_mod"
|
"sd_mod"
|
||||||
"sr_mod"
|
"sr_mod"
|
||||||
];
|
];
|
||||||
|
|
@ -27,23 +27,6 @@
|
||||||
boot.kernelModules = [ "kvm-intel" ];
|
boot.kernelModules = [ "kvm-intel" ];
|
||||||
boot.extraModulePackages = [ ];
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
fileSystems."/" = {
|
|
||||||
device = "/dev/disk/by-label/NIXROOT";
|
|
||||||
fsType = "ext4";
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/boot" = {
|
|
||||||
device = "/dev/disk/by-label/NIXBOOT";
|
|
||||||
fsType = "vfat";
|
|
||||||
};
|
|
||||||
|
|
||||||
swapDevices = [
|
|
||||||
{
|
|
||||||
device = "/.swapfile";
|
|
||||||
size = 18 * 1024; # 18GB
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||||
# (the default) this is the recommended approach. When using systemd-networkd it's
|
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||||
# still possible to use this option, but it's recommended to use it in conjunction
|
# still possible to use this option, but it's recommended to use it in conjunction
|
||||||
|
|
|
||||||
142
hosts/h002/hardware-mounts.nix
Normal file
142
hosts/h002/hardware-mounts.nix
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
{
|
||||||
|
utils,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
BOOT = "/dev/disk/by-uuid/CC65-4ADF";
|
||||||
|
PRIMARY = "/dev/disk/by-uuid/35c8b82e-de7d-45bc-9cb2-2a422a99ee9c";
|
||||||
|
|
||||||
|
SWAP = "/dev/disk/by-uuid/85801775-1aad-4cc8-846a-560f9f4b11f4";
|
||||||
|
|
||||||
|
primaryDeviceUnit = "${utils.escapeSystemdPath PRIMARY}.device";
|
||||||
|
in
|
||||||
|
lib.mkMerge [
|
||||||
|
# Main filesystems
|
||||||
|
{
|
||||||
|
# BOOT
|
||||||
|
fileSystems."/boot" = {
|
||||||
|
device = BOOT;
|
||||||
|
fsType = "vfat";
|
||||||
|
options = [
|
||||||
|
"fmask=0077"
|
||||||
|
"dmask=0077"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# PRIMARY
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = PRIMARY;
|
||||||
|
fsType = "bcachefs";
|
||||||
|
options = [
|
||||||
|
"X-mount.subdir=@root"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
fileSystems."/nix" = {
|
||||||
|
device = PRIMARY;
|
||||||
|
fsType = "bcachefs";
|
||||||
|
options = [
|
||||||
|
"X-mount.mkdir"
|
||||||
|
"X-mount.subdir=@nix"
|
||||||
|
"relatime"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
fileSystems."/.snapshots" = {
|
||||||
|
device = PRIMARY;
|
||||||
|
fsType = "bcachefs";
|
||||||
|
options = [
|
||||||
|
"X-mount.mkdir"
|
||||||
|
"X-mount.subdir=@snapshots"
|
||||||
|
"relatime"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
# (optional) for preservation/impermanence
|
||||||
|
fileSystems."/persist" = {
|
||||||
|
device = PRIMARY;
|
||||||
|
fsType = "bcachefs";
|
||||||
|
options = [
|
||||||
|
"X-mount.mkdir"
|
||||||
|
"X-mount.subdir=@persist"
|
||||||
|
];
|
||||||
|
neededForBoot = true; # NOTE for impermanence only
|
||||||
|
};
|
||||||
|
}
|
||||||
|
# SWAP (optional)
|
||||||
|
{ swapDevices = [ { device = SWAP; } ]; }
|
||||||
|
{
|
||||||
|
# Impermanence fix
|
||||||
|
boot.initrd.systemd.services.create-needed-for-boot-dirs = {
|
||||||
|
after = [
|
||||||
|
"bcachefs-reset-root.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"bcachefs-reset-root.service"
|
||||||
|
];
|
||||||
|
serviceConfig.KeyringMode = "shared";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
# Reset root for erase your darlings/impermanence/preservation
|
||||||
|
(lib.mkIf true {
|
||||||
|
boot.initrd.systemd.services.bcachefs-reset-root = {
|
||||||
|
description = "Reset bcachefs root subvolume before pivot";
|
||||||
|
|
||||||
|
after = [
|
||||||
|
"initrd-root-device.target"
|
||||||
|
"cryptsetup.target"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
primaryDeviceUnit
|
||||||
|
];
|
||||||
|
|
||||||
|
before = [
|
||||||
|
"sysroot.mount"
|
||||||
|
];
|
||||||
|
wantedBy = [
|
||||||
|
"initrd-root-fs.target"
|
||||||
|
"sysroot.mount"
|
||||||
|
"initrd.target"
|
||||||
|
];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
KeyringMode = "shared";
|
||||||
|
};
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
cleanup() {
|
||||||
|
if [[ ! -e /primary_tmp/@root ]]; then
|
||||||
|
echo "Cleanup: Creating new @root"
|
||||||
|
bcachefs subvolume create /primary_tmp/@root
|
||||||
|
fi
|
||||||
|
echo "Cleanup: Unmounting /primary_tmp"
|
||||||
|
umount /primary_tmp || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
mkdir -p /primary_tmp
|
||||||
|
|
||||||
|
echo "Mounting ${PRIMARY}..."
|
||||||
|
if ! mount "${PRIMARY}" /primary_tmp; then
|
||||||
|
echo "Mount failed. Cannot reset root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e /primary_tmp/@root ]]; then
|
||||||
|
mkdir -p /primary_tmp/@snapshots/old_roots
|
||||||
|
|
||||||
|
# Use safe timestamp format (dashes instead of colons)
|
||||||
|
timestamp=$(date "+%Y-%m-%d_%H-%M-%S")
|
||||||
|
snap="/primary_tmp/@snapshots/old_roots/$timestamp"
|
||||||
|
echo "Snapshotting @root to $snap"
|
||||||
|
bcachefs subvolume snapshot /primary_tmp/@root "$snap"
|
||||||
|
|
||||||
|
echo "Deleting current @root"
|
||||||
|
bcachefs subvolume delete /primary_tmp/@root
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Trap handles creating new root and unmount
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
})
|
||||||
|
]
|
||||||
76
hosts/h002/impermanence-tools.nix
Normal file
76
hosts/h002/impermanence-tools.nix
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.impermanence.tools;
|
||||||
|
|
||||||
|
bcacheImpermanenceBin = pkgs.writeShellScriptBin "bcache-impermanence" (
|
||||||
|
builtins.readFile ./impermanence-tools.sh
|
||||||
|
);
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.impermanence.tools = {
|
||||||
|
snapshotRoot = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "/.snapshots/old_roots";
|
||||||
|
description = "Root directory containing old root snapshots.";
|
||||||
|
};
|
||||||
|
|
||||||
|
gc = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable garbage collection of old root snapshots.";
|
||||||
|
};
|
||||||
|
|
||||||
|
keepPerMonth = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 1;
|
||||||
|
description = "Keep at least this many snapshots per calendar month (latest ones).";
|
||||||
|
};
|
||||||
|
|
||||||
|
keepRecentWeeks = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 4;
|
||||||
|
description = "Keep at least one snapshot per ISO week within this many recent weeks.";
|
||||||
|
};
|
||||||
|
|
||||||
|
keepRecentCount = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 5;
|
||||||
|
description = "Always keep at least this many most recent snapshots overall.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
environment.systemPackages = [
|
||||||
|
bcacheImpermanenceBin
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.findutils
|
||||||
|
pkgs.diffutils
|
||||||
|
pkgs.bcachefs-tools
|
||||||
|
pkgs.fzf
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services."bcache-impermanence-gc" = lib.mkIf cfg.gc.enable {
|
||||||
|
description = "Garbage collect bcachefs impermanence snapshots";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
exec ${bcacheImpermanenceBin}/bin/bcache-impermanence gc \
|
||||||
|
--snapshot-root ${cfg.snapshotRoot} \
|
||||||
|
--keep-per-month ${toString cfg.gc.keepPerMonth} \
|
||||||
|
--keep-recent-weeks ${toString cfg.gc.keepRecentWeeks} \
|
||||||
|
--keep-recent-count ${toString cfg.gc.keepRecentCount}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
625
hosts/h002/impermanence-tools.sh
Normal file
625
hosts/h002/impermanence-tools.sh
Normal file
|
|
@ -0,0 +1,625 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SNAPSHOT_ROOT="/.snapshots/old_roots"
|
||||||
|
KEEP_PER_MONTH=1
|
||||||
|
KEEP_RECENT_WEEKS=4
|
||||||
|
KEEP_RECENT_COUNT=5
|
||||||
|
DRY_RUN=0
|
||||||
|
DIFF_MAX_DEPTH=0
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
bcache-impermanence - tools for managing impermanence snapshots
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
bcache-impermanence gc [--snapshot-root DIR] [--keep-per-month N] [--keep-recent-weeks N] [--keep-recent-count N] [--dry-run]
|
||||||
|
bcache-impermanence ls [-nN] [--snapshot-root DIR]
|
||||||
|
bcache-impermanence diff [-s SNAPSHOT] [--snapshot-root DIR] [--max-depth N] [PATH_PREFIX...]
|
||||||
|
|
||||||
|
Subcommands:
|
||||||
|
gc Run garbage collection on old root snapshots.
|
||||||
|
ls List snapshots (newest first). With -nN prints N latest.
|
||||||
|
diff Browse and diff files/dirs between current system and a snapshot.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--snapshot-root DIR Override snapshot root directory (default: /.snapshots/old_roots).
|
||||||
|
--keep-per-month N For gc: keep at least N snapshots per calendar month.
|
||||||
|
--keep-recent-weeks N For gc: keep at least one snapshot per ISO week within the last N weeks.
|
||||||
|
--keep-recent-count N For gc: always keep at least N most recent snapshots overall.
|
||||||
|
--dry-run For gc: show what would be deleted.
|
||||||
|
--max-depth N For diff: limit scan depth under each prefix.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
list_snapshots_desc() {
|
||||||
|
if [ ! -d "$SNAPSHOT_ROOT" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
for entry in "$SNAPSHOT_ROOT"/*; do
|
||||||
|
[ -d "$entry" ] || continue
|
||||||
|
basename "$entry"
|
||||||
|
done | sort -r
|
||||||
|
}
|
||||||
|
|
||||||
|
latest_snapshot_name() {
|
||||||
|
list_snapshots_desc | head -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_ls() {
|
||||||
|
local count=0
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-n*)
|
||||||
|
# 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)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "--snapshot-root requires a value" >&2; exit 1; }
|
||||||
|
SNAPSHOT_ROOT="$1"
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
echo "Usage: bcache-impermanence ls [-nN] [--snapshot-root DIR]" >&2
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown ls option: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
local snaps
|
||||||
|
snaps=$(list_snapshots_desc)
|
||||||
|
|
||||||
|
if [ -z "$snaps" ]; then
|
||||||
|
echo "No snapshots found in $SNAPSHOT_ROOT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$count" -gt 0 ] 2>/dev/null; then
|
||||||
|
printf '%s\n' "$snaps" | head -n "$count"
|
||||||
|
else
|
||||||
|
printf '%s\n' "$snaps"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build_keep_set() {
|
||||||
|
# Prints snapshot names to keep, one per line, based on policies.
|
||||||
|
local now
|
||||||
|
now=$(date +%s)
|
||||||
|
|
||||||
|
local snaps
|
||||||
|
snaps=$(list_snapshots_desc)
|
||||||
|
|
||||||
|
if [ -z "$snaps" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tmpdir
|
||||||
|
tmpdir=$(mktemp -d)
|
||||||
|
|
||||||
|
# Always keep newest KEEP_RECENT_COUNT snapshots.
|
||||||
|
if [ "$KEEP_RECENT_COUNT" -gt 0 ]; then
|
||||||
|
printf '%s\n' "$snaps" | head -n "$KEEP_RECENT_COUNT" >"$tmpdir/keep_recent"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Per-month: keep up to KEEP_PER_MONTH newest per month.
|
||||||
|
if [ "$KEEP_PER_MONTH" -gt 0 ]; then
|
||||||
|
while read -r snap; do
|
||||||
|
[ -n "$snap" ] || continue
|
||||||
|
local month
|
||||||
|
month=${snap%_*} # YYYY-MM-DD
|
||||||
|
month=${month%-*} # YYYY-MM
|
||||||
|
local month_file="$tmpdir/month_$month"
|
||||||
|
local count=0
|
||||||
|
if [ -f "$month_file" ]; then
|
||||||
|
count=$(wc -l <"$month_file")
|
||||||
|
fi
|
||||||
|
if [ "$count" -lt "$KEEP_PER_MONTH" ]; then
|
||||||
|
echo "$snap" >>"$month_file"
|
||||||
|
fi
|
||||||
|
done <<EOF_SNAPS
|
||||||
|
$snaps
|
||||||
|
EOF_SNAPS
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Recent weeks: keep latest snapshot per week within last KEEP_RECENT_WEEKS weeks.
|
||||||
|
if [ "$KEEP_RECENT_WEEKS" -gt 0 ]; then
|
||||||
|
local max_age
|
||||||
|
max_age=$(( KEEP_RECENT_WEEKS * 7 * 24 * 3600 ))
|
||||||
|
while read -r snap; do
|
||||||
|
[ -n "$snap" ] || continue
|
||||||
|
local ts
|
||||||
|
ts=$(date -d "${snap%_*} ${snap#*_}" +%s 2>/dev/null || true)
|
||||||
|
[ -n "$ts" ] || continue
|
||||||
|
local age
|
||||||
|
age=$(( now - ts ))
|
||||||
|
if [ "$age" -gt "$max_age" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
local week
|
||||||
|
week=$(date -d "${snap%_*} ${snap#*_}" +"%G-%V" 2>/dev/null || true)
|
||||||
|
[ -n "$week" ] || continue
|
||||||
|
local week_file="$tmpdir/week_$week"
|
||||||
|
if [ ! -f "$week_file" ]; then
|
||||||
|
echo "$snap" >"$week_file"
|
||||||
|
fi
|
||||||
|
done <<EOF_SNAPS2
|
||||||
|
$snaps
|
||||||
|
EOF_SNAPS2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Aggregate and dedupe.
|
||||||
|
for f in "$tmpdir"/*; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
cat "$f"
|
||||||
|
done | sort -u
|
||||||
|
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_gc() {
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--snapshot-root)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "--snapshot-root requires a value" >&2; exit 1; }
|
||||||
|
SNAPSHOT_ROOT="$1"
|
||||||
|
;;
|
||||||
|
--keep-per-month)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "--keep-per-month requires a value" >&2; exit 1; }
|
||||||
|
KEEP_PER_MONTH="$1"
|
||||||
|
;;
|
||||||
|
--keep-recent-weeks)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "--keep-recent-weeks requires a value" >&2; exit 1; }
|
||||||
|
KEEP_RECENT_WEEKS="$1"
|
||||||
|
;;
|
||||||
|
--keep-recent-count)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "--keep-recent-count requires a value" >&2; exit 1; }
|
||||||
|
KEEP_RECENT_COUNT="$1"
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=1
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
echo "Usage: bcache-impermanence gc [--snapshot-root DIR] [--keep-per-month N] [--keep-recent-weeks N] [--keep-recent-count N] [--dry-run]" >&2
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown gc option: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -d "$SNAPSHOT_ROOT" ]; then
|
||||||
|
echo "Snapshot root $SNAPSHOT_ROOT does not exist; nothing to do" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local snaps
|
||||||
|
snaps=$(list_snapshots_desc)
|
||||||
|
if [ -z "$snaps" ]; then
|
||||||
|
echo "No snapshots to process" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local keep
|
||||||
|
keep=$(build_keep_set)
|
||||||
|
|
||||||
|
local tmpkeep
|
||||||
|
tmpkeep=$(mktemp -d)
|
||||||
|
while read -r k; do
|
||||||
|
[ -n "$k" ] || continue
|
||||||
|
: >"$tmpkeep/$k"
|
||||||
|
done <<EOF_KEEP
|
||||||
|
$keep
|
||||||
|
EOF_KEEP
|
||||||
|
|
||||||
|
local deleted=0
|
||||||
|
while read -r snap; do
|
||||||
|
[ -n "$snap" ] || continue
|
||||||
|
if [ -f "$tmpkeep/$snap" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
local full
|
||||||
|
full="$SNAPSHOT_ROOT/$snap"
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
echo "[dry-run] Would delete $full"
|
||||||
|
else
|
||||||
|
echo "Deleting snapshot $full"
|
||||||
|
if ! bcachefs subvolume delete "$full"; then
|
||||||
|
echo "Failed to delete $full" >&2
|
||||||
|
else
|
||||||
|
deleted=$((deleted + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<EOF_SNAPS
|
||||||
|
$snaps
|
||||||
|
EOF_SNAPS
|
||||||
|
|
||||||
|
rm -rf "$tmpkeep"
|
||||||
|
echo "GC complete; deleted $deleted snapshots"
|
||||||
|
}
|
||||||
|
|
||||||
|
browse_diff_tree() {
|
||||||
|
local snapshot_name snapshot_dir diff_list initial_prefix
|
||||||
|
snapshot_name="$1"
|
||||||
|
snapshot_dir="$2"
|
||||||
|
diff_list="$3"
|
||||||
|
initial_prefix="${4-}"
|
||||||
|
|
||||||
|
local current_prefix=""
|
||||||
|
if [ -n "$initial_prefix" ]; then
|
||||||
|
current_prefix="$initial_prefix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
local children
|
||||||
|
children=$(mktemp)
|
||||||
|
local seen
|
||||||
|
seen=$(mktemp)
|
||||||
|
|
||||||
|
# Optional parent entry
|
||||||
|
if [ -n "$current_prefix" ]; then
|
||||||
|
echo "dir .." >"$children"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build immediate children under current_prefix.
|
||||||
|
while read -r st rel; do
|
||||||
|
[ -n "$rel" ] || continue
|
||||||
|
local sub
|
||||||
|
if [ -n "$current_prefix" ]; then
|
||||||
|
case "$rel" in
|
||||||
|
"$current_prefix")
|
||||||
|
# Exact match at this level; treat as leaf, not a separate child.
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
"$current_prefix"/*)
|
||||||
|
sub="${rel#"$current_prefix"/}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
sub="$rel"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "$sub" ] || continue
|
||||||
|
|
||||||
|
local child
|
||||||
|
child="${sub%%/*}"
|
||||||
|
local child_rel
|
||||||
|
if [ -n "$current_prefix" ]; then
|
||||||
|
child_rel="$current_prefix/$child"
|
||||||
|
else
|
||||||
|
child_rel="$child"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -qx "$child_rel" "$seen" 2>/dev/null; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$st $child_rel" >>"$children"
|
||||||
|
echo "$child_rel" >>"$seen"
|
||||||
|
done <"$diff_list"
|
||||||
|
|
||||||
|
rm -f "$seen"
|
||||||
|
|
||||||
|
if [ ! -s "$children" ]; then
|
||||||
|
echo "No further differences under ${current_prefix:-/}" >&2
|
||||||
|
rm -f "$children"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v fzf >/dev/null 2>&1; then
|
||||||
|
echo "fzf is required for diff browsing" >&2
|
||||||
|
rm -f "$children"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
local preview_cmd
|
||||||
|
preview_cmd='sel_rel=$(printf "%s\n" {} | cut -d" " -f2-)
|
||||||
|
if [ "$sel_rel" = ".." ]; then
|
||||||
|
echo "[UP] .."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
snap_dir="'"$snapshot_dir"'"
|
||||||
|
a_path="$snap_dir/$sel_rel"
|
||||||
|
b_path="/$sel_rel"
|
||||||
|
|
||||||
|
if [ ! -e "$a_path" ] && [ -e "$b_path" ]; then
|
||||||
|
echo "[ADDED] /$sel_rel"; echo
|
||||||
|
if [ -d "$b_path" ]; then
|
||||||
|
(cd / && find "$sel_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
|
||||||
|
elif [ -e "$a_path" ] && [ ! -e "$b_path" ]; then
|
||||||
|
echo "[REMOVED] /$sel_rel"; echo
|
||||||
|
if [ -d "$a_path" ]; then
|
||||||
|
(cd "$snap_dir" && find "$sel_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
|
||||||
|
else
|
||||||
|
if [ -d "$a_path" ] && [ -d "$b_path" ]; then
|
||||||
|
echo "[CHANGED DIR] /$sel_rel"; echo
|
||||||
|
diff -ru "$a_path" "$b_path" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo "[CHANGED] /$sel_rel"; echo
|
||||||
|
diff -u "$a_path" "$b_path" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
local selection
|
||||||
|
selection=$(FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS:-} --ansi --preview-window=right:70%:wrap" \
|
||||||
|
fzf --prompt="[bcache-impermanence diff ${current_prefix:-/}] " \
|
||||||
|
--preview "$preview_cmd" <"$children") || {
|
||||||
|
rm -f "$children"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -f "$children"
|
||||||
|
|
||||||
|
local sel_rel
|
||||||
|
sel_rel=$(printf "%s\n" "$selection" | cut -d" " -f2-)
|
||||||
|
|
||||||
|
if [ "$sel_rel" = ".." ]; then
|
||||||
|
if [ -z "$current_prefix" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if printf "%s" "$current_prefix" | grep -q '/'; then
|
||||||
|
current_prefix="${current_prefix%/*}"
|
||||||
|
else
|
||||||
|
current_prefix=""
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If this selection has descendants, drill down; otherwise treat as leaf and exit.
|
||||||
|
if grep -q " $sel_rel/" "$diff_list"; then
|
||||||
|
current_prefix="$sel_rel"
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
# Leaf: user already saw diff in preview; exit.
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_diff() {
|
||||||
|
local snapshot_name=""
|
||||||
|
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-s)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "-s requires a snapshot name" >&2; exit 1; }
|
||||||
|
snapshot_name="$1"
|
||||||
|
;;
|
||||||
|
--snapshot-root)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "--snapshot-root requires a value" >&2; exit 1; }
|
||||||
|
SNAPSHOT_ROOT="$1"
|
||||||
|
;;
|
||||||
|
--max-depth)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "--max-depth requires a value" >&2; exit 1; }
|
||||||
|
DIFF_MAX_DEPTH="$1"
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
echo "Usage: bcache-impermanence diff [-s SNAPSHOT] [--snapshot-root DIR] [--max-depth N] [PATH_PREFIX...]" >&2
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
echo "Unknown diff option: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$snapshot_name" ]; then
|
||||||
|
snapshot_name=$(latest_snapshot_name || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$snapshot_name" ]; then
|
||||||
|
echo "No snapshots found for diff" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local snapshot_dir
|
||||||
|
snapshot_dir="$SNAPSHOT_ROOT/$snapshot_name"
|
||||||
|
|
||||||
|
if [ ! -d "$snapshot_dir" ]; then
|
||||||
|
echo "Snapshot directory $snapshot_dir does not exist" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
set -- /
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v fzf >/dev/null 2>&1; then
|
||||||
|
echo "fzf is required for diff browsing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build list of bind mounts backed by /persist so we can filter them out.
|
||||||
|
local persist_mounts
|
||||||
|
persist_mounts=$(awk '$2 ~ "^/persist(/|$)" { print $2 }' /proc/self/mounts || true)
|
||||||
|
|
||||||
|
is_persist_backed() {
|
||||||
|
local p
|
||||||
|
p="$1"
|
||||||
|
if [ -z "$p" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$persist_mounts" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local m
|
||||||
|
for m in $persist_mounts; do
|
||||||
|
case "$p" in
|
||||||
|
"$m"|"$m"/*) return 0 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local prefixes
|
||||||
|
prefixes=("$@")
|
||||||
|
|
||||||
|
local tmp
|
||||||
|
tmp=$(mktemp)
|
||||||
|
|
||||||
|
for prefix in "${prefixes[@]}"; do
|
||||||
|
case "$prefix" in
|
||||||
|
/*) : ;;
|
||||||
|
*)
|
||||||
|
echo "Path prefix must be absolute: $prefix" >&2
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Skip prefixes that are themselves backed by /persist.
|
||||||
|
if is_persist_backed "$prefix"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
local rel
|
||||||
|
rel="${prefix#/}"
|
||||||
|
[ -z "$rel" ] && rel="."
|
||||||
|
|
||||||
|
if [ "$DIFF_MAX_DEPTH" -gt 0 ] 2>/dev/null; then
|
||||||
|
(
|
||||||
|
cd "$snapshot_dir" && find "$rel" -mindepth 1 -maxdepth "$DIFF_MAX_DEPTH" -print 2>/dev/null || true
|
||||||
|
) | sed "s/^/A /" >>"$tmp"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd / && find "$rel" -mindepth 1 -maxdepth "$DIFF_MAX_DEPTH" -print 2>/dev/null || true
|
||||||
|
) | sed "s/^/B /" >>"$tmp"
|
||||||
|
else
|
||||||
|
(
|
||||||
|
cd "$snapshot_dir" && find "$rel" -mindepth 1 -print 2>/dev/null || true
|
||||||
|
) | sed "s/^/A /" >>"$tmp"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd / && find "$rel" -mindepth 1 -print 2>/dev/null || true
|
||||||
|
) | sed "s/^/B /" >>"$tmp"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
# Skip paths that reside under a /persist-backed mount in the live system.
|
||||||
|
if is_persist_backed "$b_path"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
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>/dev/null; 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
|
||||||
|
|
||||||
|
local initial_prefix=""
|
||||||
|
if [ "$#" -eq 1 ]; then
|
||||||
|
initial_prefix="${1#/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
browse_diff_tree "$snapshot_name" "$snapshot_dir" "$diff_list" "$initial_prefix"
|
||||||
|
rm -f "$diff_list"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if [ "$#" -lt 1 ]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local cmd
|
||||||
|
cmd="$1"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
case "$cmd" in
|
||||||
|
gc)
|
||||||
|
cmd_gc "$@"
|
||||||
|
;;
|
||||||
|
ls)
|
||||||
|
cmd_ls "$@"
|
||||||
|
;;
|
||||||
|
diff)
|
||||||
|
cmd_diff "$@"
|
||||||
|
;;
|
||||||
|
--help|-h|help)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown subcommand: $cmd" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
44
hosts/h002/impermanence.nix
Normal file
44
hosts/h002/impermanence.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
environment.persistence."/persist" = {
|
||||||
|
enable = true;
|
||||||
|
hideMounts = true;
|
||||||
|
directories = [
|
||||||
|
"/var/log"
|
||||||
|
"/var/lib/nixos"
|
||||||
|
"/var/lib/systemd/coredump"
|
||||||
|
"/var/lib/systemd/timers"
|
||||||
|
|
||||||
|
"/etc/nixos"
|
||||||
|
"/etc/ssh"
|
||||||
|
|
||||||
|
"/etc/NetworkManager/system-connections"
|
||||||
|
"/var/lib/bluetooth"
|
||||||
|
"/var/lib/NetworkManager"
|
||||||
|
"/var/lib/iwd"
|
||||||
|
"/var/lib/fail2ban"
|
||||||
|
];
|
||||||
|
files = [
|
||||||
|
"/etc/machine-id"
|
||||||
|
];
|
||||||
|
users.luser = {
|
||||||
|
directories = [
|
||||||
|
".ssh"
|
||||||
|
".gnupg"
|
||||||
|
|
||||||
|
".config/nixos-config"
|
||||||
|
|
||||||
|
".config/atuin"
|
||||||
|
".local/share/atuin"
|
||||||
|
|
||||||
|
".local/share/zoxide"
|
||||||
|
|
||||||
|
# neovim ros_neovim
|
||||||
|
".local/state/nvim_ringofstorms_helium"
|
||||||
|
];
|
||||||
|
files = [
|
||||||
|
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -30,7 +30,6 @@
|
||||||
nixosConfigurations = {
|
nixosConfigurations = {
|
||||||
"${configurationName}" = (
|
"${configurationName}" = (
|
||||||
lib.nixosSystem {
|
lib.nixosSystem {
|
||||||
inherit system;
|
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit inputs;
|
inherit inputs;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ lib.mkMerge [
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
# Impermanence fix
|
# Impermanence fix for working with custom unlock and reset with root bcache
|
||||||
boot.initrd.systemd.services.create-needed-for-boot-dirs = {
|
boot.initrd.systemd.services.create-needed-for-boot-dirs = {
|
||||||
after = [
|
after = [
|
||||||
"unlock-bcachefs-custom.service"
|
"unlock-bcachefs-custom.service"
|
||||||
|
|
@ -169,29 +169,13 @@ lib.mkMerge [
|
||||||
primaryDeviceUnit
|
primaryDeviceUnit
|
||||||
];
|
];
|
||||||
|
|
||||||
# unitConfig = {
|
|
||||||
# # Ensure this service doesn't time out if USB detection takes a while
|
|
||||||
# DefaultDependencies = "no";
|
|
||||||
# };
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
RemainAfterExit = true;
|
RemainAfterExit = true;
|
||||||
KeyringMode = "shared"; # TODO so it shares with reset root below, not needed otherwise
|
# TODO so it shares with reset root below, not needed otherwise
|
||||||
|
KeyringMode = "shared";
|
||||||
};
|
};
|
||||||
|
|
||||||
# script = ''
|
|
||||||
# echo "Using USB key for bcachefs unlock: ${USB_KEY}"
|
|
||||||
#
|
|
||||||
# # only try mount if the node exists
|
|
||||||
# if [ ! -e "${USB_KEY}" ]; then
|
|
||||||
# echo "USB key device ${USB_KEY} not present in initrd"
|
|
||||||
# exit 1
|
|
||||||
# fi
|
|
||||||
#
|
|
||||||
# ${pkgs.bcachefs-tools}/bin/bcachefs unlock -f /usb_key/key "${PRIMARY}"
|
|
||||||
# echo "bcachefs unlock successful for ${PRIMARY}"
|
|
||||||
# '';
|
|
||||||
|
|
||||||
script = ''
|
script = ''
|
||||||
echo "Searching for USB Unlock Key..."
|
echo "Searching for USB Unlock Key..."
|
||||||
KEY_FOUND=0
|
KEY_FOUND=0
|
||||||
|
|
@ -236,7 +220,6 @@ lib.mkMerge [
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO rotate root
|
|
||||||
}
|
}
|
||||||
# Reset root for erase your darlings/impermanence/preservation
|
# Reset root for erase your darlings/impermanence/preservation
|
||||||
(lib.mkIf true {
|
(lib.mkIf true {
|
||||||
|
|
@ -266,11 +249,6 @@ lib.mkMerge [
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
RemainAfterExit = true;
|
RemainAfterExit = true;
|
||||||
KeyringMode = "shared";
|
KeyringMode = "shared";
|
||||||
# Environment = "PATH=${
|
|
||||||
# lib.makeBinPath [
|
|
||||||
# # pkgs.coreutils
|
|
||||||
# ]
|
|
||||||
# }:/bin:/sbin";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
script = ''
|
script = ''
|
||||||
|
|
@ -286,7 +264,6 @@ lib.mkMerge [
|
||||||
|
|
||||||
mkdir -p /primary_tmp
|
mkdir -p /primary_tmp
|
||||||
|
|
||||||
# If unlocked, mounts instantly. If locked, prompts for password on TTY.
|
|
||||||
echo "Mounting ${PRIMARY}..."
|
echo "Mounting ${PRIMARY}..."
|
||||||
if ! mount "${PRIMARY}" /primary_tmp; then
|
if ! mount "${PRIMARY}" /primary_tmp; then
|
||||||
echo "Mount failed. Cannot reset root."
|
echo "Mount failed. Cannot reset root."
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@
|
||||||
fastfetch
|
fastfetch
|
||||||
fzf
|
fzf
|
||||||
|
|
||||||
|
dmidecode # motherboard info
|
||||||
|
lshw # hardware info
|
||||||
|
sysbench # testing tools
|
||||||
|
|
||||||
# bcachefs
|
# bcachefs
|
||||||
# Required as a workaround for bug
|
# Required as a workaround for bug
|
||||||
# https://github.com/NixOS/nixpkgs/issues/32279
|
# https://github.com/NixOS/nixpkgs/issues/32279
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,14 @@ parted /dev/$DEVICE -- mkpart PRIMARY 2GB 100%
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
BOOT=sda1
|
BOOT=sda1
|
||||||
mkfs.fat -F 32 -n BOOT /dev/$BOOT
|
|
||||||
PRIMARY=sda2
|
PRIMARY=sda2
|
||||||
|
SWAP=sda3
|
||||||
|
|
||||||
|
mkfs.fat -F 32 -n BOOT /dev/$BOOT
|
||||||
|
|
||||||
bcachefs format --label=nixos --encrypted /dev/$PRIMARY
|
bcachefs format --label=nixos --encrypted /dev/$PRIMARY
|
||||||
bcachefs unlock /dev/$PRIMARY
|
bcachefs unlock /dev/$PRIMARY
|
||||||
SWAP=sda3
|
|
||||||
mkswap /dev/$SWAP
|
mkswap /dev/$SWAP
|
||||||
swapon /dev/$SWAP
|
swapon /dev/$SWAP
|
||||||
```
|
```
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue