💾 Archived View for going-flying.com › ~mernisse › 23.gmi captured on 2023-09-28 at 15:48:01. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
A tremendous amount has been written (both in natural languages and in software) about managing the various configuration files that follow you and I around systems. This is usually a *nix problem but as more and more tools begin to include Windows I suspect it will become essentially pervasive. In my case I wrote a function in my .profile that relies on a git post-receive hook to allow automatic updates of my .profile and several associated files. I find this really helpful as it also means I can bootstrap my environment this way (it handles absence as a signal to update).
For some reason I decided recently to take another look at Emacs. I have several thoughts about that but I'll leave that for another time. The reality of giving Emacs a fair shake at becoming a staple of my workflow is managing the sprawl of configuration files that seem to grow around it. I tried simply adding them to my existing dotfiles management but that got cumbersome quickly so I redesigned it. If you look at the breakdown of my .profile that I did you'll see that it operates on a list of files -- this seemed perfectly fine, but the difficult part was the git hook that generated the checksum files. It seemed a bit insane to vomit out more and more checksum files as I expanded the files under management so I decided to use just one. That changed my git post-receive hook to the following.
#!/bin/sh set -e SHADIR="/var/www/www.ub3rgeek.net" GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) calculate_hash() { if [ -z "$1" ] || [ -z "$2" ]; then echo "usage: calculate_hash filename rev" return 127 fi git show "$2:$1" | sha256sum | awk '{ print $1 }' } if [ -z "$GIT_DIR" ]; then echo >&2 "fatal: post-receive GIT_DIR not set" exit 1 fi if [ ! -f "$SHADIR/dotfiles.lst" ]; then echo > "$SHADIR/dotfiles.lst" fi while read oldrev newrev refname; do for fn in $(git show --pretty=oneline --name-only "$newrev" | sed 1d) do csums=$(sed "#${fn}#d" "$SHADIR/dotfiles.lst") csums="${csums}\n${fn}:$(calculate_hash "$fn" "$newrev")" echo "$csums" > "$SHADIR/dotfiles.lst" echo "Updated dotfiles.lst for $fn" done done
The clever bit here is that this uses sed to delete the existing checksum from the file prior to re-computing it. This lets me only compute checksums for updated files instead of regenerating them all every time. Now that we have the list of checksums available I just updated the dotfiles function in my .profile.
dotfiles() { # dotfiles to pull in format src:dst <whitespace> local _dotfiles=" ssh_config:.ssh/config tmux.conf:.tmux.conf gitconfig:.gitconfig githelpers:.githelpers emacs.d/init.el:.emacs.d/init.el emacs.d/highlight-beyond-fill-column.el:.emacs.d/highlight-beyond-fill-column.el emacs.d/mernisse-new-blog-post.el:.emacs.d/mernisse-new-blog-post.el " local _gitpath="git/?p=dotfiles.git;a=blob_plain;hb=HEAD;f=" local _url="https://ssl.ub3rgeek.net" local csum dest fn fl src fl=$(fetch_contents "https://ssl.ub3rgeek.net/dotfiles.lst") if [ -z "$fl" ]; then echo "Failed to retrieve file list" return fi # each line in the dotfiles.lst will refer to a single file in the # repository. The format will be: # file:sha256sum # # the _dotfiles parameter is in the format of: # repo_file:local_file for entry in $fl; do csum="${entry##*:}" dest="" fn="${entry%%:*}" for dotfile in $_dotfiles; do if [ "${dotfile%%:*}" = "$fn" ]; then dest="${dotfile##*:}" break fi done if [ -z "$dest" ]; then continue fi if ! check_hash "$HOME/$dest" "$csum"; then echo "Updating $dest" fetch_contents "${_url}/${_gitpath}${src}" "$HOME/$dest" fi done }
All this does it iterate through the files in the remote checksum file, compare to the list of files that we care about and then if the stored hash is different than the hash of the file on disk then we replace it.
So far it has worked swimmingly in managing the contents of my .emacs.d directory, which means that I have the same environment on the several computers that I regularly use. It doesn't require any special packages and works on Debian, OpenBSD and macOS (likely any *nix / BSD given it's all pure POSIX shell).
🚀 © MMXX-MMXXIII matt@going-flying.com