Claude CodeでMacbookの設定をAnsibleとchezmoiに移行してみた
ある日、Macbookのキーボード設定が壊れてしまいました。日本語配列キーボードなのに英語配列だと認識されてしまいます。Claudeに尋ねて色々試しましたが駄目でした。Karabiner-Elementsでキーバインドを変更しても反映されません。Macbook上でアカウントを新規作成すると日本語配列が復活したので、おそらく今のアカウント設定が壊れてしまったのでしょう。
新規アカウントで設定し直してもよかったのですが、使わなくなったアプリケーションが残っていたり、ディスク容量が7割をこえていたので、この機に再セットアップすることにしました。
再セットアップ自体は簡単ですが、その後の設定が面倒です。macOS特有の機能は邪魔なので軒並みオフにしていますし、キーボードやトラックパッドなどの設定もチューニングしています。アプリケーションを一つずつインストールするのも手間がかかります。
Macbookの設定をAnsibleに移行する
そこでAnsibleによるInfrastructure as Code(IaC)化をすることにしました。Ansibleを選択した理由は以下です。
- Macbookの設定をコード化できる
- 冪等性がある
- コマンドのインストールが簡単
- 以前に使ったことがあり慣れている
1つ目について、Ansibleにはosx_defaultsモジュールが提供されているので、macOS特有の設定をコード化できます。アプリケーションはHomebrew Caskによるインストールで同じくコードとして落とし込めます。
2つ目について、dotfilesリポジトリに install.sh のようなインストールスクリプトは作っていたのですが、セットアップ後の初回実行用なのでほとんどメンテナンスしていません。何度も実行できるようにif文とかで条件分岐を入れるのは面倒ですが、Ansibleは冪等性が考慮されているので何度同じように実行しても安心です。
3つ目について、セットアップするためのツールをインストールするために、あれこれインストールするのは本末転倒です。AnsibleはPython製ですが、Macbookにpython3が入っていますし、Homebrewでインストールできます。
Claude Codeで現Macbookの設定状態をAnsibleに落とし込む
AnsibleでMacbookの設定をコード化できるものの、今の状態を落とし込むのは面倒です。そこで、Claude Codeにdefaults readコマンドを実行してもらい、今のMacbookの状態をAnsibleとして実行できるようにコード化してもらうことにしました。Homebrewでインストールしたものは $(brew --prefix)/bin、アプリケーションは /Applications ディレクトリをスキャンしてYAMLファイルにまとめてもらいました。
Ansibleの設定
先ほど書いたように、Claude Codeを使って現状のMacbookの設定状態をAnsibleに反映しました。設定ファイルの作成もすべてClaude Codeにお任せです。以下のようなディレクトリ構成になりました。
ansible
├── ansible.cfg
├── group_vars
│ └── all.yml
├── mac.yml
├── requirements.yml
└── roles
├── apps
│ └── tasks
│ └── main.yml
├── homebrew
│ └── tasks
│ └── main.yml
├── macos_defaults
│ ├── handlers
│ │ └── main.yml
│ └── tasks
│ └── main.yml
└── mise
└── tasks
└── main.yml
-
group_vars/all.yml: パッケージリスト-
homebrew_packages: Homebrew でインストールするパッケージ一覧 -
homebrew_cask_packages: Homebrew Caskでインストールするパッケージ一覧 -
homebrew_cask_packages_sudo: Homebrew Caskでインストールするパッケージでsudoが必要なもの -
mise_global_tools: miseによる管理一覧 -
pipx_packages:pipxでインストールされるもの
-
-
roles/apps: シェル環境で必要な設定 -
roles/homebrew: Homebrew設定 -
roles/macos_defaults: macOS特有の設定 -
roles/mise: miseの設定
実行コマンド
$ ansible-playbook mac.yml -K
# sudoパスワードを2回入力する ※ 要改善
BECOME password:
sudo パスワードを入力してください(sudo が必要な Cask をスキップする場合は空欄) []:
以下に、実装例の一部を紹介します。
homebrewの実装例
group_vars/all.yml
---
homebrew_packages:
- bat
- direnv
- eza
- ripgrep
- sk # skim
homebrew_cask_packages:
- ghostty
- intellij-idea
- visual-studio-code
homebrew_cask_packages_sudo:
- docker-desktop
roles/homebrew/tasks/main.yml
---
- name: Homebrew パッケージをインストール
community.general.homebrew:
name: "{{ homebrew_packages }}"
state: present
update_homebrew: true
- name: Homebrew Cask アプリをインストール
community.general.homebrew_cask:
name: "{{ item }}"
state: present
loop: "{{ homebrew_cask_packages }}"
ignore_errors: true
- name: sudo が必要な Homebrew Cask アプリをインストール
community.general.homebrew_cask:
name: "{{ item }}"
state: present
sudo_password: "{{ sudo_password }}"
loop: "{{ homebrew_cask_packages_sudo }}"
when: sudo_password != ""
ignore_errors: true
macos_defaultsの実装例
roles/macos_defaults/handlers/main.yaml
---
- name: Finder を再起動
shell: killall Finder
ignore_errors: true
- name: Dock を再起動
shell: killall Dock
ignore_errors: true
- name: SystemUIServer を再起動
shell: killall SystemUIServer
ignore_errors: true
- name: cfprefsd を再起動
shell: killall cfprefsd
ignore_errors: true
roles/macos_defaults/tasks/main.yml
---
# ---- Finder ----
- name: Finder で隠しファイルを表示する
community.general.osx_defaults:
domain: com.apple.finder
key: AppleShowAllFiles
type: string
value: "true"
notify: Finder を再起動
# ---- Dock ----
- name: Dock を自動非表示にする
community.general.osx_defaults:
domain: com.apple.dock
key: autohide
type: bool
value: true
notify: Dock を再起動
# ---- デスクトップ ----
- name: ネットワークドライブに .DS_Store を作らない
community.general.osx_defaults:
domain: com.apple.desktopservices
key: DSDontWriteNetworkStores
type: string
value: "true"
# ---- キーボード ----
- name: キーリピートの間隔を短くする
community.general.osx_defaults:
domain: NSGlobalDomain
key: KeyRepeat
type: float
value: 2
# ---- トラックパッド----
- name: トラックパッドの速度を設定する
community.general.osx_defaults:
domain: NSGlobalDomain
key: com.apple.trackpad.scaling
type: float
value: 2.5
# ---- メニューバー 時計 ----
- name: メニューバーの時計に AM/PM を表示する
community.general.osx_defaults:
domain: com.apple.menuextra.clock
key: ShowAMPM
type: int
value: 1
notify: SystemUIServer を再起動
# ---- キーボードショートカット (AppleSymbolicHotKeys) ----
- name: Spotlight ショートカットを無効にする (key 27)
ansible.builtin.shell: >-
defaults write com.apple.symbolichotkeys AppleSymbolicHotKeys -dict-add 27
'<dict><key>enabled</key><false/><key>value</key><dict><key>parameters</key><array><integer>64</integer><integer>33</integer><integer>1048576</integer></array><key>type</key><string>standard</string></dict></dict>'
changed_when: true
notify: cfprefsd を再起動
dotfilesをchezmoiに移行する
シェル環境は dotfiles リポジトリで管理しています。この機にdotfilesの管理を chezmoi に移行しました。今までは dotfiles リポジトリから各ドットファイルにシンボリックリンクを貼っていましたが、chezmoi なら chezmoi apply で反映できるので管理が楽です。 chezmoiの慣習に倣って dot_xxxx というファイル名にリネームしました。そのあたりもすべてClaude Codeにお任せしました。
変更が終わったら、chezmoi apply で変更完了です。
シェル環境で変えたこと
今回はすべての設定を移行したわけではなく、一部のツールを変えてみました。
- ターミナル
- iTerm2からGhosttyに移行しました。iTerm2の設定もClaude CodeでGhostty仕様に変えてもらいました
- インクリメンタルサーチ
- Rust製ツール: エイリアスでそのまま使えるようにしています
bootstrapスクリプトで一発インストールする
新規セットアップしたMacbookにこれらの設定をゼロから構築するためのbootstrapスクリプトを用意しました。次のコマンド一つですべて動くようにしています。途中でsudoパスワードの入力やGitHub CLI(gh)のログインを求められるので、完全自動化になっていません。
/bin/bash -c "$(curl -fsSL https://gist.githubusercontent.com/ohsawa0515/411eecc5dd76196bcc6d31ba31e9a924/raw/bootstrap.sh)"
最後に
動かすために3回ぐらいMacbookを初期化しました。昔EC2インスタンスをゼロから構築していたのを思い出して懐かしい気持ちになりました。
時間はかかりましたが、環境の吸い上げからコード化、コマンド実行までClaude Codeがやってくれたので、片手間でできてとても楽ちんでした。
Discussion