🏡

ローカル環境の管理戦略を一新しよう

に公開

overview

intro

経緯

今まで環境構築時に、ansible + dotfile リポジトリ + 独自スクリプトを用いて

  • dotfiles の配置
  • git prompt (powerline 等) の便利ツール配置

をしていました。

課題感

しかしながら、以下の理由から煩わしさを感じていました。

  • 都度 playbook を記述するのが面倒
    • role 作って配置して... など
  • 配置した機能の on/off をするのが面倒

業務 PC がモタつくのもあり、新 PC の乗り換えが決まったので
ローカル環境管理を見直すことにしました。

新しい管理戦略

方針

おおまかに以下をゴールに設定しました。

  • マルチ OS (MacOS / WSL2 ≒ linux) に対応できるようにする
  • ファイルの配置に独自スクリプトを使用しない
  • dotfiles の管理を簡素化する

選定したツール

Homebrew

https://brew.sh
言わずと知れたパッケージマネージャ。mac 以外にも linux であれば利用可能。

chezmoi

https://www.chezmoi.io/
ドットファイル管理ツール。複数 OS 対応

asdf

https://asdf-vm.com
CLI のバージョン管理ツール。terraform など頻繁にバージョンを出し入れする場合に使用

解説

Homebrew

今までパッケージマネージャは OS にデフォルトで備わるものを利用していました。
あくまでローカル環境の管理なので、ポータビリティを優先して Homebrew を使うことにします。
新しい業務 PC が Mac になるかもしれないというのも理由の一つ。

chezmoi

(読み:シェモア)
今回一番推したいツール。
dotfile そのものの管理 + 配置用コマンド が用意されているため
./init.sh みたいなものを手書きする必要がない。

asdf

これも環境差分吸収の意味合いが大きいです。
あと『terraform は tfenv』『golang は goenv』みたいに、都度〇〇env を入れるのが不毛に思えたので...

ポイント

bashrc zshrc のフラグメント化

例えば go のツールや git のユーティリティを入れた場合
都度 bashrc, zshrc に追加の記述が必要になると思います。

今までもフラグメント化のアイデア自体はあって

  • ansible テンプレートを作成
  • /etc/profile.d/ に配置

で対応してたのですが、dotfile 側にディレクトリ設けてまるっと読み込ませればよくね?
と思い、そうすることに。

ディレクトリ構造

~/.bashrc or ~/.zshrc から ~/.shellrc.d 配下のファイルを読むように記述
*.sh なら bash, zsh 共通 *.{bash,zsh} なら対応した SHELL から読まれる仕組み。
ファイル名順に読み込ませるようにしたので、順番の制御も可能です。

~/.zshrc の場合
### Write your commands to ~/.shellrc.d/xxx.{sh,zsh}
if [ -d ~/.shellrc.d ]; then
  for i in `find ~/.shellrc.d/*.{sh,zsh} -type f | sort`; do
    if [ -r $i ]; then
      . $i
    fi
  done
  unset i
fi
樹形図
boomkun: ~ $ eza -TA -L 2 ~/.
/home/boomkun
├── .bashrc
├── .shellrc.d
│   ├── 010_aliases.sh
│   ├── 010_brew.sh
│   ├── 010_environment.sh
│   ├── 010_environment.zsh
│   ├── 010_git_util.bash.disabled
│   ├── 010_git_util.zsh
│   ├── 010_shopt.bash
│   ├── 010_vscode_integration.bash
│   ├── 010_vscode_integration.zsh
│   ├── 100_asdf.bash
│   ├── 100_asdf.zsh
│   ├── 200_go.sh
│   ├── 200_powerline-go.bash
│   ├── 200_powerline-go.zsh
│   └── 200_powerline-go.zsh.disabled
└── .zshrc

例えば

  • asdf は brew で入れるので、brew の読み込みが先
  • go は asdf で入れるので asdf の読み込みが先
    • でないと $(go env GOPATH) が通らない

などの配慮ができる。

非インストール時への配慮

この手の dotfiles 管理であるあるなのが
『スクリプトで全部入れるので、bashrc, zshrc に入っている前提で書いてしまう』
というのが考えられます。

例えば Homebrew にパスを通すとき /home/linuxbrew/.linuxbrew/bin/brew を実行するのですが
当然ながら Homebrew が入っていなければ失敗します。
なので『入ってない場合は実行しない』を {bash,zsh}rc には徹底しています。

# homebrew
if [ -f /home/linuxbrew/.linuxbrew/bin/brew ]; then
    eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
fi
# asdf install by brew on zsh
if [ -f /home/linuxbrew/.linuxbrew/opt/asdf/libexec/asdf.sh ]; then
    . "/home/linuxbrew/.linuxbrew/opt/asdf/libexec/asdf.sh"
fi

chezmoi の利用

dotfile の管理ツールということは前述したので省きます。
詳細な使い方は他の方も記事書いてらっしゃるので、そちらを参照のこと。
ここでは便利だと思ったポイントに絞って紹介。

chezmoiexternals

従来の環境構築時に ansible を利用していた理由でもあるんですが
git-prompt などを配置する際、外部からファイルをダウンロードして特定のパスに配置する必要があります。

ansible の場合
- block:
  - name: git-completion download
    get_url:
      url: https://raw.github.com/git/git/master/contrib/completion/git-completion.bash
      dest: ~/.git-completion.bash
  - name: git-prompt download
    get_url:
      url: https://raw.github.com/git/git/master/contrib/completion/git-prompt.sh
      dest: ~/.git-prompt.bash

この管理手法だと

  • dotfiles 側のリポジトリに入っていてほしいものが ansible で管理される
  • ansible をたたくまで、dotfile で定義した環境が揃わない

という辛さがありました。

~/.chezmoiexternals/git_util.toml
[".git_util/git-completion.bash"]
    type       = "file"
    url        = "https://raw.github.com/git/git/master/contrib/completion/git-completion.bash"
    executable = true

[".git_util/git-prompt.sh"]
    type       = "file"
    url        = "https://raw.github.com/git/git/master/contrib/completion/git-prompt.sh"
    executable = true

chezmoi だと上記のように記述でき、以下のようなメリットがあります。

  • dotfiles と dotfiles で読むのに必要な外部ファイルが同じリポジトリ・スキームで管理できる
  • 常に最新のファイルが参照できる

chezmoiscripts

https://www.chezmoi.io/user-guide/use-scripts-to-perform-actions/

~/.chezmoiscripts 配下にスクリプトを置くと

  • 初回 apply 時のみ
  • 変更があった場合のみ
  • 毎回の apply 時に実行

という条件付けで任意のスクリプトを実行させられます。
ユースケースとしては、brewfile を置いといてパッケージを一括で入れる などの使い方をするようです。

私は chezmoi を dotfile 管理リポジトリとして捉えているため
スクリプトを実行する役割をもたせるのは違うかなと思い、~/.Brewfile をダンプして管理するのに留めています。

新しいセットアップ手順

dotfile を整えるまでのステップは以下のようになりました。

従来の方法

  • dotfile リポジトリクローン
  • dotfile セットアップスクリプト実行
  • ansible インストール
  • ansible リポジトリクローン
  • ansible-playbook 実行

新しい方法

  • chezmoi インストール
  • chezmoi リポジトリクローン
  • chezmoi apply

なんなら ↑ の手順をワンライナーでやる方法すらあります。
詳しくは公式ページを確認

sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply $GITHUB_USERNAME

outro

やはり新環境の構築は楽しいですね。
chezmoi の外部ファイルダウンロードはかなりありがたかったです。

Discussion