💾 Archived View for going-flying.com › ~mernisse › 23.gmi captured on 2024-05-10 at 10:44:53. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-01-29)

➡️ Next capture (2024-06-16)

-=-=-=-=-=-=-

[05/04/2021 @14:58]: Shell Quickie: Trying Emacs, dotfile update

Managing Environments

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).

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).

↩ back to index

backlinks [geminispace.info]

🚀 © MMXX-MMXXIII matt@going-flying.com