
2026/06/19 2:09
GNU Stow から Chezmoi への移行
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
著者は、GNU stow から chezmoi へ移行し、3 台の Mac および Linux 仮想マシン間でのドットファイル管理を簡素化し、苦痛だったシンクリンクの競合問題を解決しました。破損したリンクの代わりに、chezmoi は実際のファイルをソースディレクトリ(
~/.local/share/chezmoi)に格納し、ファイル名で特定の属性をエンコードします——具体的には dot_ がパスマッピング用の指示、private_ が制限された権限を示すもの、.tmpl は非標準パス(例:Anthropic の agent skills など)を扱う Go テンプレートです。この構成では、1 つの Git リポジトリが真実の単一ソースとして機能し、idempotent コマンドを利用して、zsh、git、shellcheck、ghostty、GitHub CLI、Claude Code、Codex agents といったツールの一貫したツリートップ構造を維持します。個人と勤務というアイデンティティの間での厳格な分離を実現するために、メインルートスクリプトによって管理される分かれた Gitconfig ファイルが使用されます。新しいデバイスのプロビジョニングは、brew install chezmoi を実行し、続いて chezmoi init --apply を実行することで処理され、この際 .chezmoiscripts/ 内にあるスクリプト(before、after、onchange イベント用の特定のフックを含む)が実行されて、環境固有の論理を処理します。最後に、埋め込まれた SHA256 ハッシュチェックを備えた Brewfile により、Homebrew パッケージは必要な場合のみアップデートされ、多様なオペレーティングシステム間での信頼性があり競合のない管理が可能となります。本文
GNU stow から chezmoi への移行:Mac dotfiles 管理の再構築
長年、GNU
stow を使用して設定ファイル(dotfiles)を管理しておりましたが、2023 年には環境について冗談めかしたタイトルでブログ記事も執筆するほどでした。しかし、複数のデバイス間をまたぐシンボリックリンクの管理は負担となり、より優れたツールを探求し、最終的には同僚から紹介された chezmoi に切り替えました。現在、必要な機能はすべて満たされており、エージェント向けスキルファイルの追跡にも採用しています。
運用環境と課題
マシン構成
私の環境には以下の 3 台の Mac が存在します。
- 「仕事用」MacBook Pro
- 「個人用」MacBook Air
- 「Mac Mini」:小規模な個人サーバーとして運用し、他の 2 台から主に SSH で接続。
これら 3 台はすべて同じ dotfiles を共有しています。また、数台の Linux ボックスを仮想マシンとして維持していますが、それらの設定は Ansible でプロビジョニングするため頻繁には必要としません。このワークフローは厳密にデスクトップ用マシン向けです。
stow を離脱した理由
stow は単一マシンの場合は非常に優れていましたが、マルチデバイス環境では以下の問題が生じました。
-
シンボリックリンクの双方向性
- stow はリンクが「双方向」に機能するモデルです。
- どのマシンでも設定ファイルを編集すると、変更はそのマシンの Git クローンを通じて直接書き込まれてしまいます。
- 結果として、数ヶ月後には Air 上で行いながら記憶していない変更が見つかり、Pro でプッシュした内容と競合します。3 つのクローンを収束させるのは面倒でした。
-
新マシンへの導入が困難
- stow は実際のファイル上へのリンク作成ができないため、新規 Mac に Homebrew やツールをインストールした時点で
や~/.zprofile
などが既に存在してしまいます。~/.gitconfig - セットアップ手順:リポジトリのクローン → 競合するファイルの手動削除 → パッケージごとの再ストウ化 → ナメ付けの記録など、手間がかかるものでした。
- stow は実際のファイル上へのリンク作成ができないため、新規 Mac に Homebrew やツールをインストールした時点で
-
機能の制限
- stow はファイルのみを扱います。
- Homebrew パッケージや macOS 自体の設定は、実行順序を管理する別々のスクリプト記述が必要です。
chezmoi の仕組みと特徴
chezmoi は
~/.local/share/chezmoi というソースディレクトリ(通常 Git リポジトリ)を使用します。
ファイル名の符号化
- 例:
を実行すると、実際のファイルはこのディレクトリにコピーされ、名前がchezmoi add ~/.zshrc
となります。dot_zshrc - ディレクトリ構造はホームディレクトリを反映し、ドット設定(dot)はすべて
プレフィックス で管理されます(例:dot_
→~/.config/gh/config.yml
)。dot_config/gh/config.yml - 名前付けは手動ではなく、
コマンドが自動的にパスから導き出してくれます。chezmoi add
メタデータと属性
プレフィックス:ファイルからグループおよびワールドへのアクセス権限を除去します(例:_private
の設定)。dot_config/gh/config.yml
サフィックス:ファイルを Go テンプレートに変換し、マシン固有のデータを参照できるようにします。.tmpl
「apply」の動作
chezmoi apply は逆方向に動作します。名前から読み取ったホームパス(例:dot_zshrc → ~/.zshrc)へ実際のファイルとして書き込みます。
- コピーはシンボリックリンクではなく、実際のファイル です。
- ソースディレクトリが唯一の情報源となります。
- ホームディレクトリのファイルとソースの一致度が保たれていない場合、
で差分を表示し、次の apply で元通りに戻してくれます。chezmoi diff
最も気に入っている点:シンボリックリンクによる自動的な双方向書き込みがないため、リポジトリへの変更は意図的に行うことが保証されます。
リポジトリの構成概要
chezmoi cd コマンドでサブシェルへ移行すると、以下の構造となります(一部のカスタマイズを除きデフォルト値を利用しています):
~/.local/share/chezmoi ├── .chezmoi.toml.tmpl # chezmoi 設定テンプレート ├── .chezmoiignore # ソースに残しホームに書込まないファイルの除外リスト ├── .chezmoiscripts # apply 時の実行スクリプト │ └── macos/ # macOS 用スクリプト群 ├── .gitignore ├── Brewfile # Homebrew パッケージ管理 ├── README.md ├── dot_agents/ # エージェント向けスキル (skills) ├── dot_claude/ # Claude Code の設定とマッピング ├── dot_codex/ # Codex の設定 (private_config.toml) ├── dot_config/ # 各種設定 (gh, ghostty など) ├── dot_gitconfig # アイデンティティを分断した git 設定 ├── dot_shellcheckrc └── dot_zsh_aliases # zshrc のソースもここに格納 └── dot_zshrc
Git とアイデンティティの分離
すべてのプロジェクトは以下の 2 つのディレクトリ下に存在し、主に gitconfig でルーターしています。
:仕事用(業務用メールアドレス)~/canvas/werk/
:個人用(個人的なメールアドレス)~/canvas/pers/
[includeIf "gitdir:~/canvas/pers/"] path = ~/.gitconfig-pers [includeIf "gitdir:~/canvas/werk/"] path = ~/.gitconfig-werk
Note: この仕組みは chezmoi テンプレティングではなく、通常の Git 機能ですが、
chezmoi はすべてのマシーン上でこれら 3 つのファイルが存在することを保証します。
マシン固有の設定
トップにある
.chezmoi.toml.tml は、マシンの名前を一度だけ入力し、~/.config/chezmoi/chezmoi.toml に記憶させます:
{{- $machineName := promptStringOnce . "machineName" "machineName" .chezmoi.hostname -}} [data] machineName = {{ $machineName | quote }}
これはリポジトリ全体における唯一のマシン固有データです。
除外ファイルの設定
.chezmoiignore には README.md, Brewfile, Brewfile.lock.json が記述されており、これらはソースディレクトリに残しつつホームディレクトリへは書き込まれません(.gitignore で lock ファイルを除外しています)。
新しい Mac のセットアップ(ブートストラップ)
最初に Homebrew を導入した後、以下のコマンドだけで全設定が可能です。
brew install chezmoi chezmoi init --apply \ --promptString machineName=mini \ https://github.com/rednafi/dotfiles.git
オプション:追跡されているファイルを即時に配置します。--apply
:対話形式ではなく、マシン名の質問に事前回答を与えます。--promptString
スクリプトの自動実行
.chezmoiscripts/ 以下のスクリプトは apply の際に自動的に実行されます。ファイル名がタイミングを制御します。
:ファイル配置前に実行。before_
:ファイル配置後に実行。after_
:最初の apply で発火し、以降は内容変更時のみ発火(onchange 手法)。run_onchange_
Homebrew & macOS 設定スクリプト
Homebrew パッケージのインストールや macOS 自体の設定は、以下の順序で処理されます。
Brewfile の管理
Brewfile ハッシュをコメントに埋め込み、ファイルが変更されたら自動的に再実行するロジックです。
#!/usr/bin/env bash # Brewfile checksum: {{ include "Brewfile" | sha256sum }} # ... elided brewfile={{ joinPath .chezmoi.sourceDir "Brewfile" | quote }} "$brew_bin" bundle check --no-upgrade --file "$brewfile" >/dev/null 2>&1 \ || "$brew_bin" bundle install --no-upgrade --file "$brewfile"
Brewfile の例:
brew "chezmoi" brew "fzf" brew "gh" brew "micro" brew "ripgrep" brew "uv" cask "claude-code" cask "codex" cask "ghostty" cask "raycast"
macOS 設定スクリプト
- マシンの初期化:ホスト名の設定、システムデフォルトの書き込み。
- アニメーション無効化:
を経由して UI アニメーションを無効にするスクリプト。defaults write
すべてのスクリプトは
Darwin チェックで始まり、Linux ボックスでの実行時は早期終了(何事も発生しません)。
日常運用のルーティン
全体の作業フローは以下の約 5 つのコマンドで行えます。
1. 編集(ソースから)
通常はソースから編集します。
chezmoi edit --apply はホームファイルを開き、エディタを閉じたときに即座に適用して書き込みます。
chezmoi edit --apply ~/.zshrc
逆方向の編集(ライブファイル)
インストーラーによる追加や、直感的な編集からライブファイルを直接改訂する場合、ホームディレクトリがソースよりも先行し、apply で変更を元に戻すことになります。これを防ぐには
chezmoi re-add を使います。
# 単一ファイルの再インポート chezmoi re-add ~/.zshrc # ※複数のファイルがソースより先に移動している場合も一括で処理可能
2. Git の共有
リポジトリの状態を整理し、Git でコミットします。
chezmoi cd git add -A git commit -m "Update dotfiles" git push exit
他のマシンを追跡するには:
chezmoi init --apply https://github.com/rednafi/dotfiles.git
3. 差分の確認と適用
確認してから適用します。
chezmoi git pull -- --autostash --rebase chezmoi diff chezmoi apply --verbose
4. Brew の同期
パッケージが Brewfile と同步外れた場合、以下のコマンドで報告および修正を行います。
brew bundle check --no-upgrade --file "$(chezmoi source-path)/Brewfile" brew outdated --greedy brew bundle cleanup --file "$(chezmoi source-path)/Brewfile"
エージェントスキルの追跡
リポジトリへの最新な追加は、LLM エージェント向けのスキルファイルです。
スキルフォルダ構造
スキルとは、
SKILL.md と必要な参照ファイルを持つフォルダです。
:フロントマター(タイトル・説明)と指示を含みます。SKILL.md- Anthropic の標準に従い、どこでも動作するようオープンスタンダードを採用しています。
環境での検出箇所
- Codex:
をデフォルトとして検出。~/.agents/skills - Claude Code:まだこの慣習に追いついておらず、
を探求します。~/.claude/skills
シンボリックリンクの統一 (dot_claude
)
dot_claudeClaude Code が標準的な
~/.agents/skills を使用していないため、chezmoi でマッピングしています。
ファイル:dot_claude/symlink_skills.tmpl
{{ .chezmoi.homeDir }}/.agents/skills
この仕組みにより、以下の 3 つの要素が協力して機能します。
- ターゲットマッピング:
を~/.claude/skills
を通じてマッピング。dot_claude/ - リンク指定:
プレフィックスにより、内容を示す場所へのシンボリックリンクとして作成するよう指示。symlink_ - テンプレート展開:
サフィックスにより、.tmpl
がマシン固有に展開されます。{{ .chezmoi.homeDir }}
結果:
lrwxr-xr-x 1 rednafi staff 29 Jun 11 17:37 /Users/rednafi/.claude/skills -> /Users/rednafi/.agents/skills
皮肉:stow を捨ててシンボリックリンクから逃れ、結局 chezmoi が管理するのは「私が必要な唯一つのシンボリックリンク」であるという状況に至りました。しかし、Anthropic の仕様追従のため仕方ありません。これで両方のエージェントが同じスキルを読み取り、Git は単一のコピーを保持できます。
利用のススメ
ここにあるものはすべて私の dotfiles リポジトリ上にあります。有用そうなものをご利用ください。