Compare commits
14
Commits
5d0a33d09f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a6830c24e
|
||
|
|
e0912f568c
|
||
|
|
cb01efd267
|
||
|
|
0f7f1dade2
|
||
|
|
bbb468c432
|
||
|
|
24b940810e
|
||
|
|
d4f326bfd9
|
||
|
|
93a4a934eb
|
||
|
|
61300bd9b9
|
||
|
|
38556c8839
|
||
|
|
102fb3c3c4
|
||
|
|
752ee84430
|
||
|
|
fbf68fa4a4
|
||
|
|
b9e8641bbf
|
@@ -101,6 +101,29 @@ this repo pins, not the reverse.
|
||||
Your machine list, hostnames, and any private specifics stay in *your* flake — you pull
|
||||
only the generic modules from here.
|
||||
|
||||
## Updating (day-to-day)
|
||||
|
||||
**Every** package version comes from the `nixpkgs` **you** declare — including the packages
|
||||
this repo's own modules install, since they build against your `nixpkgs` via `follows`, not
|
||||
against anything this repo pins. So the whole upgrade happens in your flake; you never touch
|
||||
this repo to move packages, even the ones it declares:
|
||||
|
||||
```sh
|
||||
nix flake update # bump every input to newest
|
||||
sudo nixos-rebuild switch --flake .#myhost # build + activate
|
||||
```
|
||||
|
||||
`nix flake update` advances `nixpkgs` to the newest commit on the branch you pinned (e.g.
|
||||
`nixos-unstable` or `nixos-25.11`) — that is what upgrades your packages. `home-manager` and
|
||||
`baseline` **follow** your `nixpkgs`, so they move in lockstep with no version skew. To move
|
||||
one input at a time, name it: `nix flake update nixpkgs` bumps only packages;
|
||||
`nix flake update baseline` pulls the latest **modules** from this repo (new features/fixes)
|
||||
without changing any package version. These are independent knobs — routine upgrades need
|
||||
only the `nixpkgs` bump.
|
||||
|
||||
A **release jump** (e.g. `nixos-25.11` → `nixos-26.05`) is not a plain update: edit the
|
||||
branch in the `nixpkgs` and `home-manager` URLs in your `flake.nix`, then update and rebuild.
|
||||
|
||||
## Overriding the baseline
|
||||
|
||||
The public modules are a baseline you extend without forking. Three ways to shape them
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Hunspell dictionaries, joined into one directory so anything needing a
|
||||
# dictionary *path* can be pointed at a single store path.
|
||||
#
|
||||
# Two consumers, two mechanisms:
|
||||
# - LibreOffice needs nothing beyond the package being installed: its wrapper
|
||||
# scans `share/hunspell` in every NIX_PROFILES entry.
|
||||
# - Gecko apps (LibreWolf, Thunderbird) need `spellchecker.dictionary_path`
|
||||
# set explicitly — there is no /usr/share/hunspell on NixOS and wrapFirefox
|
||||
# wires up nothing itself. Point that pref at `local.dictionaries.path`.
|
||||
#
|
||||
# Chromium-based browsers are NOT served by this: they ignore hunspell and
|
||||
# fetch their own dictionaries, selected by enterprise policy.
|
||||
#
|
||||
# Which languages is a personal choice, so the default is empty; a consumer sets
|
||||
# `local.dictionaries.packages = [ pkgs.hunspellDicts.de_DE ];`. en-US ships
|
||||
# inside Firefox/Thunderbird already and does not need listing for those.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.local.dictionaries;
|
||||
|
||||
joined = pkgs.symlinkJoin {
|
||||
name = "hunspell-dicts";
|
||||
paths = cfg.packages;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.local.dictionaries = {
|
||||
packages = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = [ ];
|
||||
example = lib.literalExpression "[ pkgs.hunspellDicts.de_DE ]";
|
||||
description = ''
|
||||
Hunspell dictionary packages to install and expose via
|
||||
`local.dictionaries.path`.
|
||||
'';
|
||||
};
|
||||
|
||||
path = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
Directory holding every `packages` dictionary. Feed this to
|
||||
`spellchecker.dictionary_path` in Gecko apps.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
local.dictionaries.path = "${joined}/share/hunspell";
|
||||
home.packages = lib.optional (cfg.packages != [ ]) joined;
|
||||
};
|
||||
}
|
||||
+8
-3
@@ -2,7 +2,7 @@
|
||||
# signing identity — those are personal (→ inventory). Enabling this alone is
|
||||
# harmless: a user with no key just has an idle agent.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
programs.gpg = {
|
||||
@@ -26,7 +26,12 @@
|
||||
# sk-based ssh keys use the normal ssh-agent; don't let gpg-agent hijack
|
||||
# SSH_AUTH_SOCK.
|
||||
enableSshSupport = false;
|
||||
defaultCacheTtl = 1800; # 30 min
|
||||
maxCacheTtl = 7200; # 2 h
|
||||
# Idle timer, reset on every use. The hard cap runs from the unlock and
|
||||
# never resets, so a machine in continuous use still re-asks once a workday —
|
||||
# otherwise an unattended unlocked session signs forever.
|
||||
# mkDefault so an inventory can dial these per machine with a plain
|
||||
# `services.gpg-agent.defaultCacheTtl = …;` and no mkForce.
|
||||
defaultCacheTtl = lib.mkDefault 3600; # 1 h idle
|
||||
maxCacheTtl = lib.mkDefault 28800; # 8 h absolute
|
||||
};
|
||||
}
|
||||
|
||||
+13
-14
@@ -115,14 +115,17 @@ let
|
||||
| "keyword monitor \($l.name),preferred,0x0,\($l.scale)"
|
||||
+ " ; keyword monitor \($r.name),preferred,\($off)x0,\($r.scale)"')
|
||||
"$hc" --batch "$cmd"
|
||||
# re-anchor the layer-shell clients onto the new geometry (see comment above)
|
||||
${pkgs.procps}/bin/pkill -USR2 -x .waybar-wrapped || true
|
||||
# re-anchor the layer-shell clients onto the new geometry (see comment above).
|
||||
# Anchored ERE covers both comms: the wrapper-launched bar (comm
|
||||
# `.waybar-wrapped`) and one the monitor-hotplug handler relaunched via
|
||||
# `hyprctl dispatch exec waybar` (comm `waybar`).
|
||||
${pkgs.procps}/bin/pkill -USR2 -x '\.waybar-wrapped|waybar' || true
|
||||
bg="$HOME/.local/share/bg"
|
||||
[ -e "$bg" ] && ${pkgs.awww}/bin/awww img "$bg" || true
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ./nvim.nix ];
|
||||
imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ./nvim.nix ./dictionaries.nix ];
|
||||
|
||||
# Put ~/.local/bin (the XDG user-bin dir) on PATH so hand-dropped scripts run
|
||||
# by name without wiring each through nix.
|
||||
@@ -144,9 +147,8 @@ in
|
||||
# -p. Pins throwaway REPLs and new projects to one version across machines
|
||||
# (further steered by uv.toml below). Fetched on demand by uv, not Nix.
|
||||
UV_PYTHON = "3.14";
|
||||
# psql / wget / readline configs, XDG-routed (the rc files are declared
|
||||
# via xdg.configFile below).
|
||||
PSQLRC = "${config.xdg.configHome}/psql/rc";
|
||||
# wget / readline configs, XDG-routed (the rc files are declared via
|
||||
# xdg.configFile below).
|
||||
WGETRC = "${config.xdg.configHome}/wget/wgetrc";
|
||||
INPUTRC = "${config.xdg.configHome}/readline/inputrc";
|
||||
# Rust/cargo: keep registry cache, git checkouts and `cargo install` bins
|
||||
@@ -190,6 +192,9 @@ in
|
||||
unzip # extract .zip (tools that shell out to it expect the binary)
|
||||
jq # JSON on the command line (scripts, ad-hoc inspection)
|
||||
pv # pipe progress meter (throughput, ETA on streamed data)
|
||||
git-filter-repo # `git filter-repo`: history rewriting (upstream's replacement
|
||||
# for `git filter-branch`); a standalone script git picks
|
||||
# up as a subcommand, so programs.git needs no config
|
||||
libreoffice
|
||||
grim # screenshot capture (also used by the Print-key binds)
|
||||
slurp # interactive region selector for grim
|
||||
@@ -218,12 +223,6 @@ in
|
||||
python-preference = "managed"
|
||||
'';
|
||||
|
||||
# psql: unlimited, per-database history file (kept out of $HOME).
|
||||
"psql/rc".text = ''
|
||||
\set HISTFILE ${config.xdg.cacheHome}/psql/history-:DBNAME
|
||||
\set HISTSIZE -1
|
||||
'';
|
||||
|
||||
# wget: keep its HSTS db in the cache dir, not ~/.
|
||||
"wget/wgetrc".text = ''
|
||||
hsts-file=${config.xdg.cacheHome}/wget/hsts
|
||||
@@ -668,8 +667,8 @@ in
|
||||
# the ~/.local/share/bg symlink if it exists.
|
||||
"awww-daemon"
|
||||
"sh -c '[ -e \"$HOME/.local/share/bg\" ] && awww img \"$HOME/.local/share/bg\"'"
|
||||
# re-push the wallpaper onto monitors connected after login (media.nix)
|
||||
"wallpaper-hotplug"
|
||||
# re-push wallpaper + relaunch waybar on monitor add/remove (media.nix)
|
||||
"monitor-hotplug"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
+28
-8
@@ -7,7 +7,7 @@
|
||||
# awww-daemon and the boot-time restore from that symlink run as Hyprland
|
||||
# `exec-once` (see home.nix).
|
||||
|
||||
{ pkgs, ... }:
|
||||
{ pkgs, config, ... }:
|
||||
|
||||
let
|
||||
# setbg: persist the choice as an XDG-data symlink, then set it live. Startup
|
||||
@@ -21,15 +21,35 @@ let
|
||||
# awww-daemon paints only the outputs present when `awww img` last ran and does
|
||||
# not repaint a monitor hotplugged afterwards — it comes up on the fill color
|
||||
# (black), and a transparent foot on it then shows that black, not the
|
||||
# wallpaper. Watch Hyprland's event socket and re-push the wallpaper (setbg
|
||||
# with no arg → all current outputs) on each monitor-add. Hyprland emits both
|
||||
# `monitoradded` and `monitoraddedv2`; match only the former so setbg fires
|
||||
# once. Runs as a Hyprland exec-once (see home.nix).
|
||||
wallpaperHotplug = pkgs.writeShellScriptBin "wallpaper-hotplug" ''
|
||||
# wallpaper. Symmetric problem on real DP/HDMI unplug: Hyprland drops the
|
||||
# layer-shell surfaces on the surviving monitor and does not recreate them, so
|
||||
# its waybar and wallpaper vanish. Watch Hyprland's event socket and, on either
|
||||
# add or remove, re-push the wallpaper (setbg with no arg → all current
|
||||
# outputs) and fully relaunch waybar. A SIGUSR2 reload is not enough — it
|
||||
# rereads config but does not recreate a bar Hyprland already tore down — so
|
||||
# kill and restart, which enumerates the current outputs and draws a bar on
|
||||
# each. The event flaps (several add/remove lines per plug); the loop is
|
||||
# sequential so each pass converges to one waybar. Hyprland emits both
|
||||
# `monitoradded` and `monitoraddedv2`; match only the non-v2 forms so each side
|
||||
# fires once. Runs as a Hyprland exec-once (see home.nix).
|
||||
hyprctl = "${config.wayland.windowManager.hyprland.finalPackage}/bin/hyprctl";
|
||||
monitorHotplug = pkgs.writeShellScriptBin "monitor-hotplug" ''
|
||||
sock="''${XDG_RUNTIME_DIR}/hypr/''${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock"
|
||||
${pkgs.socat}/bin/socat -u UNIX-CONNECT:"$sock" - | while IFS= read -r line; do
|
||||
case "$line" in
|
||||
"monitoradded>>"*) setbg ;;
|
||||
"monitoradded>>"* | "monitorremoved>>"*)
|
||||
sleep 1 # let Hyprland settle the new geometry
|
||||
setbg # re-push wallpaper to all current outputs
|
||||
# Anchored ERE matching `comm` (max 15 chars): the bar launched via the
|
||||
# home-manager wrapper reports comm `.waybar-wrapped` (arg[0] is still
|
||||
# `waybar`), so a bare `-x waybar` never matches it and leaves a stale bar.
|
||||
wb='\.waybar-wrapped|waybar'
|
||||
${pkgs.procps}/bin/pkill -x "$wb" || true # relaunch waybar fresh (see comment)
|
||||
i=0
|
||||
while [ $i -lt 6 ] && ${pkgs.procps}/bin/pgrep -x "$wb" >/dev/null; do sleep 0.3; i=$((i+1)); done
|
||||
${pkgs.procps}/bin/pkill -9 -x "$wb" || true
|
||||
${hyprctl} dispatch exec waybar
|
||||
;;
|
||||
esac
|
||||
done
|
||||
'';
|
||||
@@ -66,7 +86,7 @@ in
|
||||
|
||||
home.packages = with pkgs; [
|
||||
setbg
|
||||
wallpaperHotplug # re-push wallpaper to a hotplugged monitor (see comment above)
|
||||
monitorHotplug # re-push wallpaper + reload waybar on monitor add/remove (see comment above)
|
||||
awww # wayland wallpaper daemon (setbg backend)
|
||||
wl-clipboard # wl-copy, for the yank binds
|
||||
imagemagick # `magick`: rotate/flip binds
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [ ./shutdown-debug.nix ./printing-scanning.nix ];
|
||||
imports = [ ./shutdown-debug.nix ./printing-scanning.nix ./rebuild-diff.nix ];
|
||||
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
# Deduplicate identical store paths via hardlinks.
|
||||
|
||||
+80
-9
@@ -1,8 +1,26 @@
|
||||
# Neovim, configured declaratively via nixvim. The editor — plugins, LSP,
|
||||
# completion, keymaps — is defined here and pinned by the flake lock; nothing is
|
||||
# downloaded at runtime, unlike an imperative distro (NvChad/LazyVim). The nixvim
|
||||
# home-manager module that provides `programs.nixvim` is imported for consumers by
|
||||
# `homeModules.default` in flake.nix, so this file only sets options.
|
||||
# downloaded at runtime. The nixvim home-manager module that provides
|
||||
# `programs.nixvim` is imported for consumers by `homeModules.default` in
|
||||
# flake.nix, so this file only sets options.
|
||||
#
|
||||
# Why this rather than a distro (LazyVim, NvChad, AstroNvim): those drive
|
||||
# lazy.nvim as a package manager, which git-clones plugins into
|
||||
# ~/.local/share/nvim on first launch and updates them in place. The editor's
|
||||
# actual contents would then live outside the flake — unpinned, unreproducible,
|
||||
# and invisible to a rollback.
|
||||
#
|
||||
# That choice has a price, and it is paid in ergonomics. A distro ships hundreds
|
||||
# of defaults other people already sanded smooth; this config does not. Rough
|
||||
# edges surface one at a time, mid-task, and each gets fixed by hand here (see
|
||||
# the `<leader>bd` and `<leader>Q` keymaps for two such fixes). Anyone extending
|
||||
# this should expect that cost rather than be surprised by it.
|
||||
#
|
||||
# The middle path stays open: the objection is to lazy.nvim as a *runtime*
|
||||
# package manager, not to a distro's ergonomics. Individual keymaps, option
|
||||
# sets, and plugin configurations can be lifted from LazyVim & co. into this
|
||||
# file, and nixvim installs any plugin set declaratively. Borrow freely — just
|
||||
# keep the installation declarative.
|
||||
#
|
||||
# Consumer knobs (the option-with-default contract): `local.nvim.enable` to opt
|
||||
# out wholesale, `local.nvim.colorscheme` to swap theme in one line, and
|
||||
@@ -86,8 +104,8 @@ in
|
||||
scrolloff = 8; # keep 8 lines of context around the cursor
|
||||
|
||||
expandtab = true; # spaces, not tabs
|
||||
shiftwidth = 2;
|
||||
tabstop = 2;
|
||||
shiftwidth = 4;
|
||||
tabstop = 4;
|
||||
smartindent = true;
|
||||
|
||||
ignorecase = true; # case-insensitive search…
|
||||
@@ -162,10 +180,31 @@ in
|
||||
blink-cmp = {
|
||||
enable = true;
|
||||
settings = {
|
||||
keymap.preset = "default"; # C-space open, C-y confirm, C-n/C-p move
|
||||
# "enter" preset: <CR> accepts, Up/Down and C-n/C-p move, Tab is
|
||||
# left to snippet jumping. Custom keys below merge over the preset.
|
||||
keymap = {
|
||||
preset = "enter";
|
||||
# Vim's own ins-completion confirm key, kept alongside <CR>.
|
||||
"<C-y>" = [ "select_and_accept" "fallback" ];
|
||||
# Dismiss the menu and undo the auto_insert preview, staying in
|
||||
# insert mode. `cancel` rather than the preset's `hide`, which
|
||||
# would leave the previewed text in the buffer without the
|
||||
# additionalTextEdits (imports) and snippet expansion that a real
|
||||
# accept applies — a degraded accept nobody wants. This also
|
||||
# matches builtin ins-completion, where C-e restores the typed
|
||||
# text. With no menu open both keys fall back to their native
|
||||
# meaning (Esc leaves insert mode).
|
||||
"<C-e>" = [ "cancel" "fallback" ];
|
||||
"<Esc>" = [ "cancel" "fallback" ];
|
||||
};
|
||||
appearance.nerd_font_variant = "mono";
|
||||
sources.default = [ "lsp" "path" "snippets" "buffer" ];
|
||||
completion.documentation.auto_show = true;
|
||||
completion = {
|
||||
documentation.auto_show = true;
|
||||
# No item is selected until one is picked with Down/C-n, so <CR>
|
||||
# inserts a newline unless a completion was deliberately chosen.
|
||||
list.selection.preselect = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -227,11 +266,24 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
# Moodle/Mustache templates. nvim detects `.mustache` as filetype mustache
|
||||
# but ships no syntax file for it, so the buffer renders as plain text.
|
||||
# Tree-sitter is not an option here: the Handlebars grammar (glimmer)
|
||||
# rejects mustache's argument-less sections, `{{{unescaped}}}`, partials
|
||||
# and Moodle's `{{< parent }}` inheritance, and a single error node drops
|
||||
# highlighting for the rest of the file. This plugin carries the syntax,
|
||||
# indent and ftdetect scripts. nixpkgs marks it unfree only because
|
||||
# upstream ships no license metadata; `allowUnfree` in modules/nixos.nix
|
||||
# already covers it.
|
||||
extraPlugins = [ pkgs.vimPlugins.vim-mustache-handlebars ];
|
||||
|
||||
# Global (non-LSP) keymaps. Deliberately small — a starting point to grow.
|
||||
keymaps = [
|
||||
{ mode = "n"; key = "<leader>w"; action = "<cmd>w<cr>"; options.desc = "Save"; }
|
||||
{ mode = "n"; key = "<leader>q"; action = "<cmd>q<cr>"; options.desc = "Quit window"; }
|
||||
{ mode = "n"; key = "<leader>Q"; action = "<cmd>qa<cr>"; options.desc = "Quit all (exit nvim)"; }
|
||||
# `confirm` prompts Save/Discard/Cancel per modified buffer rather than
|
||||
# aborting with E37, so quitting never dead-ends and never writes silently.
|
||||
{ mode = "n"; key = "<leader>Q"; action = "<cmd>confirm qa<cr>"; options.desc = "Quit all (prompt to save)"; }
|
||||
# Restore the saved session for the current directory (persistence.nvim).
|
||||
{ mode = "n"; key = "<leader>S"; action.__raw = "function() require('persistence').load() end"; options.desc = "Restore session"; }
|
||||
{ mode = "n"; key = "<leader>e"; action = "<cmd>Neotree toggle<cr>"; options.desc = "File tree"; }
|
||||
@@ -251,7 +303,26 @@ in
|
||||
# Buffer navigation (no bufferline: cycle with Shift-h/l, list via <leader>fb)
|
||||
{ mode = "n"; key = "<S-l>"; action = "<cmd>bnext<cr>"; options.desc = "Next buffer"; }
|
||||
{ mode = "n"; key = "<S-h>"; action = "<cmd>bprevious<cr>"; options.desc = "Prev buffer"; }
|
||||
{ mode = "n"; key = "<leader>bd"; action = "<cmd>bdelete<cr>"; options.desc = "Close buffer"; }
|
||||
# Plain `:bdelete` closes the window when no other listed buffer is left
|
||||
# for it to show, which with neo-tree open leaves a full-width tree.
|
||||
# Point the window at another buffer (or a fresh empty one) first, so the
|
||||
# layout survives; `confirm` prompts rather than failing when modified.
|
||||
{
|
||||
mode = "n";
|
||||
key = "<leader>bd";
|
||||
action.__raw = ''
|
||||
function()
|
||||
local cur = vim.api.nvim_get_current_buf()
|
||||
local listed = vim.tbl_filter(
|
||||
function(b) return vim.bo[b].buflisted end,
|
||||
vim.api.nvim_list_bufs()
|
||||
)
|
||||
if #listed > 1 then vim.cmd('bprevious') else vim.cmd('enew') end
|
||||
vim.cmd('confirm bdelete ' .. cur)
|
||||
end
|
||||
'';
|
||||
options.desc = "Close buffer";
|
||||
}
|
||||
|
||||
# Terminal: toggle a horizontal or vertical split (toggleterm). Same key
|
||||
# hides it again. From inside the terminal, <C-w> drops to normal mode and
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# `nixos-diff`: after a rebuild, report (1) what packages changed and (2) whether the
|
||||
# change needs a reboot, a re-login, or nothing. NixOS keeps two markers —
|
||||
# /run/booted-system (what you booted) and /run/current-system (what the last `switch`
|
||||
# activated). A switch applies userland live, but:
|
||||
# - the kernel, initrd, and kernel modules are frozen until the next BOOT — detected
|
||||
# exactly, by comparing those store paths between the two systems;
|
||||
# - session-mapped components (the Wayland compositor, GL stack, portals) a running
|
||||
# graphical session keeps until you log out and back in — detected HEURISTICALLY,
|
||||
# by matching changed package names against `local.rebuildDiff.reloginPackages`.
|
||||
# Generic and topology-free, so it rides in the baseline; disable with
|
||||
# `local.rebuildDiff.enable = false`.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.local.rebuildDiff;
|
||||
reloginPattern = lib.concatStringsSep "|" cfg.reloginPackages;
|
||||
|
||||
nixos-diff = pkgs.writeShellApplication {
|
||||
name = "nixos-diff";
|
||||
runtimeInputs = [ pkgs.nvd pkgs.coreutils pkgs.gnugrep ];
|
||||
text = ''
|
||||
booted=/run/booted-system
|
||||
current=/run/current-system
|
||||
|
||||
# Package-level diff of the running system vs the activated one. Force colour
|
||||
# (nvd auto-disables it when captured); only version parts are coloured, so the
|
||||
# package-name grep below is unaffected.
|
||||
diff=$(nvd --color always diff "$booted" "$current")
|
||||
printf '%s\n' "$diff"
|
||||
|
||||
# Reboot (exact): kernel/initrd/modules only take effect on the next boot.
|
||||
reboot=0
|
||||
for c in kernel initrd kernel-modules; do
|
||||
[ "$(readlink -f "$booted/$c" 2>/dev/null)" = "$(readlink -f "$current/$c" 2>/dev/null)" ] \
|
||||
|| reboot=1
|
||||
done
|
||||
|
||||
# Re-login (heuristic): session-mapped components a running Wayland session
|
||||
# keeps until you log out and back in.
|
||||
relogin_hits=""
|
||||
${lib.optionalString (reloginPattern != "") ''
|
||||
relogin_hits=$(printf '%s\n' "$diff" | grep -Ew "${reloginPattern}" || true)
|
||||
''}
|
||||
|
||||
echo
|
||||
if [ "$reboot" -eq 1 ]; then
|
||||
printf '>>> REBOOT needed (kernel/initrd/modules changed).\n'
|
||||
elif [ -n "$relogin_hits" ]; then
|
||||
printf '>>> RE-LOGIN recommended — session components changed:\n'
|
||||
printf '%s\n' "$relogin_hits"
|
||||
else
|
||||
printf '>>> No reboot/re-login needed.\n'
|
||||
fi
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
options.local.rebuildDiff = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Install `nixos-diff`: after a `nixos-rebuild switch`, print an `nvd` closure
|
||||
diff of the running vs activated system plus a reboot / re-login / live verdict.
|
||||
'';
|
||||
};
|
||||
|
||||
reloginPackages = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "mesa" "wayland" "hyprland" "xdg-desktop-portal" ];
|
||||
description = ''
|
||||
Whole-word package-name patterns matched against the diff; a match means a
|
||||
running Wayland session should be restarted (re-login) to pick the change up —
|
||||
the compositor, GL stack, and portals. Extend per host (e.g. a specific GPU
|
||||
driver). Empty disables the re-login heuristic.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [ nixos-diff pkgs.nvd ];
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user