Read the code: Isogeny
See how I use it: .bash_profile
At any one time I have at least four machines running Linux: a desktop, a laptop, a server, and a phone. Keeping the configuration for the four systems in line can be a challenge — for some programmes it will be the same for all machines, and for others there will be almost nothing in common between the server and phone.
In my most naïve approach, I kept a single file in version control and made edits to the working copy without committing them, but this is unable to receive changes from upstream. Next I kept a separate file for each machine, but this meant keeping four files perfectly just out-of-sync, and programmes often had difficulty knowing which config file to use.
The first breakthrough came when I realised I could make certain parts of my Emacs configuration dynamic:
| |
This has its limitations. It doesn't work for any programme that doesn't use a programming language to define its configuration, excluding just about anything that doesn't use Lisp, Vimscript, Lua, or shell scripts.
To my knowledge, there is no reliable way to have toml, json, or yaml files perform variable substitution or read conditionals. The closest I came was to use Bash to write the machine-specific configuration in ~/.bash_profile1 when it was run by the login shell2.
| |
I was initially hopeful that I could use the machine-specific configuration just to append to a larger, shared configuration, but it was not to be. The files don't always allow their section to be moved around on a whim, and moving a line into machine-specific configuration required one to remember to do the same for every machine. It also adds a huge amount of cruft to ~/.bash_profile, when I keep configuration for ~20 programmes this means ~150 lines of ifs and elses cluttering what should be a simple file.
Here I got the idea for Isogeny. It does the work at the same moment, but in a Clojure script and using the brilliant Selmer3 template rendering library.
Instead of appending, Isogeny uses substitution points in a template, which might be familiar to anyone who has used templates in front-end development.
| |
The values of the variables are provided by a context in an EDN file.
| |
The user should keep a different context for each machine. They can be named something like programme.<HOSTNAME>.edn so that the correct context can easily be picked using programme.$HOSTNAME.edn2.
The final result should be the rendered file in the correct position to be used by its programme.
| |
Calls to Isogeny look like so:
| |
The default context provides for a fallback should the specific context be missing, something that is useful when something has been misconfigured or when using a new machine. It can be inconvenient to have a display manager or window manager fail to launch.
Isogeny can help you prepare your config for use with Isogeny, it leaves the current config in place.
| |
In the couple of days since I wrote the first working version of Isogeny, I have had great fun coming up with new functionality and seeing just how quickly I could implement it. These include:
--add-tags- add custom tags to the renderer
--strict- fail when a value is missing
--context-string- provide context as a CLI option that overrides values in the file
--deep-merge- use deep-merge rather than merge when overriding
--verbose- provide detailed logging
--safe- will not edit or overwrite existing files
--multi-template- render multiple templates with a single call to Isogeny
--prepare- prepare config to be used with Isogeny
With these added features, I think that Isogeny fits the use cases of almost everyone conscious of configuration. Now my configuration setup looks like this, run on login:
| |
And with that, all my files are configured to this machine. Please, give it a try, submit issues and feature requests, and keep your configuration in good order.