Updating NixOS flake with Elisp
Why
- As a nixos-unstable branch user, there are frequent updates to my system
- Since I use Nix Flakes, I can and want to keep detailed log of what changes came in with any update
- Doing so manually gets boring fast
Status quo
My current flow looks like this:
cd /etc/nixos && nix flake update
- Copy the 'relevant' part of command output
- Open magit commit dialogue
- Type
flo
to trigger a `yasnippet' which adds a handy commit message by using current date and copied command output.
This generates a nice commit message that looks something like this:
flake.lock: 2023_09_15 • Updated input 'agenix': 'github:ryantm/agenix/d8c973fd228949736dedf61b7f8cc1ece3236792' (2023-07-24) → 'github:ryantm/agenix/20deb735cc405831ba04a0088fecb3887aa255c0' (2023-09-14) • Updated input 'home-manager': 'github:nix-community/home-manager/5171f5ef654425e09d9c2100f856d887da595437' (2023-09-11) → 'github:nix-community/home-manager/d9b88b43524db1591fb3d9410a21428198d75d49' (2023-09-13) • Updated input 'nixos-hardware': 'github:NixOS/nixos-hardware/ca41b8a227dd235b1b308217f116c7e6e84ad779' (2023-09-11) → 'github:NixOS/nixos-hardware/570256327eb6ca6f7bebe8d93af49459092a0c43' (2023-09-14) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/3a2786eea085f040a66ecde1bc3ddc7099f6dbeb' (2023-09-11) → 'github:nixos/nixpkgs/f2ea252d23ebc9a5336bf6a61e0644921f64e67c' (2023-09-14)
But these are still too many manual steps for me, especially for something I tend to run at least couple times a week.
The Fix
So, with help of ChatGPT and amazing Emacs introspectability, I created an interactive function that does everything for me!
1(defun bhankas-nixos-flake-update ()
2 "Update NixOS flake and commit changes to git with message."
3 (interactive)
4
5 (let ((command "cd /etc/nixos && sudo nix flake update") ;; the command
6 (regex (pcre-to-elisp "((^• Updated input .*)|(^\s+(→ )?'[^']+' \(.*\)$))")) ;; the regex for command output
7 (filtered-output ""))
8
9 ;; Step 1: Run the command
10 (setq command-output (shell-command-to-string command))
11
12 ;; Step 2: Create a temporary buffer and copy the command output
13 (with-temp-buffer
14 (insert command-output)
15
16 ;; Step 3: Create commit message by appending matching lines to filtered-output
17 (goto-char (point-min))
18 (while (re-search-forward regex nil t)
19 (setq filtered-output (concat filtered-output "\n" (match-string 0))))
20
21 ;; Step 4: Create a git commit message with prepared filtered-output
22 (vc-git-checkin '("flake.lock") (concat "flake.lock: " (format-time-string "%Y_%m_%d") "\n\n" filtered-output)))))
This function relies on awesome pcre2el package, because I know PCRE and Elisp regex1 syntax is just similar enough but not quite that I can never remember it. The other dependency being built-in vc
package, which shouldn't be a problem for any modern distro user2