Dotfile management mit stow

Posted on So 07 August 2022 in Computer & Electronics

Jedes mal, wenn ich ein neues Laptop anschaffe, oder aus irgendwelchen Gründen das Betriebssystem neu installiere stehen drei Dinge an:

  1. System konfigurieren
  2. Daten aus Sync oder Backup kopieren
  3. Anwendungen konfigurieren

Den ersten Punkt habe ich weitgehend mit Ansible automatisiert. Das sorgt dafür, dass alle wichtigen Dinge wieder installiert werden und zentrale Funktionen wie z.B. ssh auch gleich korrekt konfiguriert sind. Programme, die ich spontan mal ausprobiere installiere ich direkt und an Ansible vorbei, aber alles was ich wichtig finde kommt in mein Workstation Playbook.

Punkt zwei ist auch kein Stress – ich habe auf Laptop und File-Server unison und synchronisiere alles von/zu letzterem. So ist die Datenwiederherstellung schnell erledigt. Und im Notfall gibt es auch noch das Server-Backup auf USB-Platte.

Bleibt Punkt drei. Unter LINUX liegen die Configs der verschiedenen Anwendungen ja in "hidden files" aka dotfiles im home-Folder des Users. Grundsätzlich könnte ich diese in die Synchronisation einschließen, aber ich habe mich bewusst dagegen entschieden, weil das zu Konflikten führt, wenn mehrere verschiedene Computer sich mit dem Server synchronisieren. Auch existieren sehr viele dotfiles die nicht backup-würdig sind, weil sie garkeine Konfiguration enthalten, sondern irgendwelche App-spezifische Daten, oder aber zu Programmen gehören, die ich längst nicht mehr nutze, oder nie wirklich konfiguriert habe. Kurz: ich möchte mich eigentlich nur mit den dotfiles befassen, die ich auch wirklich angepasst habe und nicht irgendwelche default configs sichern, die eh von selbst wieder auftauchen. Nun könnte ich den copy Mechanismus von Ansible nutzen, um meine dotfiles wieder herzustellen – so mache ich das ja auch mit den systemweiten config Files in /etc. Aber das gefällt mir nicht so richtig, weil es sich hier ja um user-spezifische Daten handelt, die sich von Nutzer zu Nutzer unterscheiden. Eigentlich wäre das für mein Laptop egal, weil ich eh der einzige Nutzer bin, aber es widerstrebt mir dennoch.

Bisher hatte ich das so gelöst, dass ich automatisch alle dotfiles (bis auf ein paar Ausnahmen wie z.B. den Firefox cache) in einem tgz Archiv sichere – in Ordnern, die den jeweiligen hostname im Namen haben, so dass ich meine verschiedenen Rechner sauber trenne kann. Im Falle einer Neuinstallation bediene ich mich dann aus dieser Sicherung und kopiere alles, was mir wichtig erscheint von Hand an den richtigen Ort. Das funktioniert und bisher ist mir noch nie was verloren gegangen. Allerdings muss ich auch jedes mal daran denken, was ich alles brauche. Und natürlich vergesse ich immer was. Das ist nicht wirklich schlimm, denn ich merke es ja spätestens wenn ich das betreffende Programm dann nutzen will und es nicht so läuft, wie gewünscht. Dann also die nächste config kopieren – geht. Und so geht das dann mit mehreren Programmen bis sich nach ein paar Tagen alles eingeschliffen hat. Geht, aber wirklich elegant ist das nicht.

Das muss doch besser gehen

Manche Leute verwenden nun git direkt in ihrem home Verzeichnis, um die dotfiles zu verwalten, aber das finde ich nur so mittel handlich. Seit einer Weile wandern ja viele config files in ~/.config/. Also das mit git verwalten? Leider sind da aber einerseits noch nicht alle drin und zum anderen ist auch dieser Ordner wieder voller Sachen, die ich nie wirklich angepasst habe und daher auch nicht sichern und verwalten will. Schöner wäre es, wenn ich alle liebevoll angepassten configs irgendwo zentral ablegen und dann davon ein git Repository daraus machen könnte – gepaart mit irgendwas, dass die dotfiles dann dahin kopiert, wo sie hin gehören.

