will and way

ただの自分用メモを人に伝える形式で書くことでわかりやすくまとめてるはずのブログ

Mac OSXの設定をAnsibleで9割以上自動化する

f:id:matsuokah:20160102014950p:plain 自分のMBPRetinaは3年前のモデルでアップデートする度に、スリープからの復帰画面がおかしくなったりしたのでOSのクリーンインストールも兼ねました。データはほとんどクラウド化されてるので残るは設定ファイルのみ。0から設定するのだるい。

今後、0スタートするときも苦労したくない!ポチポチクリックしたくない!ということで

mawatari.jp

この記事にインスパイアされて、自分もやったので、ハマりどころとかプラスで対応したところをメモ。 9割はこれで行ける、残り1割はアプリにログインした時にクラウド同期してくれる系(Chromeとか).

githubにdotfilesやらshellにするよりも比較的容易にprogramaticallyにできたのでやってよかった。

github.com

対応したこと

  • ansibleのインストール
  • brewのインストール
  • brewによるアプリのインストール
  • brew-caskによるアプリのインストール
  • defaultsコマンドによるMacの設定自動化
  • zshの設定
  • vimの設定
  • gitの設定
  • sshの設定
  • xcodeプラグインパッケージマネージャのインストール

xcodeインストールして利用規約に許諾したあとに cd /path/to/repository-top && makeでプロビジョニングが終わるようになっています。

ディレクトリ構成

provisioning ❯❯❯ tree -L 1
.
├── Context.sh
├── InstallAnsible.sh
├── Makefile
├── PlayAnsible.sh
├── README.md
└── provisioning/
  • Context.sh ... 実行環境のコンテキスト
  • InstallAnsible.sh ... pipがなければインストール、ansibleがなければインストール
  • Makefile ... InstallAnsible, PlayAnsible.shのフック
  • PlayAnsible.sh ... Ansibleの実行
  • provisioning ... Ansibleの設定集

brewのインストール

はじめに紹介したリンクではbrewでansibleをインストールしており、brewがある前提でansibleが組まれています。

もっと自動化したかったのでansibleを先にインストールし、brewのroleのタスク内でインストールするようい順番を変えました。

Ansibleのインストール

InstallAnsible.sh
which pip >/dev/null 2>&1
if [ $? -ne 0 ];
then
  echo "not found command pip"
  echo "install pip"
  sudo easy_install pip
fi

which ansible >/dev/null 2>&1
if [ $? -ne 0 ];
then
  echo "not found command ansible"
  echo "install ansible"
  sudo pip install ansible
fi

easy_installでpipをインストールし、pipでansibleをインストールしています。自分の環境ではsudoを付ける必要がありました。

改めてbrewのインストール

roles/brew/tasks/main.ymlの一部抜粋
- name: update brew
  register: result
  ignore_errors: True
  command: >
    brew update

- name: get brew installer
  get_url: url=https://raw.githubusercontent.com/Homebrew/install/master/install dest=/tmp/brew_install
  when: result|failed

- name: proceed brew installer
  command: >
    ruby /tmp/brew_install
  when: result|failed

brewのアップデートに失敗すればコマンドがないという前提のもと、なければbrewをインストールしています。

brewや後述するXcodeのAlcatraz、VimのNeoBundleのようにインストールスクリプトが準備されていると、楽ですね〜。

あとは、インストールするアプリを列挙するのみ

github.com

上記がbrew-caskでインストールできるアプリのリストですが、cloneしないと、全アプリのリストを確認できません。面倒な場合は、リポジトリ内検索でしらべるとヒットしてくれたりします。

defaultsコマンドによる設定値の変更

http://docs.ansible.com/ansible/osx_defaults_module.htmlによると

- osx_defaults:
  domain=NSGlobalDomain
  key=AppleMeasurementUnits
  type=string value=Centimeters
  state=present

osx_defaultsモジュールがある!Ansible先生有能すぎ!

ただし、Ansible2.0系以上で使えるのですがまだrc版で、2016年1月時点ではpip install ansibleでインストールされるstableなバージョンは1.9.4です。

したがって、実行するとモジュールがないと怒られてしまいます。

ansible --versionで取れるバージョンのメジャーバージョンが1ならcommand、Ansibleが2.0系ならosx_defaultsによる実行するタスクを用意しroleを分け、playbook_defaults.ymlとplaybook_defaults_v1.ymlを呼び分けるようにしました。

