Replacing GNU Stow with Dotbot
#linux , #productivity
One of my guilty pleasures is maintaining my dotfiles. This is one project I can yak-shave and play with as much as I want without caring about the actual impact or in general the usefulness of the whole endeavour. So this weekend I decided to indulge myself.
Part of the process was cleaning up the older dotfiles and updating them to
newer preferences. And the other part (which is the subject of this blog post)
was the installation process, which is basically the process of how these files
get from my
dotfiles repository to my home folder.
There are many different ways you can do this. So far, I've been using GNU Stow for this purpose.
Stow is a useful little program to help you manage symbolic links.
Given a source directory and a target directory, it is able to add symbolic links into the target directory that point back to the source. So if you're maintaining your dotfiles in a git repository and want to have your home folder link back to them, Stow is perfect for this.
As an example, assume your
dotfiles project contains a
nvim folder for your
Neovim configuration files and that it should be linked to
Stow can do that for you if you run
stow nvim --target $HOME/.config.
If you maintain multiple dotfiles, your setup process might end up in a shell script resembling the following:
mkdir -p ~/.config/kitty stow kitty --target $HOME/.config mkdir -p ~/.config/nvim stow nvim --target $HOME/.config stow zsh --target $HOME/
While this does the job, the one thing that never really sat well with me (no actual impact, remember?) was the fact that it's a shell script.
Generally speaking, I'm allergic to shell scripts. I don't write them on a regular basis, so when I do have to work with one, I constantly need to look up the documentation of the most primitive things (eg. "bash if else syntax") which is really unproductive. Debugging shell scripts is also not the most fun activity. So in general, I try to avoid them as long as I can.
This weekend I was looking for a dotfiles manager (again, no actual impact) and found dotbot.
Dotbot is a dotfile bootstrapper that takes in a (YAML) configuration file that describes the symbolic link definitions (and some other metadata) and updates those definitions on disk. The functionality it offered looked almost exactly like what I wanted so I decided to give it a try.
The installation feels a bit clunky (the default instructions recommend
installing it as a git submodule) but luckily it's published on PyPI as a
package so we can use
pipx to install it.
$ pipx install dotbot
The next step was to remove the
stow invocations in favor of a dotbot
configuration file. The most recent version of my config file is available
here, but essentially it looks something like this:
- defaults: link: relink: true - link: ~/.config/kitty/kitty.conf: kitty/kitty.conf # ... ~/.zshrc: zsh/zshrc - create: - ~/.config/kitty
defaults section contains some default configuration options which I'm
not going to write about here as the README does a much better job of explaining
what it is for.
The real work happens inside
link, which is basically a key/value map of the
dotfile's destination and the source. This is where you can tell dotbot what
should be linked and where. Lastly, the
create section contains a list of
directories that should be created prior to any linking taking place.
With this place, dotbot can be invoked on the command line to set up all the symbolic links in one go:
$ dotbot --config-file install.conf.yaml Link exists ~/.config/kitty/kitty.conf -> /home/siddhant/Work/projects/dotfiles/kitty/kitty.conf ... Link exists ~/.zshrc -> /home/siddhant/Work/projects/dotfiles/zsh/zshrc All links have been set up Path exists /home/siddhant/.config/kitty ... All paths have been set up ==> All tasks executed successfully
This feels much cleaner than the original shell script. Of course, given how many dotfiles I have (which is not that many), the original script would have sufficed. But as I mentioned before, none of this is an exercise in usefulness, so I personally consider this change perfectly acceptable. 🌝
The one thing I'm missing from dotbot is support for encrypted files. It would be nice if you could add encrypted files to the git repository and have dotbot decrypt them automatically before putting them in place.
Note that there is a
shell directive for running shell commands. So in
theory, we could add a shell command for decrypting a sensitive file before the
symbolic links are established. Unfortunately, it seems like these commands are
run after the linking has been done, while we want the decryption to happen
This means that I've been forced to add back a
setup.sh file which looks like
# decrypt crestic password age --decrypt --identity ~/.age/keys.txt --output crestic/password crestic/password.enc # install dotbot --config-file install.conf.yaml
The first line uses
age to decrypt a password file and put it in a location
where dotbot can read it and eventually place the link in the right location.
This is less than ideal, but it works.
Overall I have really come to like dotbot. It does everything I need it to do and some more.
There is also support for plugins and there are a bunch of nice plugins out
there as well. For instance, you can manage your
crontab schedule or specify a
snap packages to be installed automatically. While not a strict
necessity, it's nice to know that these things are there.
In any case, I do see myself using dotbot going forward. At least until the next time I feel the need to yak-shave.