blog.lazkani.io/content/posts/dotfiles-with-chezmoi.md

343 lines
12 KiB
Markdown
Raw Permalink Normal View History

+++
title = "Dotfiles with Chezmoi"
author = ["Elia el Lazkani"]
date = 2020-10-05
lastmod = 2020-10-05
tags = ["dotfiles", "chezmoi", "encryption", "templates"]
categories = ["backup"]
draft = false
+++
A few months ago, I went on a search for a solution for my _dotfiles_.
I tried projects likes [GNU Stow](https://www.gnu.org/software/stow/), [dotbot](https://github.com/anishathalye/dotbot) and a [bare _git_ repository](https://www.atlassian.com/git/tutorials/dotfiles).
Each one of these solutions has its advantages and its advantages, but I found mine in [_Chezmoi_](https://www.chezmoi.io/).
_Chezmoi_ ? That's **French** right ? How is learning **French** going to help me ?
<!--more-->
## Introduction {#introduction}
On a _\*nix_ system, whether _Linux_, _BSD_ or even _Mac OS_ now, the applications one uses have their configuration saved in the user's home directory. These files are called _configuration_ files. Usually, these configuration files start with a `.` which on these systems designate hidden files (they do not show up with a simple `ls`). Due their names, these _configuration_ files are also referred to as _dotfiles_.
<div class="admonition note">
<p class="admonition-title">Note</p>
I will be using _dotfiles_ and _configuration files_ interchangeably in this article, and they can be thought as such.
</div>
One example of such files is the `.bashrc` file found in the user's _home directory_. It allows the user to configure _bash_ and change some behaviours.
Now that we understand what _dotfiles_ are, let's talk a little bit about the _previously mentioned_ solutions.
They deserve mentioning, especially if you're looking for such solution.
### GNU Stow {#gnu-stow}
_GNU Stow_ leverages the power of _symlinks_ to keep your _configuration_ in a **centralized** location.
Wherever your repository lives, _GNU Stow_ will mimic the internal structure of said repository in your **home directory** by _smartly symlinking_ everything.
I said _smartly_ because it tries to **minimize** the amount of _symlinks_ created by _symlinking_ to common root directories if possible.
By having all your configuration files under one directory structure, it is easier to push it to any public repository and share it with others.
The downsize is, you end-up with a lot of _symlinks_. It is also worth mentioning that not all applications behave well when their _configuration directories_ are _symlinked_. Otherwise, _GNU Stow_ is a great project.
### Dotbot {#dotbot}
_Dotbot_ is a _Python_ project that **aims** at automating your _dotfiles_. It gives you great control over what and how to manage your _dotfiles_.
Having it written in _Python_ means it is very easy to install; `pip`. It also means that it _should_ be easy to migrate it to different systems.
_Dotbot_ has a lot going for it. If the idea of having control over every aspect of your _dotfiles_, including the _possibility_ of the setup of the environment along with it, then _dotbot_ is for you.
Well, it's not for **me**.
### Bare _Git_ Repository {#bare-git-repository}
This is arguably the _most elegant_ solution of them all.
The nice thing about this solution is its _simplicity_ and _cleanliness_. It is _essentially_ creating a _bare git_ repository _somewhere_ in your _home directory_ specifying the _home directory_ itself to be the _working directory_.
If you are wondering where one would use a _bare git_ repository in real life other than this use case.
Well, you have no other place to turn than any _git server_. On the server, _Gitea_ for example, your repository is only a _bare_ repository. One has to clone it to get the _working directory_ along with it.
Anyway, back to our topic. This is a great solution if you don't have to worry about things you would like to hide.
By hide, I mean things like _credentials_, _keys_ or _passwords_ which **never** belong in a _repository_.
You will need to find solutions for these types of files. I was looking for something _less involving_ and _more involved_.
## _Chezmoi_ to the rescue ? {#chezmoi-to-the-rescue}
Isn't that what they **all** say ?
I like how the creator(s) defines [_Chezmoi_](https://www.chezmoi.io/)
> Manage your dotfiles across multiple machines, securely.
Pretty basic, straight to the point. Unfortunately, it's a little bit harder to grasp the concept of how it works.
_Chezmoi_ basically _generates_ the _dotfiles_ from the _local repository_. These _dotfiles_ are saved in different forms in the _repository_ but they **always** generate the same output; the _dotfiles_. Think of _Chezmoi_ as a _dotfiles_ templating engine, at its basic form it saves your _dotfiles_ as is and _deploys_ them in **any** machine.
## Working with _Chezmoi_ {#working-with-chezmoi}
I think we should take a _quick_ look at _Chezmoi_ to see how it works.
_Chezmoi_ is written _Golang_ making it _fairly_ easy to [install](https://www.chezmoi.io/docs/install/) so I will forgo that boring part.
### First run {#first-run}
To start using _Chezmoi_, one has to **initialize** a new _Chezmoi repository_.
```bash
chezmoi init
```
This will create a **new** _git repository_ in `~/.local/share/chezmoi`. This is now the **source state**, where _Chezmoi_ will get your _dotfiles_.
### Plain _dotfiles_ management with _Chezmoi_ {#plain-dotfiles-management-with-chezmoi}
Now that we have a _Chezmoi_ repository. We can start to _populate_ it with _dotfiles_.
Let's assume that we would like to start managing one of our _dotfiles_ with _Chezmoi_.
I'm going with an _imaginary application_'s configuration directory.
This directory will hold different files with _versatile_ content types.
This is going to showcase some of _Chezmoi_'s capabilities.
<div class="admonition note">
<p class="admonition-title">Note</p>
This is how I use _Chezmoi_. If you have a better way to do things, I'd like to hear about it!
</div>
#### Adding a _dotfile_ {#adding-a-dotfile}
This **DS9** application has its directory configuration in `~/.ds9/` where we find the `config`.
The configuration looks like any _generic_ _ini_ configuration.
```ini
[character/sisko]
Name = Benjamin
Rank = Captain
Credentials = sisko-creds.cred
Mastodon = sisko-api.mastodon
```
_Nothing_ special about this file, let's add it to _Chezmoi_
```bash
chezmoi add ~/.ds9/config
```
#### Listing _dotfiles_ {#listing-dotfiles}
And _nothing_ happened... Hmm...
```bash
chezmoi managed
```
```text
/home/user/.ds9
/home/user/.ds9/config
```
Okay, it seems that it is being managed.
#### Diffing _dotfiles_ {#diffing-dotfiles}
We can _test_ it out by doing something like this.
```bash
mv ~/.ds9/config ~/.ds9/config.old
chezmoi diff
```
```text
install -m 644 /dev/null /home/user/.ds9/config
--- a/home/user/.ds9/config
+++ b/home/user/.ds9/config
@@ -0,0 +1,5 @@
+[character/sisko]
+Name = Benjamin
+Rank = Captain
+Credentials = sisko-creds.cred
+Mastodon = sisko-api.mastodon
```
Alright, everything looks as it should be.
#### Apply _dotfiles_ {#apply-dotfiles}
But that's only a _diff_, how do I make _Chezmoi_ apply the changes because my _dotfile_ is still `config.old`.
Okay, we can actually get rid of the `config.old` file and make _Chezmoi_ regenerate the configuration.
```bash
rm ~/.ds9/config ~/.ds9/config.old
chezmoi -v apply
```
<div class="admonition note">
<p class="admonition-title">Note</p>
I like to use the `-v` flag to check what is **actually** being applied.
</div>
```text
install -m 644 /dev/null /home/user/.ds9/config
--- a/home/user/.ds9/config
+++ b/home/user/.ds9/config
@@ -0,0 +1,5 @@
+[character/sisko]
+Name = Benjamin
+Rank = Captain
+Credentials = sisko-creds.cred
+Mastodon = sisko-api.mastodon
```
And we get the same output as the `diff`. Nice!
The configuration file was also recreated, that's awesome.
#### Editing _dotfiles_ {#editing-dotfiles}
If you've followed so far, you might have wondered... If I edit `~/.ds9/config`, then _Chezmoi_ is going to **override** it!
**YES**, **yes** it will.
<div class="admonition warning">
<p class="admonition-title">warning</p>
Always use _Chezmoi_ to edit your managed _dotfiles_. Do **NOT** edit them directly.
**ALWAYS** use `chezmoi diff` before every _applying_.
</div>
To _edit_ your managed _dotfile_, simply tell _Chezmoi_ about it.
```bash
chezmoi edit ~/.ds9/config
```
_Chezmoi_ will use your `$EDITOR` to open the file for you to edit. Once saved, it's saved in the _repository database_.
Be aware, at this point the changes are not reflected in your _home_ directory, **only** in the _Chezmoi source state_. Make sure you **diff** and then **apply** to make the changes in your _home_.
### _Chezmoi_ repository management {#chezmoi-repository-management}
As mentioned previously, the repository is found in `~/.local/share/chezmoi`.
I **always** forget where it is, luckily _Chezmoi_ has a solution for that.
```bash
chezmoi cd
```
Now, we are in the repository. We can work with it as a _regultar_ _git_ repository.
When you're done, don't forget to `exit`.
### Other features {#other-features}
It is worth mentioning at this point that _Chezmoi_ offers a few more integrations.
#### Templating {#templating}
Due to the fact that _Chezmoi_ is written in _Golang_, it can leverage the power of the _Golang [templating](https://www.chezmoi.io/docs/how-to/#use-templates-to-manage-files-that-vary-from-machine-to-machine)_ system.
One can replace _repeatable_ values like **email** or **name** with a template like `{{ .email }}` or `{{ .name }}`.
This will result in a replacement of these _templated variables_ with their real values in the resulting _dotfile_.
This is another reason why you should **always** edit your managed _dotfiles_ through _Chezmoi_.
Our _previous_ example would look a bit different.
```ini
[character/sisko]
Name = {{ .sisko.name }}
Rank = {{ .sisko.rank }}
Credentials = sisko-creds.cred
Mastodon = sisko-api.mastodon
```
And we would add it a bit differently now.
```bash
chezmoi add --template ~/.ds9/config
```
<div class="admonition warning">
<p class="admonition-title">warning</p>
Follow the [documentation](https://www.chezmoi.io/docs/how-to/#use-templates-to-manage-files-that-vary-from-machine-to-machine) to _configure_ the **values**.
</div>
#### Password manager integration {#password-manager-integration}
Once you have the power of _templating_ on your side, you can always take it one step further.
_Chezmoi_ has integration with a big list of [password managers](https://www.chezmoi.io/docs/how-to/#keep-data-private). These can be used directly into the _configuration files_.
In our _hypothetical_ example, we can think of the _credentials_ file (`~/.ds9/sisko-creds.cred`).
```init
Name = {{ (keepassxc "sisko.ds9").Name }}
Rank = {{ (keepassxc "sisko.ds9").Rank }}
Access_Code = {{ (keepassxc "sisko.ds9").AccessCode }}
```
Do not _forget_ that this is also using the _templating_ engine. So you need to add as a _template_.
```bash
chezmoi add --template ~/.ds9/sisko-creds.cred
```
#### File encryption {#file-encryption}
Wait, what ! You almost slipped away right there old fellow.
We have our _Mastodon_ **API** key in the `sisko-api.mastodon` file. The whole file cannot be pushed to a repository.
It turns out that _Chezmoi_ can use _gpg_ to [encrypt your files](https://www.chezmoi.io/docs/how-to/#use-gpg-to-keep-your-secrets) making it possible for you to push them.
To add a file encrypted to the _Chezmoi_ repository, use the following command.
```bash
chezmoi add --encrypt ~/.ds9/sisko-api.mastodon
```
#### Misc {#misc}
There is a list of other features that _Chezmoi_ supports that I did not mention.
I did not use all the _features_ offered yet. You should check the [website](https://www.chezmoi.io/) for the full documentation.
## Conclusion {#conclusion}
I am fully migrated into _Chezmoi_ so far. I have used all the features above, and it has worked flawlessly so far.
I like the idea that it offers **all** the features I need while at the same time staying out of the way.
I find myself, often, editing the _dotfiles_ in my _home_ directory as a _dev_ version. Once I get to a configuration I like, I add it to _Chezmoi_. If I ever mess up badly, I ask _Chezmoi_ to override my changes.
I understand it adds a little bit of _overhead_ with the use of `chezmoi` commands, which I aliased to `cm`. But the end result is a _home_ directory which seems untouched by any tools (no symlinks, no copies, etc...) making it easier to migrate _out_ of _Chezmoi_ as a solution and into another one if I ever choose in the future.