defaults内のmain.ymlは共有したいのでシンボリックリンクで対応。

本当は1つのroleにして、ansible_version.majorで取得した値でtaskのincludeを切り替えようと思ったのですが、includeされた設定はすべて構文チェックをされるらしくだめでした。

また、ansible_versionという変数があること自体は公式ドキュメントに記載されておらず、このissueで知りました。2系でundefinedになってしまうとのことだが、修正されているようです。

defaultsの設定

roles/defaults/defaults/main.ymlの一部を抜粋
#
# see also https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/defaults.1.html 
# 

mac_defaults_settings:
  - {
    domain: com.apple.ImageCapture,
    key: disableHotPlug,
    type: bool,
    value: false,
  }

mac_global_domain_defaults_settings:
  - {
    key: InitialKeyRepeat,
    type: int,
    value: 13,
  }
  - {
    key: KeyRepeat,
    type: int,
    value: 3,
  }

設定が増えてきたらドメイン別にファイルを分けて、includeするといいかも。

global_domainの設定に関してはドメイン指定が不要なので別リストとして持っています。あとは、これらをループして設定するタスクを書けばOK

defaults_v1/tasks/main.yml
- name: 
  command: >
    defaults write {{ item.domain }} {{ item.key }} -{{ item.type }} {{ item.value }}
  with_items: mac_defaults_settings
  when: mac_defaults_settings

- name: 
  command: >
    defaults write -g {{ item.key }} -{{ item.type }} {{ item.value }}
  with_items: mac_global_domain_defaults_settings
  when: mac_global_domain_defaults_settings

一部抜粋でしたのが設定できる値はいろいろあるので、こちらも参照ください

blog.matsuokah.jp

zshのインストールと設定

roles/zshではzshプラグインマネージャantigenpreztoのインストールを行っています。

ここはハマりました。antigenの設定ファイルと読み込むようにして、ログインシェルとしてzshを設定したあとでもantigenコマンドがnot foundに。

したがって少し強引にantigenコマンドを呼んでいます

- name: append source antigen
  shell: >
    echo "{{source_antigen}}" >> ~/.zshrc
  when: grep_command.rc != 0

- name: install prezto through antigen
  shell: >
    source ~/.zshrc && antigen bundle sorin-ionescu/prezto
    executable={{which_zsh.stdout}}
  ignore_errors: True
zshをログインシェルに設定

以下の設定ではログインシェルにzshを設定しており、 このときにパスワードの入力が必要です。パスワードの入力に失敗しても3回までリトライするようにしています。

- name: change login shell
  shell: >
    chpass -s {{which_zsh.stdout}}
  register: result
  until: result.rc == 0
  retries: 3

パスワードはタスクの序盤で入力が要求されるのでの直前に

osascript -e 'display notification "Prease input password in terminal" with title "osx-provisioning"'

を実行してあげると

f:id:matsuokah:20160102003827p:plain

こんな感じで、notiをしてくれます。

sayコマンドで喋ってもらうのもいいですね(笑)

xcodeプラグインパッケージマネージャ(Alcatraz)のインストール

これはダウンロードしてシェルを実行するだけでした。

sshの設定

鍵をリポジトリに含むわけにも行かないので、ここはほぼ手作業。.sshで~/sshシンボリックリンクを貼るようにしました。

sshディレクトリにプライベートリポジトリを配置すれば良いかなと。submoduleにしなかったのはpublicリポジトリからprivateリポジトリに依存するのは微妙だなと思ったので。

設定ファイルのImport

ほとんどのアプリでは設定のexportとimport機能がついています。例えば、karabenerは設定ファイルをshellとしてexportしてくれるので、exportしておいてkarabenerをインストール後にshell実行すれば設定を復元することができます。

こんな感じ

$ /Applications/Karabiner.app/Contents/Library/bin/karabiner export
#!/bin/sh

cli=/Applications/Karabiner.app/Contents/Library/bin/karabiner

$cli set remap.doublepresscommandQ 1
/bin/echo -n .

今回、自分はこの設定たちをexportする前にSSDのクリーンをしてしまったので後悔。これから作っていく予定。

まとめ

これでPC買い替えも、会社のPCの環境構築も、クリーンインストールも怖くない!!そして、まだまだdefaultsの設定が足りてないのでやらねば!

参考

ブログなど

公式docks

please fork me!

github.com

追記

blog.matsuokah.jp

brew caskでインストールしていたアプリを幾つか、AppStore経由でインストールできるように改修した。