Mac 環境構築の自動化 2015年末版 ( homebrew + homebrew-cask + homebrew-brewdle + mackup + crontab + mas-cli )

  • 210
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

この記事は クラウドワークスAdvent Calendar 2015 14日目の記事になります。(風邪をひいて一日遅れましたごめんなさい><)

今までの Mac 環境構築の歴史

最初は、インストールしたアプリケーションの手書きメモとホームディレクトリの dotfiles を dropbox 配下に移動しシンボリックリンクへ張替えを行う自前シェルスクリプトがあっただけでした。

アプリをインストールする度に、手元のメモを更新するという温かみのある日々(もちろん頻繁に更新を忘れる)、そんな日常に颯爽と現れた Github Boxen 1 !

Puppetを使ったプロビジョニングをローカルに適用するとか胸熱すぎる!さよなら僕の *.txt *.sh たち!

こうして冪等性と共に楽しい日々を送っていたのですが、半年ほどで異変が起き始めます。

まずboxen適用の実行時間がひたすら遅くなりはじめ、rubyの新バージョンをインストールしたいのに boxen 本家のアップデートを待つ羽目になり(仕方ないので手で入れようものならboxenに削除されてしまいます。冪等性の名のもとに)、いざアップデートしたら非互換の変更により僕が育てた boxen はエラーが発生し、ついに適用すらできなくなりました。2 マジFuxdajfkjfa

こうしてもはやスクラップ状態と化した Mavericks とはファイルシステムごとおさらばし、乗り換えた先が Ansible + homebrew_module 3 !

YAMLによる簡潔な記述で動作も軽快な Ansible とはパートナーとして仲良くやっていける!そう確信して意気揚々と playbook を育てていきました。

