dotfilesの育て方

## dotfilesを育てよう！
dotfilesはエンジニア人生と切っても切れない関係です。
そんなdotfilesを理想の形に育てていく方法について紹介したいと思います。


<details>
<summary>dotfilesとは</summary>
`.`（ドット）から始まるファイルの総称です。
Unix系OSでは`.`から始まるファイルは**隠しファイル**として扱われ、`ls`コマンド等にはオプションを指定しないと表示されない仕組みになっています[^1]。
古来より、この特徴を利用して、ホームディレクトリ直下に`.`から始まる設定ファイルを配置するという文化があります。
例えば、筆者のホームディレクトリで`ls`を実行した場合、下記のような出力となります。![dotfiles01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214526/90e5f04c-45c4-0c2a-3d8a-1f0b3596b43e.png)
隠しファイルも表示する`-a`オプションを加えて実行した結果は下記のようになります。
![dotfiles02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214526/11701cbd-88fa-541e-c092-83171267eb15.png)
`.`で始まるファイルが大量に表示されています。
要はこれらがdotfilesです。
</details>

## Lv.1 Gitで管理しよう
まずは最初の一歩として、dotfilesをGit管理するところから始めましょう。
履歴管理やポータビリティの向上等様々な恩恵があります。(草を生やせたりも)

