Chezmore Chezmoi

In a previous article, I briefly touched on the idea of using chezmoi to manage syncing my zsh settings across machines. This has worked pretty well, but there were things that I still needed to do manually on each machine that I wanted to keep in sync. This is, naturally, something that gets the grumps going - we should be able to automate this! So, rolling up my sleeves for the New Year, I dug a bit further into chezmoi to see what could be done about it.

What Are You Doing?

To start with, let’s take a look at what chezmoi is managing for us:

>>> chezmoi managed
.gitconfig
.zshrc

OK, so nothing too surprising here. It’s already worked out that my ZSH and git configs need to be synced. But we’d like to try managing some non-dotfiles as well. How can we go about this? The online manual isn’t hugely helpful here - although there is a section on managing different types of file, it doesn’t really tell you how to do that! (although it does give us a few hints as to some of the other things that it can do.) So, let’s turn to old faithful:

chezmoi --help


>>> chezmoi --help
Manage your dotfiles across multiple diverse machines, securely

Usage:
  chezmoi [command]

Available Commands:
  add                  Add an existing file, directory, or symlink to the source state
...

So, the first command ends up sounding pretty close to what we want to do. Let’s give it a go! We’ve got a folder for scripts and other things called $HOME/bin, so let’s try adding the script to install the various utilities I want to install on each machine to chezmoi:

>>> chezmoi add bin/install_files.zsh
>>> chezmoi managed                  
.gitconfig
.zshrc
bin
bin/install_files.zsh

This looks promising! Let’s double-check that it’s all in the right place:

>>> chezmoi cd
>>> ls
bin  dot_gitconfig  dot_zshrc
>>> ls bin
executable_install_files.zsh

Nice! It’s tagged the shell script as an executable, and also ignored all the other various scripts I had in there, so it looks like it should happily merge these onto any target machine I choose. I went ahead and committed these to git, and all was mostly well in the world.

Managing Machine-Specific Settings

The next funky feature that I found useful was the ability to have chezmoi manage scripts with specific behaviour for different machines. This is all managed via the use of templates, which allow you to do something like the following (you can tell that the example is taken from the chezmoi template documentation, as I would rather gouge out my internal organs with a toothpick than use vi):

# common config
export EDITOR=vi

# machine-specific configuration
{{- if eq .chezmoi.hostname "work-laptop" }}
# this will only be included in ~/.bashrc on work-laptop
{{- end }}

You can see that the example uses the hostname to drive this particular bit of config, but there’s a ton of other settings that can be used to drive the output:

>>> chezmoi data

{
  "chezmoi": {
    "arch": "amd64",
    "commandDir": "/home/obfuscated",
    "configFile": "/home/obfuscated/.config/chezmoi/chezmoi.toml",
    "executable": "/home/obfuscated/bin/chezmoi",
    "fqdnHostname": "MyNameGoesHere",
    "hostname": "MyNameGoesHere",
    "kernel": {
      "osrelease": "5.15.0-91-generic",
      "ostype": "Linux",
      "version": "#101-Ubuntu SMP Tue Nov 14 13:30:08 UTC 2023"
    },
    "os": "linux",
    "osRelease": {
      "id": "linuxmint",
      "idLike": "ubuntu debian",
      "name": "Linux Mint",
      "prettyName": "Linux Mint 21.2",
      "privacyPolicyURL": "https://www.linuxmint.com/",
      "supportURL": "https://forums.linuxmint.com/",
      "ubuntuCodename": "jammy",
      "version": "21.2 (Victoria)",
      "versionCodename": "victoria",
      "versionID": "21.2"
    },
    "pathListSeparator": ":",
    "pathSeparator": "/",
    "windowsVersion": {},
  }
}

I’ve only included a few of the available options here, but as you can see, it’s pretty comprehensive. This makes it nice and easy to add per-machine settings with minimal fuss.

The only wrinkle here is that these are used with templates, while the dotfiles I was using weren’t templates as far as chezmoi was concerned. Luckily, this is easy to fix:

>>> chezmoi chattr +template ~/.zshrc

>>> chezmoi cd
>>> ls -R
bin  dot_gitconfig.tmpl  dot_zshrc.tmpl

./bin:
executable_install_files.zsh.tmpl

>>> chezmoi managed                  
.gitconfig
.zshrc
bin
bin/install_files.zsht

This renames the file in your chezmoi folder to .tmpl to indicate that it’s a template now, but as far as chezmoi is concerned, the same target files are being managed.

Templating is controlled via the Go text/template syntax (with the sprig extension applied), which is pretty powerful overall. I’ve moved the installation of Oh-My-Posh to be Linux-only, and have added some machine specific aliases to the template file too. When you do a chezmoi apply, the template is applied and the output is written to the file being controlled.

Some Last Minute Touchups…

At this stage, I’m nearly happy with everything. The last thing that I’ve tidied up isn’t quite a chezmoi thing though. It’s the use of difftastic for diffing in git (and other programs, like the Jetbrains IDEs I use). Although it’s still under development, it seems pretty spiffy so far. I added this to my binary installation script, then updated my .gitconfig to add the following:

[diff]
        external = difft

This means that difftastic is now used by default for my git diff output. Adding this into chezmoi and syncing it across machines now means that I’ll have consistent diffing wherever I’m coding.

Eating Pudding

Of course, the important part is testing that this all works OK across different machines. Deploying to my OMV server worked just fine, but my Ubuntu-based laptop had a bit of weirdness, mainly due to a setting for Python that I hadn’t come across before. The 3.11 installation was marked as EXTERNALLY MANAGED, which means that you can’t by default install Python packages globally for a machine. This isn’t really a problem, all I had to do was make the Chezmoi template for binary installation work a bit differently on my laptop - which is exactly what the templates are for! And other than that minor wrinkle, it all worked just fine.

I now have a nicely synced terminal setup across the main Linux machines that I work on. I haven’t touched my Windows installation much since switching to Mint, so even though Chezmoi supports Windows, I don’t see much point in making it work for that machine.

It took a little effort and more than a few grumps to get it all setup, but now that it is, I can easily switch between machines and have them all look and feel the same, which is a big win for my productivity. Not a bad start to 2024!