そんな日々にやってきた OS X Yosemite アップグレード!
いつものように ansible-playbook コマンドを叩けばエラーの嵐!エラーを潰すために必要になる様々なコマンドを playbook に叩き込んでいくうちに、簡潔だった yaml がインデントとコマンドだらけになっていきました。。
そしてある日、 Ansible 自体のアップデートをしたら複雑化した僕の playbook は呆気無く実行できなくなってしまいました。 マジFufjdsk%^@(*&^@&!##!!!jljfldsakjkljxda!!!

思う所

環境構築を自動化したくてツールを使ってきたのですが、振り返ってみればツールの変化についていくことが精一杯で(むしろ振り切られて...)、自動化したいタイミングでいつもメンテが必要になっており、僕は自動化の恩恵を全く預かっていないことに気づきました....!!

mac の環境構築を行う頻度(新しいmac買った時、macが壊れた時、OSアップグレード時..etc) は短くても半年に一回程度ですが、現代のプロビジョニングツールの変遷はそれよりも早いようです。。

現在の構成

色々と考えた末、今(2015年末)の僕の mac の環境構築は下記のツールを組み合わせて、それらをインタラクティブに呼び出すシェルスクリプトで構成しています。

homebrew + homebrew-cask + homebrew-brewdle
パッケージとアプリの管理
mackup
dotfiles, ディレクトリ, アプリ設定の管理
crontab
バックアップ

4 5

ポイントは crontab 5 を用いてバックアップを自動化することで、プロビジョニングツール使用時に発生する 構成ファイルを編集(playbook, Brewfileなど) -> 構成を適用 という手順を省略しています。
つまり、普段はいつも通り brew install hogebrew uninstall fuga 等で管理するだけでよく、初回の環境構築の時だけ専用のシェルスクリプトを呼べば良いようになっています。

homebrew + homebrew-cask + homebrew-brewdle 4

homebrew + homebrew-cask でパッケージとアプリケーションをほぼカバーでき、インストールするパッケージ/アプリのリストは homebrew-brewdle の Brewfile という形式で管理できます。
参考用の僕の Brewfile は長いので、記事の一番下に記述しておきます。

※ MacAppStore からインストールするアプリはカバーできていないのですが、だれか良い方法があったら教えて下さい。

2016/04/23 追記

homebrew-brewdle に MacAppStore をCLIで扱える mas-cli のサポートが追加されました。
Add Mac App Store support using mas-cli. by mikemcquaid · Pull Request #172 · Homebrew/homebrew-bundle

現時点では master ブランチにしか入っていないので、明示的にリポジトリを設定すると使えるようになります。

$ brew install argon/mas/mas # mas-cli のインストール
$ brew tap homebrew/brewdler git@github.com:Homebrew/homebrew-bundle.git
$ brew update

バックアップ

homebrew-brewdle には dump コマンドで Brewfile にインストールしているパッケージ/アプリの一覧をファイルに書き出す機能があるので、これを crontab 5 に設定し現在の構成を自動的にバックアップするようにしています。

crontab
@daily  cd /your/brewfile/directory && brew brewdle dump --force && git add Brewfile && git commit -m "auto commit of Brewfile dump `date "+\%Y-\%m-\%d \%H:\%M:\%S"`" && git push

僕は git 管理下にしていますが、単に Dropbox 等の配下でもいいと思います。

crontab
@daily  cd ~/Dropbox && brew brewdle dump --force

アップデート

パッケージ(homebrew) とアプリケーション(homebrew-cask) は crontab で週次アップデートするようにしています。

crontab
@weekly time nice -n19 brew update && time nice -n19 brew upgrade && time nice -n19 brew cleanup && for c in `brew cask list`; do ! brew cask info $c | grep -qF "Not installed" || time nice -n19 brew cask install $c; done && time nice -n19 brew cask cleanup && for c in /opt/homebrew-cask/Caskroom/*; do vl=(`ls -t $c`) && for v in "${vl[@]:1}"; do \rm -rf "$c/$v"; done; done

参考リンク

mackup

mackup は dotfiles や アプリケーションの設定を dropbox 等に同期してくれるアプリです。
といってもやっていることは単純で、実ファイルをコピーしてシンボリックリンクに置き換えているだけで、特に dotfiles については同様のことを自前で書いている人も多いのではないでしょうか?

使い方は mackup backup コマンドを実行すれば終わりです。
この mackup は dotfiles に加えてかなりの数のアプリケーションの設定ファイルをサポートしているのでオススメです。

デフォルトに加えてバックアップしたいファイル/ディレクトリがある場合の設定ファイルの書き方も簡単ですし、Dropbox以外にも多数のストレージをサポートしています。参考までに僕のmackup設定ファイルはこちらです。

~/.mackup/ctokoro.cfg
[application]
name = My personal synced files and dirs

[configuration_files]
.gitignore_global
.config
.bundle
.chef
.z
.zsh_history
Gemfile
Gemfile.lock
Documents
Movies
Music
Pictures

バックアップ

この mackup のコマンドを crontab に設定しておくことで新しいアプリの設定も自動的にバックアップされるようにしています。

crontab
@daily  yes | mackup backup

環境構築用シェルスクリプト

あらかじめ Dropbox だけは同期しておき、このスクリプトを呼んで y を連打するだけで環境構築を終えられるようになっています。
インタラクティブにしている理由は、OSアップグレード時などですんなり入らず途中で終了することもよくあるので、途中から再開しやすくするためです。

setup_osx.sh
#!/bin/bash
BREWFILE_DIR=/your/brewfile/directory

ask() {
  printf "$* [y/n] "
  local answer
  read answer

  case $answer in
    "yes" ) return 0 ;;
    "y" )   return 0 ;;
    * )     return 1 ;;
  esac
}

set -e

if ask 'xcode install?'; then
  xcode-select --install
fi

if ask 'Homebrew install?'; then
  ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

  brew doctor
  brew install caskroom/cask/brew-cask
fi

if ask 'execute brew brewdler(Brewfile)?'; then
  brew tap Homebrew/brewdler
  pushd $BREWFILE_DIR
  brew brewdler
  popd
fi

if ask 'restore setting from mackup? (need Dropbox directory)'; then
  mackup restore
fi

if ask "Do you want to install ruby by rbenv-rubybuild?"; then
  INSTALL_RUBY_VERSION="$( rbenv install -l | peco)"
  brew link readline --force
  MAKE_OPTS="-j 4" RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)" rbenv install $INSTALL_RUBY_VERSION
fi

OSXの設定

OSXの設定でコマンドで設定できるものはシェルスクリプトでやるようにしています。ですが、カバー率が低くGUIでポチポチと設定が必要なものも多いので、なにかいい方法はないですかね。。

setup_osx.sh
## mac setting
if ask "set 'locate' command?"; then
  sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist
fi

if ask 'set visible dotfiles in finder?'; then
  defaults write com.apple.finder AppleShowAllFiles TRUE
  killall Finder
fi

if ask 'set fullpath title at finder?'; then
  defaults write com.apple.finder _FXShowPosixPathInTitle -bool yes
  killall Finder
fi

if ask 'set always expand save dialog?'; then
  defaults write -g NSNavPanelExpandedStateForSaveMode -bool yes
fi

if ask 'set mute in mac booting sound?'; then
  sudo nvram SystemAudioVolume=%80
fi

# @see https://discussionsjapan.apple.com/thread/10153604
if ask 'set clamshell mode off? (for multi display sleep)'; then
  sudo nvram boot-args="niog=1"
fi

最後に

他にもここでは紹介していないですが、作業ディレクトリ圧縮+バックアップスクリプトなどを駆使して、いつ何時いまこの瞬間にmacが突然死しても、半日くらいで元の状態のマシンを構築できる体制を整えています。
ツールの移り変わりもあるので、来年の今頃はどんなツールを使っているかわかりませんが、新しいmacを買った時の時間の節約や最悪の事態の回避のために、常にブラッシュアップを続けていきたいと思います。
より良い方法やツッコミ、お待ちしております!

P.S. Brewfile

Brewfile
tap 'caskroom/cask'
tap 'homebrew/brewdler'
tap 'homebrew/dupes'
tap 'homebrew/services'
tap 'homebrew/versions'
cask 'java'
cask 'xquartz'
brew 'autoconf'
brew 'autossh'
brew 'awscli'
brew 'icu4c'
brew 'boost'
brew 'bsdmake'
brew 'cmake'
brew 'colordiff'
brew 'csshx'
brew 'go'
brew 'direnv'
brew 'xz'
brew 'gettext'
brew 'dwdiff'
brew 'elasticsearch'
brew 'exiftool'
brew 'pkg-config'
brew 'x264'
brew 'lame'
brew 'libvo-aacenc'
brew 'xvid'
brew 'libpng'
brew 'freetype'
brew 'openssl'
brew 'ffmpeg'
brew 'fortune'
brew 'gdbm'
brew 'ghi'
brew 'jpeg'
brew 'libtiff'
brew 'little-cms2'
brew 'ghostscript'
brew 'ghq'
brew 'gnu-getopt'
brew 'git-now'
brew 'heroku-toolbelt'
brew 'hub'
brew 'libtool'
brew 'imagemagick'
brew 'jbig2dec'
brew 'oniguruma'
brew 'jq'
brew 'libevent'
brew 'libffi'
brew 'libyaml'
brew 'mackup'
brew 'mecab'
brew 'mecab-ipadic'
brew 'memcached'
brew 'pcre'
brew 'nginx'
brew 'packer'
brew 'peco'
brew 'readline'
brew 'postgresql'
brew 'proctools'
brew 'q'
brew 'ruby-build'
brew 'rbenv'
brew 'rbenv-binstubs'
brew 'reattach-to-user-namespace'
brew 'redis'
brew 'rmtrash'
brew 'source-highlight'
brew 'spark'
brew 'terminal-notifier'
brew 'terraform'
brew 'the_silver_searcher'
brew 'tig'
brew 'tree'
brew 'watch'
brew 'wget'
brew 'youtube-dl'
brew 'z'
brew 'zsh'
brew 'caskroom/cask/brew-cask'
brew 'homebrew/dupes/apple-gcc42'
brew 'homebrew/versions/mysql56'
brew 'homebrew/versions/node012'
cask '1password'
cask 'adobe-creative-cloud'
cask 'alfred'
cask 'atom'
cask 'bartender'
cask 'burn'
cask 'caffeine'
cask 'cheatsheet'
cask 'chefdk'
cask 'clipy'
cask 'cyberduck'
cask 'disk-inventory-x'
cask 'dockertoolbox'
cask 'dropbox'
cask 'evernote'
cask 'flickr-uploadr'
cask 'flux'
cask 'github-desktop'
cask 'google-chrome'
cask 'google-drive'
cask 'google-japanese-ime'
cask 'google-photos-backup'
cask 'handbrake'
cask 'hosts'
cask 'imageoptim'
cask 'istat-menus'
cask 'iterm2'
cask 'karabiner'
cask 'licecap'
cask 'maczip4win'
cask 'mou'
cask 'rubymine'
cask 'sequel-pro'
cask 'shiftit'
cask 'skitch'
cask 'skype'
cask 'slack'
cask 'sublime-text'
cask 'vagrant'
cask 'virtualbox'
cask 'vlc'
cask 'zoomus'

  1. Mac - Boxen使わなくても許されるのは2012年までだよね - Qiita 

  2. Mac - Boxen使ってて許されるのは2013年だけだった - Qiita 

  3. AnsibleでHomebrew, Cask, Atomエディターのパッケージを管理する - Qiita 

  4. この記事を書いている最中に調べたら、homebrew-brewdle は homebrew-bundle に変更された(正確には戻った)ようです。Rename to brew bundle. · Homebrew/homebrew-bundle@2c59ed1 ですが、僕は brew-bundle で試していない+今もhomebrew-brewdleで動くので、記事中では homebrew-brewdle で統一します。 

  5. mac では cron は非推奨で launchd を使うべきなようですが、launchd の記法を覚えるのが面倒でぼくは crontab 使ってます。><