# Day-to-day lifecycle CLIs for a Nomarchy machine flake (~/.nomarchy). # Installed on both NixOS (systemPackages) and Home Manager so a home # switch can refresh them without waiting for a full system rebuild — # otherwise a broken nomarchy-pull can never update itself. { lib, writeShellScriptBin, nvd, jq, symlinkJoin, nomarchy-what-changed }: let # Shared preamble: refuse root, resolve flake path. preamble = name: '' set -euo pipefail if [ "$(id -u)" -eq 0 ]; then echo "${name}: run as your normal user" >&2 exit 1 fi flake="''${NOMARCHY_PATH:-$HOME/.nomarchy}" ''; # Opt-in sweep (settings.autoCommit — the same flag theme-sync honours): # commit EVERYTHING dirty in the machine flake before a pull/rebuild/ # home switch, so hand edits to system.nix/home.nix and lock bumps land # in history at the moment they become live — `git log` mirrors the # generation list. Complements nomarchy-theme-sync's auto_commit, which # is pathspec-limited to theme-state.json on menu writes precisely so # half-finished hand edits never ride a settings-named commit; here the # sweep is the point, and the commit body lists what was swept. Never # fatal — callers `|| true` so a git hiccup can't block a rebuild. Not # in the symlinkJoin paths (internal; callers use the store path) but # exposed as passthru.autocommit for checks.lifecycle-autocommit. nomarchy-autocommit = writeShellScriptBin "nomarchy-autocommit" '' ${preamble "nomarchy-autocommit"} label="''${1:-rebuild}" [ -d "$flake/.git" ] || exit 0 command -v git >/dev/null 2>&1 || exit 0 flag=$(${jq}/bin/jq -r '.settings.autoCommit // false' \ "$flake/theme-state.json" 2>/dev/null || true) [ "$flag" = "true" ] || exit 0 dirty=$(git -C "$flake" status --porcelain || true) [ -n "$dirty" ] || exit 0 g=(git -C "$flake") if [ -z "$("''${g[@]}" config user.email 2>/dev/null || true)" ]; then g+=(-c user.name=Nomarchy -c user.email=nomarchy@localhost) fi "''${g[@]}" add -A if "''${g[@]}" commit --quiet -m "nomarchy: auto-commit before $label" \ -m "$dirty"; then echo "nomarchy-autocommit: committed pending flake changes before $label:" echo "$dirty" | sed 's/^/ /' else echo "nomarchy-autocommit: commit failed — continuing without it" >&2 fi ''; nomarchy-pull = writeShellScriptBin "nomarchy-pull" '' ${preamble "nomarchy-pull"} if [ ! -e "$flake/flake.nix" ]; then echo "nomarchy-pull: no flake.nix at $flake" >&2 echo " Set NOMARCHY_PATH or put your machine flake in ~/.nomarchy." >&2 exit 1 fi # With settings.autoCommit on, sweep pending hand edits into a commit # first — also keeps the ff-only pull below safe on a dirty tree. ${nomarchy-autocommit}/bin/nomarchy-autocommit pull || true # Optional: pull *your* machine config only if this checkout tracks a # remote. Distro updates come from the nomarchy flake *input* below — # many installs have no upstream on ~/.nomarchy (local auto-commits). if [ -d "$flake/.git" ] \ && git -C "$flake" rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1; then echo "nomarchy-pull: git pull --ff-only (machine flake tracks upstream)" git -C "$flake" pull --ff-only elif [ -d "$flake/.git" ]; then echo "nomarchy-pull: machine flake has no upstream branch — skipping git pull" echo " (normal for a local-only ~/.nomarchy; distro updates use flake inputs)" fi echo "nomarchy-pull: nix flake update in $flake" nix flake update --flake "$flake" nom=$(nix flake metadata "$flake" --json 2>/dev/null \ | ${jq}/bin/jq -r ' .locks.nodes.nomarchy.locked | if . == null then empty elif .rev then "nomarchy @ \(.rev[0:12])" else empty end ' 2>/dev/null || true) [ -n "''${nom:-}" ] && echo "nomarchy-pull: $nom" echo "nomarchy-pull: done — next: nomarchy-rebuild then nomarchy-home" ''; nomarchy-rebuild = writeShellScriptBin "nomarchy-rebuild" '' ${preamble "nomarchy-rebuild"} if [ ! -e "$flake/flake.nix" ]; then echo "nomarchy-rebuild: no flake.nix at $flake" >&2 exit 1 fi # With settings.autoCommit on, sweep pending hand edits (system.nix, # lock bumps, …) into a commit so `git log` mirrors the generations. ${nomarchy-autocommit}/bin/nomarchy-autocommit rebuild || true before=$(readlink -f /run/current-system) log=$(mktemp) trap 'rm -f "$log"' EXIT set +e if command -v nixos-rebuild-snap >/dev/null 2>&1; then sudo env NOMARCHY_PATH="$flake" nixos-rebuild-snap "$@" 2>&1 | tee "$log" else sudo nixos-rebuild switch --flake "$flake#default" "$@" 2>&1 | tee "$log" fi rc=''${PIPESTATUS[0]} set -e if [ "$rc" -ne 0 ]; then echo echo "nomarchy-rebuild: FAILED (exit $rc). Last lines:" tail -n 40 "$log" || true echo echo "Diagnose: nomarchy-doctor" echo "Recovery: docs/RECOVERY.md (boot menu generations / snapper)" exit "$rc" fi after=$(readlink -f /run/current-system) if [ "$before" = "$after" ]; then echo "nomarchy-rebuild: no changes — the system is identical." else echo "nomarchy-rebuild: what changed:" ${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 ''; nomarchy-home = writeShellScriptBin "nomarchy-home" '' ${preamble "nomarchy-home"} if [ ! -e "$flake/flake.nix" ]; then echo "nomarchy-home: no flake.nix at $flake" >&2 exit 1 fi # With settings.autoCommit on, sweep pending hand edits (home.nix, …) # into a commit so `git log` mirrors the generations. ${nomarchy-autocommit}/bin/nomarchy-autocommit "home switch" || true # 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) trap 'rm -f "$log"' EXIT set +e home-manager switch --flake "$flake" "$@" 2>&1 | tee "$log" rc=''${PIPESTATUS[0]} set -e if [ "$rc" -ne 0 ]; then echo echo "nomarchy-home: FAILED (exit $rc). Last lines:" tail -n 40 "$log" || true echo echo "Diagnose: nomarchy-doctor" echo "Recovery: home-manager generations (or docs/RECOVERY.md)" exit "$rc" 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." fi ''; # Legacy aliases sys-update = writeShellScriptBin "sys-update" '' nomarchy-pull && nomarchy-rebuild "$@" ''; sys-rebuild = writeShellScriptBin "sys-rebuild" '' exec nomarchy-rebuild "$@" ''; home-update = writeShellScriptBin "home-update" '' exec nomarchy-home "$@" ''; in symlinkJoin { name = "nomarchy-lifecycle"; paths = [ nomarchy-pull nomarchy-rebuild nomarchy-home sys-update sys-rebuild home-update nomarchy-what-changed ]; passthru.autocommit = nomarchy-autocommit; meta = { description = "Nomarchy machine-flake lifecycle: pull, rebuild, home"; mainProgram = "nomarchy-pull"; }; }