gemini.git

going-flying.com gemini git repository

summary

tree

log

refs

cbb7ce635bbb441aab48008196477bb40cd20125 - Matthew Ernisse - 1620156270

new post

view tree

view raw

diff --git a/users/mernisse/articles/23.gmi b/users/mernisse/articles/23.gmi
new file mode 100644
index 0000000..474f549
--- /dev/null
+++ b/users/mernisse/articles/23.gmi
@@ -0,0 +1,142 @@
+---
+Title:		Shell Quickie: Trying Emacs, dotfile update
+Date:		2021-05-04 14:58
+
+## Managing Environments
+A tremendous 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 migrate over to
+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).
+
+=> 14.gmi Breakdown of my .profile
+
+## In Comes Emacs
+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.
+
+### post-receive hook
+```
+#!/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.
+
+### .profile update
+```
+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.
+
+## Results
+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).