Kurz hatte ich überlegt, mir einen solchen Ordner anzulegen und dann ein kleines Shell Skript zu basteln, mit dem ich diese Configs dann an den richtigen Ort kopieren kann. Aber dann habe ich mich erinnert, dass es schon ein gutes Tool für dieses Problem gibt, das ich zwar schon vor ewigen Zeiten gesehen, aber irgendwie nie genauer angeschaut hatte: stow.

Dieses Tool erlaubt es, alle möglichen Files zentral zu speichern und dann via Symlinks dort bereitzustellen, wo sie eigentlich liegen müssen. Das klingt elegant.

Stow

Und das geht so:

Zunächst habe ich mir einen Folder für meine dotfiles angelegt:

mkdir -p ~/it/stow/dotfiles

Und in diesem Verzeichnis legen wir nun je einen Folder pro Package an. Ein Package kann dabei beliebig viel enthalten – eine einzelne Datei, alle Dateien für eine Anwendung, oder für eine Gruppe von Anwendungen, oder auch einfach alle Files, die wir so verwalten wollen.

Ich habe mich dafür entschieden, im Wesentlichen ein Package pro Anwendung zu haben:

> ll dotfiles/
clementine
digikam
filezilla
git
gnupg
i3
i3status
Insync
keepassxc
msmtp
mutt
offlineimap
rclone
rofi
shell
ssh
thunar
unison
virtualbox
xfce4
zsh

Jeder der Folder enthält nun die gewünschten config files inkl. Pfad ab ./. Z.B.:

> tree -a i3
i3
└── .config
    └── i3
        └── config
> tree -a ssh/
ssh/
└── .ssh
    ├── authorized_keys
    ├── config
    ├── id_rsa
    └── id_rsa.pub

Um diese config Files nun an Ort und Stelle zur Verfügung zu stellen machen wir dies:

stow -t /home/phil i3
stow -t /home/phil ssh

# oder alles auf einmal
stow -t /home/phil */

Und schon finden sich an den entsprechenden Stellen Symlinks auf die echten Files in unserem dotfile Folder:

> ll ~/.ssh
authorized_keys -> ../it/stow/dotfiles/ssh/.ssh/authorized_keys
config -> ../it/stow/dotfiles/ssh/.ssh/config
id_rsa -> ../it/stow/dotfiles/ssh/.ssh/id_rsa
id_rsa.pub -> ../it/stow/dotfiles/ssh/.ssh/id_rsa.pub
known_hosts

> ll -d .config/i3
.config/i3 -> ../it/stow/dotfiles/i3/.config/i3

Das ist praktischer, als wenn die Dateien dorthin kopiert worden wären, denn nun sind eventuelle Veränderungen der Dateien im dotfile-Ordner repräsentiert.

Wollen wir nun ein Package wieder deaktivieren geht das ebenfalls ganz einfach:

stow -t /home/phil --delete ssh

Und damit wir den target Pfad nicht jedes mal angeben müssen, speichern wir den in .stowrc – entweder im home Folder, oder da wo wir auch die stow packeges haben.

echo "--target=/home/phil" > .stowrc
# Und alle installieren
stow .

# deinstallieren
stow -D .

Zuguterletzt kann es noch praktisch sein, im stow folder ein .stow-local-ignore file anzulegen – ähnlich wie man es von .gitignore kennt:

> cat .stow-local-ignore 
.git
.gitignore
todo.md
README.md

So verhindert Ihr, dass diese Dateien ebenfalls in euer home directory verlinkt werden.

Und um das Ganze ordentlich zu versionieren brauchen wir den Ordner nur unter git Kontrolle zu stellen und schon ist alles wohlorganisiert. Zudem können wir nun z.B. je einen eigenen Branch für verschiedene Computer oder unterschiedliche Situationen anlegen, oder nach Herzenslust an einer Config rumschrauben, ohne Angst haben zu müssen, die ursprüngliche (funktionierende) Konfiguration nie wieder zusammen zu kriegen...