dotfilesというリポジトリを作成し、`.bashrc`等の各種dotfilesをリポジトリ配下にコミットします。
ホームディレクトリには`ln`コマンドを用いて[シンボリックリンク](https://ja.wikipedia.org/wiki/%E3%82%BD%E3%83%95%E3%83%88%E3%83%AA%E3%83%B3%E3%82%AF)で配置します。
**シンボリックリンクを作成することで、リポジトリ内のファイルで一元管理できるため、更新や最新化が簡単にできるようになります。**

```console:.bashrcのシンボリックリンク作成の例
# リンクの作成
$ cd path/to/dotfiles
$ ln -s .bashrc ~/.bashrc

# リンクを確認
$ ls -l ~/.bashrc
lrwxrwxrwx 1 reireias reireias 59  6月  8 22:30 /home/reireias/.bashrc -> /home/reireias/dev/src/github.com/reireias/dotfiles/.bashrc
```

なお、GitHubでは実に多くのdotfilesリポジトリが公開されています。
https://github.com/topics/dotfiles

**ちなみに、`AWS_ACCESS_KEY_ID`等の認証情報が公開されてしまわないように注意しましょう**

## Lv.2 deployを簡単にしよう
シンボリックリンクの作成は、その環境に対して1度しか実施しませんが、対象となるdotfileが増えれば、その作業も効率化したくなるのがエンジニアの性というものです。

簡単なshell scriptで実装することができます。
shell script以外で実装してもよいのですが、クリーンな環境にインストールする際に依存するパッケージが少ないほど楽なので、大抵の環境で利用できるshell scriptがよく採用されています。

以下のスクリプトでは、dotfilesリポジトリ直下の`.`ではじまるファイル全てのシンボリックリンクをホームディレクトリに作成します。

```shell:install.sh
#!/bin/bash -e

IGNORE_PATTERN="^\.(git|travis)"

echo "Create dotfile links."
for dotfile in .??*; do
    [[ $dotfile =~ $IGNORE_PATTERN ]] && continue
    ln -snfv "$(pwd)/$dotfile" "$HOME/$dotfile"
done
echo "Success"
```

## Lv.3 インストールスクリプトを書こう
dotfileによっては、パッケージやライブラリのインストールが必要となるものもあるでしょう。

例えば、

- `.tmux.conf`があるので、tmuxをインストールしたい
- `peco`コマンドを利用する自作関数が`.bashrc`に定義してある
- vimの特定のプラグインがpythonのライブラリに依存している

等があげられます。

はじめのうちはREADMEに依存パッケージの一覧を記載したりしているでしょう。
やっぱり自動化するのがエンジニアですね。

例えばshell scriptであれば下記のようにシンプルにコマンドを列挙していくだけでも、立派なインストールスクリプトになります。

```shell:インストールスクリプトの一例
#!/bin/bash

brew install tmux peco neovim
pip install neovim
```


## Lv.4 インストールスクリプトの冪等性を意識しよう
[冪等性](https://ja.wikipedia.org/wiki/%E5%86%AA%E7%AD%89)を意識したインストールスクリプトにしてみましょう。
何度実行しても同じ結果になるようにすることで、新たにインストールスクリプトで行う処理を追加した場合でも既存の環境に対してインストールスクリプトを実行することが可能です。

簡単な例を下記に示します。

```shell
# 冪等性なし
mv config /etc/hoge/config
echo "some setting" >> ~/.hogerc

# 冪等性あり
cp config /etc/hoge/config
if ! grep -q "^some setting$" ~/.hogerc; then
  echo "some setting" >> ~/.hogerc
fi
```

インストールスクリプトが大きくなるほど、冪等性をshell scriptで担保するのは難しくなってくるので、[Ansible](https://www.redhat.com/ja/technologies/management/ansible)や[Chef](https://www.chef.io/)等の構成管理ツールを使ってインストールスクリプトを記述するのも良い選択と言えるでしょう。

ただ、shell script以外のツールを利用し始めると、まっさらな環境にインストールする際の手順が多少増えてしまうので、注意が必要です。


## Lv.5 CIをまわそう
猫も杓子も[CI](https://ja.wikipedia.org/wiki/%E7%B6%99%E7%B6%9A%E7%9A%84%E3%82%A4%E3%83%B3%E3%83%86%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)な世の中です。
dotfilesにおいてもCIを回しましょう。

インストールするパッケージやライブラリのバージョンを指定せず、最新版をインストールするようなスクリプトの場合、非互換な更新によりインストールスクリプトが失敗するようになることもあるでしょう。
インストールスクリプトの実行や適切にファイルが配置されたか等を確認するのがよいでしょう。

GitHub Actionsでのごくごくシンプルなサンプルコードを記載しておきます。

```yaml:.github/workflows/main.yml
---
name: main

on: [push]

jobs:
  main:
    runs-on: ubuntu-latest # or macOS-latest
    steps:
      - uses: actions/checkout@v1
      - name: install dotfiles
        run: bash install.sh
      - name: test
        run: # some tests
```

## Lv.6 ファイルを分割しよう
dotfilesが順調に成長していくと、`.bashrc`(`.zshrc`)や`.vimrc`なんかはかなり巨大になっていきます。
巨大な1ファイルは条件反射で悪とみなすのがエンジニアの常なので、なんとか分割したいものです。

幸い、`.bashrc`や`.vimrc`等は別ファイルを読み込む機能を搭載しているので、分割が容易です。

例えば以下のように`.bashrc`から別ファイルを読み込むことができます。

```bash:.bashrc
source ${HOME}/.bash/keybind.sh
source ${HOME}/.bash/alias.sh
```

## Lv.7 READMEを整備しよう
dotfilesは人に見られることでも成長します(?)
READMEを整備し、イケてるdotfilesリポジトリに仕立てましょう。

個人的には以下が記載されていると、~~パクリたく~~参考にしたくなります。

- 対象のエディタ、shell、ツール
- ターミナルのスクリーンショット
- コンセプト
- 依存するパッケージやライブラリ

## 番外編 様々な環境に対応しよう
LinuxとMac OSの両方を利用することのある~~変態~~珍しい人は、両方の環境に対応した設定やインストールスクリプトを用意すると幸せになれるのではないでしょうか？
CIもマトリックスビルド等を活用し、LinuxとMac OSの両方の環境で実行するとよいでしょう。

ちなみに、筆者は最近、AnsibleでLinuxのaptとMac OSのbrewをwhenによる分岐を利用せずに書く方法がなくて困っています。

## さいごに
じゃあ、お前のdotfilesはどうなんだよ？と言われそうなので、いちおう掲載しておきます。
そんなに期待しないでくださいね。

<a href="https://github.com/reireias/dotfiles"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/reireias/dotfiles.png" width="460px"></a>


[^1]: [Unix系の隠しファイルの仕組みは開発者のポカから生まれた悪習だった](https://qiita.com/weloan/items/d4e0135f947d7f981603)