feat(lifecycle): plain-language generation diffs (#82)
Some checks failed
Check / eval (push) Has been cancelled
Some checks failed
Check / eval (push) Has been cancelled
nomarchy-what-changed wraps nvd into "N added, M removed, K updated" summaries. nomarchy-rebuild / nomarchy-home print full nvd and toast the one-liner; System › What changed? offers toast + floating report. checks.what-changed fixtures nvd via NOMARCHY_NVD. Verified: V2 — flake check --no-build; checks.what-changed green; local smoke on real HM generations.
This commit is contained in:
@@ -187,6 +187,8 @@ nomarchy-pull # nix flake update — refresh inputs (nomarchy, nixpkgs, …
|
|||||||
nomarchy-rebuild # system rebuild against the CURRENT lock (sudo inside;
|
nomarchy-rebuild # system rebuild against the CURRENT lock (sudo inside;
|
||||||
# BTRFS snapshot first when snapper is on; nvd diff after)
|
# BTRFS snapshot first when snapper is on; nvd diff after)
|
||||||
nomarchy-home # home-manager switch — desktop only, no sudo, no lock bump
|
nomarchy-home # home-manager switch — desktop only, no sudo, no lock bump
|
||||||
|
# (prints nvd + a "What changed" toast when the generation moved)
|
||||||
|
nomarchy-what-changed # plain-language last rebuild (system + desktop; --summary for one-liners)
|
||||||
```
|
```
|
||||||
|
|
||||||
`~/.nomarchy` is **your machine flake** (`system.nix` / `home.nix`). Newer
|
`~/.nomarchy` is **your machine flake** (`system.nix` / `home.nix`). Newer
|
||||||
|
|||||||
@@ -55,13 +55,6 @@ runtime-computed `resume_offset` into the boot cmdline, and the dev box runs
|
|||||||
exactly this layout (`/proc/cmdline` verified) but isn't Nomarchy. Steps in
|
exactly this layout (`/proc/cmdline` verified) but isn't Nomarchy. Steps in
|
||||||
HARDWARE-QUEUE. Close #76 on PASS.
|
HARDWARE-QUEUE. Close #76 on PASS.
|
||||||
|
|
||||||
### 82. Generation readability (`VISION § B`)
|
|
||||||
"What changed last rebuild" in plain language: post-switch `nvd` /
|
|
||||||
`nix store diff-closures` summary (n packages up/down/added) surfaced
|
|
||||||
as a toast and/or a "What changed?" row beside System › Rollback.
|
|
||||||
Rollback lists generations but the story is incomplete. VM assert on
|
|
||||||
the diff text.
|
|
||||||
|
|
||||||
### 83. Doctor first-boot section: pre-activate fail flag (`VISION § A`)
|
### 83. Doctor first-boot section: pre-activate fail flag (`VISION § A`)
|
||||||
The installer leaves `/var/log/nomarchy-hm-preactivate.log` on the
|
The installer leaves `/var/log/nomarchy-hm-preactivate.log` on the
|
||||||
target when the theme bake fails, but nothing on the installed system
|
target when the theme bake fails, but nothing on the installed system
|
||||||
@@ -133,9 +126,8 @@ high-ROI, etc.) live in the journal + ROADMAP — not here.*
|
|||||||
2026-07-10). Keep #59 commented install guidance; no
|
2026-07-10). Keep #59 commented install guidance; no
|
||||||
`nomarchy.hardware.nvidia.*` until a hybrid maintainer + queue.
|
`nomarchy.hardware.nvidia.*` until a hybrid maintainer + queue.
|
||||||
|
|
||||||
_(#80 battery health + #81 first-boot welcome shipped 2026-07-11.
|
_(#80–#82 shipped 2026-07-11. Remaining promoted: pre-activate flag,
|
||||||
Remaining promoted: generation readability, pre-activate flag, dock
|
dock life, hall of fame → **NEXT #83–#85**.)_
|
||||||
life, hall of fame → **NEXT #82–#85**.)_
|
|
||||||
|
|
||||||
### v1.0 pointer
|
### v1.0 pointer
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,17 @@ Template:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-11 — #82 generation readability
|
||||||
|
- **Task:** NEXT #82 — plain-language “what changed last rebuild”.
|
||||||
|
- **Did:** `pkgs/nomarchy-what-changed` (nvd → “N added, M removed, K
|
||||||
|
updated”); wired into lifecycle post-`nomarchy-rebuild` / `nomarchy-home`
|
||||||
|
(full nvd + notify toast); System › What changed? (toast + floating
|
||||||
|
full report). `checks.what-changed` with NOMARCHY_NVD fixture. Closed #82.
|
||||||
|
- **Verified:** **V2** — checks.what-changed green; local smoke on real
|
||||||
|
HM gens (“Desktop: 1 added, 1 removed”) + fixture counts.
|
||||||
|
- **Pending:** —
|
||||||
|
- **Next suggestion:** #83 doctor pre-activate fail flag or #85 docs.
|
||||||
|
|
||||||
## 2026-07-11 — #81 first-boot welcome toast
|
## 2026-07-11 — #81 first-boot welcome toast
|
||||||
- **Task:** NEXT #81 — dismissible “you're set” card (`VISION § B`).
|
- **Task:** NEXT #81 — dismissible “you're set” card (`VISION § B`).
|
||||||
- **Did:** `pkgs/nomarchy-first-boot` + user oneshot
|
- **Did:** `pkgs/nomarchy-first-boot` + user oneshot
|
||||||
|
|||||||
@@ -376,6 +376,11 @@ Design/decision records and a running log of shipped work (items marked
|
|||||||
`hostname == nomarchy-live` (live ISO keeps its own welcome). Toggle:
|
`hostname == nomarchy-live` (live ISO keeps its own welcome). Toggle:
|
||||||
`nomarchy.firstBootWelcome.enable`. `checks.first-boot` gates toast /
|
`nomarchy.firstBootWelcome.enable`. `checks.first-boot` gates toast /
|
||||||
marker / re-run silence / live skip.
|
marker / re-run silence / live skip.
|
||||||
|
✓ **Generation readability (#82, `VISION § B`):** `nomarchy-what-changed`
|
||||||
|
wraps nvd into plain-language counts (added/removed/updated); post-switch
|
||||||
|
toast from `nomarchy-rebuild` / `nomarchy-home`; System › What changed?
|
||||||
|
(toast + floating full report). `checks.what-changed` fixtures nvd via
|
||||||
|
`NOMARCHY_NVD`.
|
||||||
- ✓ **Hibernate double-unlock:** on resume from hibernate the LUKS
|
- ✓ **Hibernate double-unlock:** on resume from hibernate the LUKS
|
||||||
passphrase already gates the machine, but locking hyprlock before sleep
|
passphrase already gates the machine, but locking hyprlock before sleep
|
||||||
meant a second password. Fixed by *not locking* before an encrypted
|
meant a second password. Fixed by *not locking* before an encrypted
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Reference: [HARDWARE.md](HARDWARE.md) §4–§5, §10.
|
|||||||
|------|--------|-------|
|
|------|--------|-------|
|
||||||
| **Silent first-week card** ✓ | One dismissible “you’re set” (menu, themes, keys, network) — not a wizard | ✓ shipped #81 — `settings.firstBootShown` + notify; live ISO keeps its own toast |
|
| **Silent first-week card** ✓ | One dismissible “you’re set” (menu, themes, keys, network) — not a wizard | ✓ shipped #81 — `settings.firstBootShown` + notify; live ISO keeps its own toast |
|
||||||
| **Boreal as default** | Identity on first boot | BACKLOG PROPOSED already |
|
| **Boreal as default** | Identity on first boot | BACKLOG PROPOSED already |
|
||||||
| **Generation readability** | “What changed last rebuild” in plain language | Rollback exists; story is incomplete |
|
| **Generation readability** ✓ | “What changed last rebuild” in plain language | ✓ shipped #82 — nvd toast + System › What changed? |
|
||||||
| **Post-install hints** | One-shot MOTD/notify for fwupd / fprintd when relevant | Avoid permanent nag |
|
| **Post-install hints** | One-shot MOTD/notify for fwupd / fprintd when relevant | Avoid permanent nag |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
68
flake.nix
68
flake.nix
@@ -87,9 +87,11 @@
|
|||||||
nomarchy-control-center = final.callPackage ./pkgs/nomarchy-control-center { };
|
nomarchy-control-center = final.callPackage ./pkgs/nomarchy-control-center { };
|
||||||
nomarchy-battery-notify = final.callPackage ./pkgs/nomarchy-battery-notify { };
|
nomarchy-battery-notify = final.callPackage ./pkgs/nomarchy-battery-notify { };
|
||||||
nomarchy-first-boot = final.callPackage ./pkgs/nomarchy-first-boot { };
|
nomarchy-first-boot = final.callPackage ./pkgs/nomarchy-first-boot { };
|
||||||
|
nomarchy-what-changed = final.callPackage ./pkgs/nomarchy-what-changed { };
|
||||||
nomarchy-detect-hw = final.callPackage ./pkgs/nomarchy-detect-hw { };
|
nomarchy-detect-hw = final.callPackage ./pkgs/nomarchy-detect-hw { };
|
||||||
# pull / rebuild / home (+ legacy aliases) — also on HM so a home
|
# pull / rebuild / home (+ legacy aliases) — also on HM so a home
|
||||||
# switch can refresh a broken system-package nomarchy-pull.
|
# switch can refresh a broken system-package nomarchy-pull.
|
||||||
|
# Includes nomarchy-what-changed (generation readability, #82).
|
||||||
nomarchy-lifecycle = final.callPackage ./pkgs/nomarchy-lifecycle { };
|
nomarchy-lifecycle = final.callPackage ./pkgs/nomarchy-lifecycle { };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,6 +128,7 @@
|
|||||||
nomarchy-control-center = pkgs.nomarchy-control-center;
|
nomarchy-control-center = pkgs.nomarchy-control-center;
|
||||||
nomarchy-battery-notify = pkgs.nomarchy-battery-notify;
|
nomarchy-battery-notify = pkgs.nomarchy-battery-notify;
|
||||||
nomarchy-first-boot = pkgs.nomarchy-first-boot;
|
nomarchy-first-boot = pkgs.nomarchy-first-boot;
|
||||||
|
nomarchy-what-changed = pkgs.nomarchy-what-changed;
|
||||||
nomarchy-detect-hw = pkgs.nomarchy-detect-hw;
|
nomarchy-detect-hw = pkgs.nomarchy-detect-hw;
|
||||||
default = pkgs.nomarchy-theme-sync;
|
default = pkgs.nomarchy-theme-sync;
|
||||||
};
|
};
|
||||||
@@ -746,6 +749,71 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Generation readability (#82): nomarchy-what-changed turns an
|
||||||
|
# nvd report into plain-language counts. Inject a fake nvd via
|
||||||
|
# NOMARCHY_NVD so the VM stays minimal (no real store closures).
|
||||||
|
what-changed = pkgs.testers.runNixOSTest {
|
||||||
|
name = "nomarchy-what-changed";
|
||||||
|
nodes.machine = { ... }: {
|
||||||
|
environment.systemPackages = [ pkgs.nomarchy-what-changed ];
|
||||||
|
};
|
||||||
|
testScript = ''
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
# Fake nvd: args are `nvd --color never diff A B` — ignore paths.
|
||||||
|
machine.succeed(
|
||||||
|
"mkdir -p /shim && cat > /shim/fake-nvd <<'EOF'\n"
|
||||||
|
"#!/bin/sh\n"
|
||||||
|
"cat <<'R'\n"
|
||||||
|
"<<< /nix/store/old\n"
|
||||||
|
">>> /nix/store/new\n"
|
||||||
|
"Added packages:\n"
|
||||||
|
"[A.] #1 hello 2.12\n"
|
||||||
|
"[A.] #2 cowsay 3.04\n"
|
||||||
|
"Removed packages:\n"
|
||||||
|
"[R.] #1 fortune 1.0\n"
|
||||||
|
"Version changes:\n"
|
||||||
|
"[C.] #1 bash: 5.2 -> 5.3\n"
|
||||||
|
"Closure size: 100 -> 105 (10 paths added, 5 paths removed, delta +5).\n"
|
||||||
|
"R\n"
|
||||||
|
"EOF\n"
|
||||||
|
"chmod +x /shim/fake-nvd"
|
||||||
|
)
|
||||||
|
# Dummy before/after paths (script only needs them to exist and differ).
|
||||||
|
machine.succeed("mkdir -p /tmp/gen-a /tmp/gen-b && touch /tmp/gen-a/x /tmp/gen-b/y")
|
||||||
|
|
||||||
|
out = machine.succeed(
|
||||||
|
"NOMARCHY_NVD=/shim/fake-nvd nomarchy-what-changed --summary "
|
||||||
|
"--diff /tmp/gen-a /tmp/gen-b"
|
||||||
|
)
|
||||||
|
assert "2 added" in out, f"missing added count:\n{out}"
|
||||||
|
assert "1 removed" in out, f"missing removed count:\n{out}"
|
||||||
|
assert "1 updated" in out, f"missing updated count:\n{out}"
|
||||||
|
|
||||||
|
# Identical paths → no package changes (no nvd call needed).
|
||||||
|
out2 = machine.succeed(
|
||||||
|
"NOMARCHY_NVD=/shim/fake-nvd nomarchy-what-changed --summary "
|
||||||
|
"--diff /tmp/gen-a /tmp/gen-a"
|
||||||
|
)
|
||||||
|
assert "no package changes" in out2, f"identical paths should short-circuit:\n{out2}"
|
||||||
|
|
||||||
|
# Empty-ish nvd report.
|
||||||
|
machine.succeed(
|
||||||
|
"cat > /shim/fake-nvd-empty <<'EOF'\n"
|
||||||
|
"#!/bin/sh\n"
|
||||||
|
"echo 'No version or selection state changes.'\n"
|
||||||
|
"echo 'Closure size: 1 -> 1 (0 paths added, 0 paths removed, delta +0).'\n"
|
||||||
|
"EOF\n"
|
||||||
|
"chmod +x /shim/fake-nvd-empty"
|
||||||
|
)
|
||||||
|
out3 = machine.succeed(
|
||||||
|
"NOMARCHY_NVD=/shim/fake-nvd-empty nomarchy-what-changed --summary "
|
||||||
|
"--diff /tmp/gen-a /tmp/gen-b"
|
||||||
|
)
|
||||||
|
assert "no package changes" in out3, f"empty report:\n{out3}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Memory-pressure protection (oom.nix): earlyoom must kill a
|
# Memory-pressure protection (oom.nix): earlyoom must kill a
|
||||||
# runaway allocator BEFORE the kernel thrash point — and must not
|
# runaway allocator BEFORE the kernel thrash point — and must not
|
||||||
# take anything else with it. A bystander unit survives the kill,
|
# take anything else with it. A bystander unit survives the kill,
|
||||||
|
|||||||
@@ -895,6 +895,20 @@ ${themeRows}
|
|||||||
# Nomarchy TUI Control Center.
|
# Nomarchy TUI Control Center.
|
||||||
exec ${cfg.terminal} -e nomarchy-control-center ;;
|
exec ${cfg.terminal} -e nomarchy-control-center ;;
|
||||||
|
|
||||||
|
whatchanged)
|
||||||
|
# Plain-language last rebuild (system + desktop nvd). Toast the
|
||||||
|
# one-liners; open a floating terminal for the full report when
|
||||||
|
# the user wants detail (same ghostty class pattern as Doctor).
|
||||||
|
if ! command -v nomarchy-what-changed >/dev/null 2>&1; then
|
||||||
|
notify-send "What changed" "nomarchy-what-changed not on PATH (lifecycle package?)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
summary=$(nomarchy-what-changed --summary 2>/dev/null || true)
|
||||||
|
[ -n "$summary" ] && notify-send -a Nomarchy "What changed" "$summary"
|
||||||
|
exec ghostty --class=com.nomarchy.doctor --gtk-single-instance=false \
|
||||||
|
-e sh -c "nomarchy-what-changed
|
||||||
|
printf '\nEnter to close.'; read -r _" ;;
|
||||||
|
|
||||||
rollback)
|
rollback)
|
||||||
# Undo, one menu away (informative + rock-stable). Desktop =
|
# Undo, one menu away (informative + rock-stable). Desktop =
|
||||||
# Home Manager generations: theme/config history is already one
|
# Home Manager generations: theme/config history is already one
|
||||||
@@ -996,6 +1010,9 @@ ${themeRows}
|
|||||||
fi
|
fi
|
||||||
command -v nomarchy-snapshots >/dev/null 2>&1 && row "Snapshots" timeshift
|
command -v nomarchy-snapshots >/dev/null 2>&1 && row "Snapshots" timeshift
|
||||||
row "Rollback" edit-undo
|
row "Rollback" edit-undo
|
||||||
|
# Plain-language last-rebuild summary (nvd; BACKLOG #82).
|
||||||
|
command -v nomarchy-what-changed >/dev/null 2>&1 \
|
||||||
|
&& row "What changed?" view-refresh
|
||||||
command -v nomarchy-doctor >/dev/null 2>&1 && row "Doctor" utilities-system-monitor
|
command -v nomarchy-doctor >/dev/null 2>&1 && row "Doctor" utilities-system-monitor
|
||||||
# Firmware/LVFS updates — self-gated on fwupd (default-on, but off
|
# Firmware/LVFS updates — self-gated on fwupd (default-on, but off
|
||||||
# on VMs/headless via services.fwupd.enable=false), like Printers.
|
# on VMs/headless via services.fwupd.enable=false), like Printers.
|
||||||
@@ -1024,6 +1041,7 @@ ${themeRows}
|
|||||||
*"Auto-commit"*) exec "$0" autocommit ;;
|
*"Auto-commit"*) exec "$0" autocommit ;;
|
||||||
*Snapshots*) exec "$0" snapshot ;;
|
*Snapshots*) exec "$0" snapshot ;;
|
||||||
*Rollback*) exec "$0" rollback ;;
|
*Rollback*) exec "$0" rollback ;;
|
||||||
|
*"What changed"*) exec "$0" whatchanged ;;
|
||||||
*Doctor*) exec "$0" doctor ;;
|
*Doctor*) exec "$0" doctor ;;
|
||||||
*Firmware*) exec "$0" firmware ;;
|
*Firmware*) exec "$0" firmware ;;
|
||||||
*Fingerprint*) exec "$0" fingerprint ;;
|
*Fingerprint*) exec "$0" fingerprint ;;
|
||||||
@@ -1134,7 +1152,7 @@ ${themeRows}
|
|||||||
esac ;;
|
esac ;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "usage: nomarchy-menu [lookfeel|tools|system|power|power-profile|powermgmt|batterylimit|theme|clipboard|calc|files|emoji|web|network|bluetooth|audio|display|display-profile|printers|capture|colorpicker|keybinds|ask|dnd|nightlight|autotimezone|autocommit|vpn|snapshot|doctor|firmware|fingerprint|controlcenter|rollback]" >&2
|
echo "usage: nomarchy-menu [lookfeel|tools|system|power|power-profile|powermgmt|batterylimit|theme|clipboard|calc|files|emoji|web|network|bluetooth|audio|display|display-profile|printers|capture|colorpicker|keybinds|ask|dnd|nightlight|autotimezone|autocommit|vpn|snapshot|doctor|firmware|fingerprint|controlcenter|rollback|whatchanged]" >&2
|
||||||
exit 64 ;;
|
exit 64 ;;
|
||||||
esac
|
esac
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Installed on both NixOS (systemPackages) and Home Manager so a home
|
# Installed on both NixOS (systemPackages) and Home Manager so a home
|
||||||
# switch can refresh them without waiting for a full system rebuild —
|
# switch can refresh them without waiting for a full system rebuild —
|
||||||
# otherwise a broken nomarchy-pull can never update itself.
|
# otherwise a broken nomarchy-pull can never update itself.
|
||||||
{ lib, writeShellScriptBin, nvd, jq, symlinkJoin }:
|
{ lib, writeShellScriptBin, nvd, jq, symlinkJoin, nomarchy-what-changed }:
|
||||||
|
|
||||||
let
|
let
|
||||||
# Shared preamble: refuse root, resolve flake path.
|
# Shared preamble: refuse root, resolve flake path.
|
||||||
@@ -78,6 +78,13 @@ let
|
|||||||
else
|
else
|
||||||
echo "nomarchy-rebuild: what changed:"
|
echo "nomarchy-rebuild: what changed:"
|
||||||
${nvd}/bin/nvd diff "$before" "$after" || true
|
${nvd}/bin/nvd diff "$before" "$after" || true
|
||||||
|
# One-line toast for the session (full report already on the terminal).
|
||||||
|
if command -v notify-send >/dev/null 2>&1 \
|
||||||
|
&& command -v nomarchy-what-changed >/dev/null 2>&1; then
|
||||||
|
body=$(nomarchy-what-changed --summary --diff "$before" "$after" 2>/dev/null \
|
||||||
|
| sed 's/^Diff: //' || true)
|
||||||
|
[ -n "''${body:-}" ] && notify-send -a Nomarchy "System rebuild" "$body" || true
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
@@ -87,6 +94,16 @@ let
|
|||||||
echo "nomarchy-home: no flake.nix at $flake" >&2
|
echo "nomarchy-home: no flake.nix at $flake" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
# Snapshot the active HM generation before the switch so we can nvd it.
|
||||||
|
hm_before=""
|
||||||
|
for d in \
|
||||||
|
"''${XDG_STATE_HOME:-$HOME/.local/state}/nix/profiles" \
|
||||||
|
"/nix/var/nix/profiles/per-user/$(id -un)"; do
|
||||||
|
if [ -L "$d/home-manager" ]; then
|
||||||
|
hm_before=$(readlink -f "$d/home-manager" 2>/dev/null || true)
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
log=$(mktemp)
|
log=$(mktemp)
|
||||||
trap 'rm -f "$log"' EXIT
|
trap 'rm -f "$log"' EXIT
|
||||||
set +e
|
set +e
|
||||||
@@ -102,7 +119,27 @@ let
|
|||||||
echo "Recovery: home-manager generations (or docs/RECOVERY.md)"
|
echo "Recovery: home-manager generations (or docs/RECOVERY.md)"
|
||||||
exit "$rc"
|
exit "$rc"
|
||||||
fi
|
fi
|
||||||
|
hm_after=""
|
||||||
|
for d in \
|
||||||
|
"''${XDG_STATE_HOME:-$HOME/.local/state}/nix/profiles" \
|
||||||
|
"/nix/var/nix/profiles/per-user/$(id -un)"; do
|
||||||
|
if [ -L "$d/home-manager" ]; then
|
||||||
|
hm_after=$(readlink -f "$d/home-manager" 2>/dev/null || true)
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -n "$hm_before" ] && [ -n "$hm_after" ] && [ "$hm_before" != "$hm_after" ]; then
|
||||||
|
echo "nomarchy-home: what changed:"
|
||||||
|
${nvd}/bin/nvd diff "$hm_before" "$hm_after" || true
|
||||||
|
if command -v notify-send >/dev/null 2>&1 \
|
||||||
|
&& command -v nomarchy-what-changed >/dev/null 2>&1; then
|
||||||
|
body=$(nomarchy-what-changed --summary --diff "$hm_before" "$hm_after" 2>/dev/null \
|
||||||
|
| sed 's/^Diff: //' || true)
|
||||||
|
[ -n "''${body:-}" ] && notify-send -a Nomarchy "Desktop updated" "$body" || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
echo "nomarchy-home: desktop applied."
|
echo "nomarchy-home: desktop applied."
|
||||||
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Legacy aliases
|
# Legacy aliases
|
||||||
@@ -125,6 +162,7 @@ symlinkJoin {
|
|||||||
sys-update
|
sys-update
|
||||||
sys-rebuild
|
sys-rebuild
|
||||||
home-update
|
home-update
|
||||||
|
nomarchy-what-changed
|
||||||
];
|
];
|
||||||
meta = {
|
meta = {
|
||||||
description = "Nomarchy machine-flake lifecycle: pull, rebuild, home";
|
description = "Nomarchy machine-flake lifecycle: pull, rebuild, home";
|
||||||
|
|||||||
9
pkgs/nomarchy-what-changed/default.nix
Normal file
9
pkgs/nomarchy-what-changed/default.nix
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Plain-language generation diff (BACKLOG #82). Wraps nvd; PATH-resolved
|
||||||
|
# notify is left to callers. NOMARCHY_NVD lets checks inject a fixture.
|
||||||
|
{ writeShellApplication, coreutils, nvd, gnugrep, gnused }:
|
||||||
|
|
||||||
|
writeShellApplication {
|
||||||
|
name = "nomarchy-what-changed";
|
||||||
|
runtimeInputs = [ coreutils nvd gnugrep gnused ];
|
||||||
|
text = builtins.readFile ./nomarchy-what-changed.sh;
|
||||||
|
}
|
||||||
178
pkgs/nomarchy-what-changed/nomarchy-what-changed.sh
Normal file
178
pkgs/nomarchy-what-changed/nomarchy-what-changed.sh
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
# nomarchy-what-changed — plain-language "what changed last rebuild".
|
||||||
|
# Wraps nvd into a short summary (for toasts / menu) and optional full
|
||||||
|
# report. In-checkout state is not involved; this only reads Nix profiles.
|
||||||
|
#
|
||||||
|
# usage:
|
||||||
|
# nomarchy-what-changed # system + home (last two gens each)
|
||||||
|
# nomarchy-what-changed system|home # one layer
|
||||||
|
# nomarchy-what-changed --summary … # one line per layer (toast body)
|
||||||
|
# nomarchy-what-changed --diff PATH1 PATH2
|
||||||
|
# nomarchy-what-changed --summary --diff PATH1 PATH2
|
||||||
|
#
|
||||||
|
# NOMARCHY_NVD overrides the nvd binary (tests inject a fixture).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
nvd_bin="${NOMARCHY_NVD:-nvd}"
|
||||||
|
summary_only=0
|
||||||
|
mode="" # empty | system | home | diff
|
||||||
|
diff_a="" diff_b=""
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: nomarchy-what-changed [--summary] [system|home]" >&2
|
||||||
|
echo " nomarchy-what-changed [--summary] --diff <before> <after>" >&2
|
||||||
|
exit 64
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--summary) summary_only=1; shift ;;
|
||||||
|
--diff)
|
||||||
|
mode="diff"
|
||||||
|
shift
|
||||||
|
[ $# -ge 2 ] || usage
|
||||||
|
diff_a=$1; diff_b=$2; shift 2
|
||||||
|
;;
|
||||||
|
system|home) mode="$1"; shift ;;
|
||||||
|
-h|--help) usage ;;
|
||||||
|
*) usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# One-line plain-language summary of an nvd --color never report.
|
||||||
|
# Prefer package row counts ([A.]/[R.]/[C.]); fall back to closure path delta.
|
||||||
|
summarize_report() {
|
||||||
|
local label=$1 report=$2
|
||||||
|
local added removed changed
|
||||||
|
if printf '%s\n' "$report" | grep -q 'No version or selection state changes'; then
|
||||||
|
printf '%s: no package changes\n' "$label"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
# Count package rows (nvd tags: A added, R removed, C version/selection change).
|
||||||
|
added=$(printf '%s\n' "$report" | grep -c '\[A\.' || true)
|
||||||
|
removed=$(printf '%s\n' "$report" | grep -c '\[R\.' || true)
|
||||||
|
changed=$(printf '%s\n' "$report" | grep -c '\[C\.' || true)
|
||||||
|
# Strip trailing newline noise from grep -c on empty (already || true → 0).
|
||||||
|
added=${added:-0}; removed=${removed:-0}; changed=${changed:-0}
|
||||||
|
|
||||||
|
if [ "$added" -eq 0 ] && [ "$removed" -eq 0 ] && [ "$changed" -eq 0 ]; then
|
||||||
|
# Closure-only deltas (sources, etc. without package names).
|
||||||
|
local paths_a paths_r
|
||||||
|
paths_a=$(printf '%s\n' "$report" \
|
||||||
|
| sed -n 's/.*(\([0-9]*\) paths added,.*/\1/p' | head -1)
|
||||||
|
paths_r=$(printf '%s\n' "$report" \
|
||||||
|
| sed -n 's/.*paths added, \([0-9]*\) paths removed.*/\1/p' | head -1)
|
||||||
|
paths_a=${paths_a:-0}; paths_r=${paths_r:-0}
|
||||||
|
if [ "$paths_a" -eq 0 ] && [ "$paths_r" -eq 0 ]; then
|
||||||
|
printf '%s: no package changes\n' "$label"
|
||||||
|
else
|
||||||
|
printf '%s: %s paths added, %s removed\n' "$label" "$paths_a" "$paths_r"
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local parts=()
|
||||||
|
[ "$added" -gt 0 ] && parts+=("$added added")
|
||||||
|
[ "$removed" -gt 0 ] && parts+=("$removed removed")
|
||||||
|
[ "$changed" -gt 0 ] && parts+=("$changed updated")
|
||||||
|
local joined
|
||||||
|
joined=$(printf '%s, ' "${parts[@]}")
|
||||||
|
joined=${joined%, }
|
||||||
|
printf '%s: %s\n' "$label" "$joined"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_diff() {
|
||||||
|
local before=$1 after=$2 label=$3
|
||||||
|
if [ ! -e "$before" ] || [ ! -e "$after" ]; then
|
||||||
|
printf '%s: no previous generation to compare\n' "$label"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ "$(readlink -f "$before" 2>/dev/null || echo "$before")" \
|
||||||
|
= "$(readlink -f "$after" 2>/dev/null || echo "$after")" ]; then
|
||||||
|
printf '%s: no package changes\n' "$label"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local report
|
||||||
|
report=$("$nvd_bin" --color never diff "$before" "$after" 2>/dev/null) || {
|
||||||
|
printf '%s: could not diff (is nvd installed?)\n' "$label"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if [ "$summary_only" -eq 1 ]; then
|
||||||
|
summarize_report "$label" "$report"
|
||||||
|
else
|
||||||
|
printf '── %s ──\n' "$label"
|
||||||
|
printf '%s\n' "$report"
|
||||||
|
echo
|
||||||
|
summarize_report "$label" "$report"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve last two links for a profile stem (…/system or …/home-manager).
|
||||||
|
last_two() {
|
||||||
|
local stem=$1
|
||||||
|
# shellcheck disable=SC2012
|
||||||
|
ls -d "${stem}"-[0-9]*-link 2>/dev/null | sort -V | tail -n 2
|
||||||
|
}
|
||||||
|
|
||||||
|
system_pair() {
|
||||||
|
last_two /nix/var/nix/profiles/system
|
||||||
|
}
|
||||||
|
|
||||||
|
home_pair() {
|
||||||
|
local d
|
||||||
|
d="${XDG_STATE_HOME:-$HOME/.local/state}/nix/profiles"
|
||||||
|
if ls -d "$d"/home-manager-[0-9]*-link >/dev/null 2>&1; then
|
||||||
|
last_two "$d/home-manager"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
d="/nix/var/nix/profiles/per-user/${USER:-$(id -un)}"
|
||||||
|
if ls -d "$d"/home-manager-[0-9]*-link >/dev/null 2>&1; then
|
||||||
|
last_two "$d/home-manager"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pair_to_before_after() {
|
||||||
|
# stdin: 0–2 paths; sets before/after (before may be empty).
|
||||||
|
local -a links=()
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -n "$line" ] && links+=("$line")
|
||||||
|
done
|
||||||
|
before=""; after=""
|
||||||
|
if [ "${#links[@]}" -ge 2 ]; then
|
||||||
|
before=$(readlink -f "${links[0]}")
|
||||||
|
after=$(readlink -f "${links[1]}")
|
||||||
|
elif [ "${#links[@]}" -eq 1 ]; then
|
||||||
|
after=$(readlink -f "${links[0]}")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$mode" = diff ]; then
|
||||||
|
run_diff "$diff_a" "$diff_b" "Diff"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$mode" ] || [ "$mode" = system ]; then
|
||||||
|
before=""; after=""
|
||||||
|
pair_to_before_after < <(system_pair)
|
||||||
|
if [ -n "$after" ] && [ -n "$before" ]; then
|
||||||
|
run_diff "$before" "$after" "System"
|
||||||
|
elif [ -n "$after" ]; then
|
||||||
|
printf 'System: only one generation on disk — rebuild once more to compare\n'
|
||||||
|
else
|
||||||
|
printf 'System: no NixOS profile found\n'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$mode" ] || [ "$mode" = home ]; then
|
||||||
|
before=""; after=""
|
||||||
|
pair_to_before_after < <(home_pair)
|
||||||
|
if [ -n "$after" ] && [ -n "$before" ]; then
|
||||||
|
run_diff "$before" "$after" "Desktop"
|
||||||
|
elif [ -n "$after" ]; then
|
||||||
|
printf 'Desktop: only one generation on disk — switch once more to compare\n'
|
||||||
|
else
|
||||||
|
printf 'Desktop: no Home Manager profile found\n'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user