| Crates.io | allenap-unison-confgen |
| lib.rs | allenap-unison-confgen |
| version | 0.3.0 |
| created_at | 2024-05-10 09:59:22.347432+00 |
| updated_at | 2025-08-07 21:35:55.830205+00 |
| description | Generate Unison configuration files from a TOML description |
| homepage | |
| repository | https://github.com/allenap/unison-confgen |
| max_upload_size | |
| id | 1235881 |
| size | 981,998 |
This helps me generate my Unison configuration files.
Unison does bidirectional file synchronisation. I write a configuration file,
run unison name-of-config-file, and Unison synchronises files between here
and there. The there can be another directory, or it can be another machine
reachable over SSH. This is typically how I use it.
I have a few machines that I keep in sync. I don't have a central server that
serves as a source of truth, so I'm usually syncing back and forth from a few
different machines. One day I'll run unison on machine A to sync to machine B,
and another day I'll do the reverse, then also to machine C, and combinations of
those. It follows that I want to have the same configuration on every machine
– so I use Unison itself to sync those configuration files around.
I also have different sets of files that I want to sync. I want to sync dotfiles up to my server in the cloud, but not my financial records. I want to sync my coding projects between my laptop and my Linux machine under the desk, but not my photos.
This script takes a single configuration file – read from stdin – that describes all the hosts that I have. For example:
# schemes.toml
[hosts.alice]
hostname = "alice.example.com"
home = "/home/gavin"
sets = ["common", "financial", "github"]
[hosts.bob]
hostname = "bob.example.org"
home = "/Users/gavin"
sets = ["common", "github", "photos"]
[hosts.carol]
hostname = "carol.example.net"
home = "/home/gavin"
sets = ["common", "financial", "photos"]
This configuration file describes three hosts – alice, bob, and carol – and the sets of files that each syncs.
When I run unison-confgen < schemes.toml on alice, two configuration files
are generated:
alice-bob.prfalice-carol.prfThese contain standard Unison configuration directives to sync between hosts for the sets of files that those hosts have in common.
Now I can type unison alice-bob or unison alice-carol to:
common and github sets between alice and bob, orcommon and financial sets between alice and carol.Likewise, bob will have bob-alice.prf and bob-carol.prf, and carol will
have carol-alice.prf and carol-bob.prf.
A set file lives in a sets subdirectory. It's named exactly the same as the
name of the set; there's no file extension. It contains Unison configuration
directives. It doesn't have to describe a set of files, but typically it might
look like:
# sets/github
path = GitHub
ignore = Regex GitHub/.*/target
ignore = Name .overmind.sock
Paths, like GitHub above, are resolved relative to the home setting from
schemes.toml.
These set files are included verbatim in the generated configuration files, with
one addition: one can use an include directive to include another set file:
# sets/all-code
include github
include gitlab
include projects
I put the unison-confgen binary on PATH – typically because I've used cargo install to install it, and Cargo's bin directory is already on my PATH.
Then, in ~/.unison, I have the etc/schemes.toml configuration file, a sets
subdirectory, and a Makefile to drive it:
.PHONY: all
all: clean gen
.PHONY: gen
gen:
@type unison-confgen >/dev/null || cargo install allenap-unison-confgen
unison-confgen < etc/schemes.toml
.PHONY: clean
clean:
@$(RM) $(wildcard *.prf)
.PHONY: complete
complete: clean gen
@echo $(basename $(wildcard *.prf))
Running make -C ~/.unison clears away all .prf files and regenerates them.
The complete target is useful for shell completion. I use Bash with the
following code in my ~/.bashrc:
# Unison stuff
# shellcheck disable=SC2317
_unison() {
local cur words
# The current word due for completion.
cur=${COMP_WORDS[COMP_CWORD]}
# Non-local array storing the possible completions.
COMPREPLY=()
case "${cur}" in
-*)
words="$(unison -help | awk '/^ -/ { print $1 }')"
;;
*)
words="$(make -sC ~/.unison complete)"
;;
esac
mapfile -t COMPREPLY < <(compgen -W "${words}" -- "${cur}")
return 0
}
for _unison in $(compgen -c unison)
do
complete -F _unison "$_unison"
done
unset _unison
# Override local hostname. Useful when WiFi publishes a different
# local domain (<cough>FritzBox</cough>), for example.
if [ -f ~/.unison/hostname ]
then
read -r UNISONLOCALHOSTNAME < ~/.unison/hostname &&
export UNISONLOCALHOSTNAME
fi
That's all. It's simple but effective; I've been using it for years. Recently I rewrote it in Rust – from Python – and this is the first time I've published it. Since it does all I need, I probably won't work much on it. Well, maybe a rainy day will see me:
Makefile.But I'm not holding my breath.
Have fun!
GNU General Public License 3.0 (or later). See LICENSE.