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

続編を書きました - [Boxen使ってて許されるのは2013年だけだった](http://qiita.com/yuku_t/items/50b2b376604c2fefd8cd)

すごいすごいと話題な割に誰も使っていないと話題の[Boxen](http://boxen.github.com/)を使ってみた。

## 3行で分かる結論

Boxenは...

- Macのセットアップを自動化してくれる
- 個人用途でも十分便利だが真価を発揮するのは大人数で使うとき
- Puppet知らなくても案外使える

### この記事で分かるもの

- Boxenの個人用途での使い方
- Boxenのチーム用途での使い方
- Puppetのmanifestの簡単な書き方

### この記事を読んでも分からないもの

- Puppetの詳しい使い方

## Boxenを使うと何ができるのか

BoxenはGitHub社が開発しているシステムで、Macのセットアップを簡単にできるようにしてくれる。新しくMacを購入したら開発環境や各種アプリケーションをインストールすることから始めるが、これをコマンド一発で全てやってくれるようになる。

## Boxenにまつわる各種の誤解

### Puppetを知らないと使えない

PuppetとはBoxenが内部で使っているセットアップツール。似たものにChefなどがある。
BoxenではGitHub社が公開している各種resourceを組み合わせるだけでだいたいのことができる。個人用途ならこれで十分。
細かい制御をするにはPuppetについて学習する必要があるが、GitHub社が公開しているresourceのmanifestを見れば雰囲気でなんとかなる。実際私は今回Puppetを初めて使ったがなんとかなった。
（「nginxをインストールする」という設定があった場合、nginxはresourceで、resourceをインストールする手順が書いてあるのがmanifest。ここらへんはPuppet用語だから間違っているかも）

### 個人で使うには向かない

Boxenはチームで使った方が便利だが、個人用途としても十分に使える。チーム用のBoxenリポジトリに各個人の設定も書ける。
ただ個人だとそう何度もマシンを一からセットアップする機会がある訳ではないので、やはり大人数で設定を共有した方が真価を発揮する。ハマりどころはだいたいいつも同じだからそこら辺を回避する設定を入れておくと捗ること間違いなし。

### GitHub社に特化しているので使えない

確かにGitHub社が使っている環境を構築するのに便利なmanifestが充実しているが、自分の環境に流用できるものも多い。またmanifestは自作することも簡単に可能。

## 使い方

1. 事前準備
2. boxen/our-boxenのコピーを作成
3. 設定を書く
  1. Puppetfile
  2. 個人設定
  3. プロジェクト設定
  4. チーム設定
4. Boxenを実行

### 事前準備

- Xcodeをインストール（CLI、GUIどちらでもOK）
- GitHubアカウントを持っていないなら作る

最近のMacにはgitがプリインストールされている。何かしらの理由でgitが無い場合はそれもインストールする。
あと以下を実行する。

```sh:Terminal
sudo mkdir -p /opt/boxen
sudo chown $USER:admin /opt/boxen
```

### boxen/our-boxenのコピーを作成

[boxen/our-boxen](https://github.com/boxen/our-boxen)はBoxenを使うためのひな形プロジェクト。まずはこれのコピーを作って、そこに設定を書いていく。

```sh:Terminal
mkdir -p ~/src/my-boxen #名前は好きにする
cd ~/src/my-boxen
git init
git remote add upstream https://github.com/boxen/our-boxen
git pull upstream master
```

これで~/src/my-boxenディレクトリにour-boxenのコピーが作られた。

### 設定を書く

Boxenの設定する手順は大雑把にいって2段階踏む

1. GitHub（やその他公開リポジトリ）上で公開されているresourceを指定
2. 設定ファイルで1で指定したものをincludeする

#### Puppetfile

外部のresourceを使う場合はここで指定する。現在主に仕えるのは[boxen](https://github.com/boxen)にある _puppet-*_ という名前のリポジトリに入っているもの。設定自体は~/src/my-boxenにあるPuppetfileに書く。
Puppetfileには初期状態で何個か設定がある。こいつらはBoxen自体が使ったりするので消さないこと。
_puppet-*_ の中で今回は以下のものを追記した。

```ruby:Puppetfile
# Puppetfileの末尾に追記

# これはGitHub上にあるboxen/puppet-dropboxリポジトリの1.0.0
# を使うという意味。バージョン名はGitHub上で確認する必要がある。
github "dropbox",     "1.0.0"
github "mysql",       "1.0.0"
github "iterm2",      "1.0.0"
github "chrome",      "1.0.0"
github "skype",       "1.0.0"
github "redis",       "1.0.0"
github "icu4c",       "1.0.0"
github "imagemagick", "1.0.0"
github "xquartz",     "1.0.0"
github "libtool",     "1.0.0"
github "osx",     "1.0.0"
```

例えばdropbox、skype、iterm2、chromeというのがあることからも分かるように、Boxenでアプリケーションのインストールもできる。

ここで指定したresourceをインストールするには~/src/my-boxen/manifests/site.pp内で `include` する。しかしsite.ppは全てのマシンに適応される設定を書く場所なので、基本的にはここには書かず、後述する個人設定やプロジェクト設定に書く。

もしhomebrew上に存在するがそれをインストールする _puppet-*_ resourceが提供されていない、という場合、自前でresourceを用意してもいいが、 **homebrewでインストールできる場合に限って** 簡単に書く方法をBoxenが用意しているのでそれを使ってもいい。この方法については「プロジェクト設定」で触れる。

デフォルトではNodejsとRubyがそれぞれnvmとrbenvを使って古いバージョンもインストールされるようになっているが、最新のものだけで十分なのでコメントアウトした。
`include` コマンドに渡されている `nodejs` や `ruby` は確かにPuppetfileの中に現れている。

```puppet:manifests/site.pp
# site.ppの下の方にある
node default {
  # すべてのマシンで実行される設定

  # node version
  #include nodejs::0-4
  #include nodejs::0-6
  include nodejs::0-8  

  # default ruby versions
  #include ruby::1-8-7
  #include ruby::1-9-2
  include ruby::1-9-3
}
```

#### 個人設定

事前準備に「GitHubアカウントを持っていないなら作る」があったことを思い出してほしい。Boxenではmodules/people/manifestsディレクトリ内に \<GitHubアカウント名\>.pp というmanifestがあったらそれを自動的に実行するようになっている。個人用途で使う場合は、ここにもりもり書く感じになる。

例えば私（[taka84u9](https://github.com/taka84u9/)）の場合はmodules/people/manifests/taka84u9.ppに以下のように書いた。

```puppet:modules/people/manifests/taka84u9.pp
class people::taka84u9 {
  # 自分の環境で欲しいresourceをincludeする
  include dropbox
  include skype
  include iterm2::stable #::devもある
  include chrome

  # homebrewでインストール
  package {
    [
      'tmux',
      'reattach-to-user-namespace',
      'tig',
    ]:
  }

  $home     = "/Users/${::luser}"
  $src      = "${home}/src"
  $dotfiles = "${src}/dotfiles"

  # ~/src/dotfilesにGitHub上のtaka84u9/dotfilesリポジトリを
  # git-cloneする。そのとき~/srcディレクトリがなければいけない。
  repository { $dotfiles:
    source  => "taka84u9/dotfiles",
    require => File[$src]
  }
  # git-cloneしたらインストールする
  exec { "sh ${dotfiles}/install.sh":
    cwd => $dotfiles,
    creates => "${home}/.zshrc",
    require => Repository[$dotfiles],
  }
}
```

だいたい雰囲気で何をしているかは分かると思う。 `File` というのはfileタイプのresourceを指定するのに使う。詳しい説明はしないが、resourceの定義には全て小文字、指定するには先頭大文字になる。 `$src` のfile定義が無い、と思うかも知れないが~/srcはBoxenが使うために事前に定義しているので今回は場合は必要ない（特殊事例）。普通は以下のように書かねばならない（~/codeディレクトリの場合）。

```puppet:modules/people/manifests/taka84u9.pp
class people::taka84u9 {
  $code     = "/Users/${::luser}/code"
  file { $code:
    ensure => derectory #これはディレクトリです
  }
  repository { "${code}/dotfiles": #こういう書き方もできる
    source  => "taka84u9/dotfiles",
    require => File[$code]
  }
}
```

詳しくはPuppetのドキュメントを読むべし。

##### package を使いこなす

Macアプリをインストールしたい場合は次のように書く。 `source` でダウンロードURLを指定して、 `provider` は .zip なら`compressed_app`  .dmg なら `pkgdmg` にする。これを書いておくだけで[Kobito](http://kobito.qiita.com/)と[XtraFinder](http://www.trankynam.com/xtrafinder/)がインストールされる。

```puppet
package {
  'Kobito':
    source   => "http://kobito.qiita.com/download/Kobito_v1.2.0.zip",
    provider => compressed_app;
  'XtraFinder':
    source   => "http://www.trankynam.com/xtrafinder/downloads/XtraFinder.dmg",
    provider => pkgdmg;
}
```

Homebrew経由でインストールするには同じく `package` コマンドを使うが、インストール時のオプションを指定するには `install_options` を指定する。ここではzshを `--disable-etcdir` オプションでインストールしている。（`--disable-etcdir` については `brew info zsh` 参照）
ついでに説明すると file_line はファイルに行を追加する。新しくインストールしたzshをログインシェルとして指定できるように/etc/shellsに追記する。依存関係としてはzshのインストールが済んだあとにしている。
osx_chsh は `include osx` をすることで使えるようになる便利コマンドで自分のログインシェルを変更する。file_lineがbeforeで指定しているので、osx_chshが実行される時点では既にログインシェルとして新しいzshは登録可能な状態にある。
[boxen/puppet-osx](https://github.com/boxen/puppet-osx) にはこれ以外にもosx_login_itemなど便利なのが定義されているので使いこなせると一気に幸せになれそう。

```puppet
include osx
package {
  'zsh':
    install_options => [
      '--disable-etcdir'
    ]
}
file_line { 'add zsh to /etc/shells':
  path    => '/etc/shells',
  line    => "${boxen::config::homebrewdir}/bin/zsh",
  require => Package['zsh'],
  before  => Osx_chsh[$::luser];
}
osx_chsh { $::luser:
  shell   => "${boxen::config::homebrewdir}/bin/zsh";
}
```

#### プロジェクト設定

個人用途だったらここから先の内容は不要。

Boxenの本来の目的は新しく会社に来た人や、マシンを新しくした人がさっさと開発に取り掛かれるようにすることだ。会社なら複数のプロジェクトがあり、それぞれにセットアップ方法が異なるのが普通だろう。Boxenではそれをmodules/projects/manifestsディレクトリに \<プロジェクト名\>.pp で定義する。

仮にawesomeprojectというプロジェクトがあるとする。このプロジェクトの設定ファイルはmodules/projects/manifests/awesomeproject.ppになる。

このプロジェクトではRMagickというgemを使っているのでimagemagickとlibtoolを `include` した。それに加えてRMagickをビルドするにはxzというのも必要なのだが _puppet-*_ を探してみてもそれらしきものが存在しない。だがxzはhomebrewを使ってインストールできる。

実際にインストールしてみて発覚したのだが、imagemagickをインストールして作られるdylibファイルは名前がおかしくてエラーになる。 [この記事](http://qiita.com/items/b6c7b56e060849c7509f) を参考にシンボリックリンクを貼って回避することにした。またhomebrewによってlittle-cmsというパッケージもインストールされている（imagemagickかlibtoolかxzが依存していてhomebrewによって自動的にインストールされた）のだが、こちらも同様の理由からシンボリックリンクを作成している。

最後にリポジトリの用意をする。Gitリポジトリを用意するには `repository` コマンドを使うが、 `boxen::project` コマンドという便利なものも用意されている。詳しい使い方は [ここを読む](https://github.com/boxen/puppet-boxen/blob/master/manifests/project.pp#L1-L46)

```puppet:modules/projects/manifests/awesomeproject.pp
class projects::awesomeproject {
  # awesomeprojectで必要なものをincludeする

  include imagemagick
  include libtool
  package { 'xz': } # homebrewでインストール

  $lib = "/opt/boxen/homebrew/lib"
  file {
    "${lib}/libMagickCore.dylib":
      ensure => link,  # シンボリックリンク
      target => "./libMagickCore-Q16.7.dylib";
    "${lib}/liblcms2.2.dylib":
      ensure => link,
      target => "./liblcms.dylb"
  }

  boxen::project { 'awesomeproject':
    ruby   => '1.9.3',
    mysql  => true,
    redis  => true,
    # GitHub上のtaka84u9/awesomeprojectをgit-cloneする
    # クローン先はデフォルトで~/src/awesomeproject
    source => 'taka84u9/awesomeproject'
  }
}
```

プロジェクト設定はこのままでは適応されない。これをインストールするには個人設定でプロジェクトを `include` する。

```puppet:modules/people/manifests/taka84u9.pp
class people::taka84u9 {
  # owsomeprojectがインストールされる
  include projects::owsomeproject

  # allプロジェクトは全てのプロジェクトという意味
  # modules/projects/manifests/all.pp参照
  include projects::all
}
```

#### チーム設定

個人のプロジェクトが所属しているチームに応じて決まる、という場合はチーム設定を使うと便利。

modulesディレクトリにはpeopleとprojectsというディレクトリがあるが好きに追加できる。
例えばmiraclecompanyという会社にamazingteamというチームがいるとする。そしてこのチームはawesomeprojectとsuperprojectプロジェクトを担当しているならmodules/miraclecompany/manifests/amazingteam.ppは次のようになる。

```puppet:modules/miraclecompany/manifests/amazingteam.pp
class miraclecompany::amazingteam {
  include projects::awesomeproject
  include projects::superproject
}
```

そしてこのamazingteamに所属するgeniushuckerの個人設定でチームを `include` する。これでGitHubアカウントがgeniushuckerの人がBoxenを実行するとamazingteam用の環境がサクッと作られる。

```puppet:modules/people/geniushucker.pp
class people::geniushucker {
  include miraclecompany::amazingteam
}
```

すべての人に共通する内容を記述するには、例えばmiraclecompany::environmentsを作ってmanifests/site.ppで `include` すればいい。（site.ppは全員で実行されることを思い出してほしい）

### Boxenを実行

Boxenはデフォルトでディスクの暗号化を要求する。企業で使っているのであれば基本的に行った方がいいだろうと思う。ただMacはデフォルトで暗号化を行わない設定になっているので、システム設定を開いて暗号化を許可しなければならない。英語インタフェースなら"System Preferences"→"Security & Privacy"→"FileVault"にある。

以上を行ってから下記実行。

```
cd ~/src/my-boxen
script/boxen
# 暗号化を行わない場合は
# script/boxen --no-fde
```

実行すると、Gitリポジトリに変更がある状態だといろいろ言ってくるが気にしなくてもOK。しばらくすると完了する。
それができたら.zshrcや.bashrcに以下を追記する。

```sh:.zshrc
[ -f /opt/boxen/env.sh ] && source /opt/boxen/env.sh
[ -f /opt/boxen/nvm/nvm.sh ] && source /opt/boxen/nvm/nvm.sh
```

あと自分のリポジトリに `git-push` する。プライベートリポジトリ推奨。
リポジトリの場所はGitHubじゃなきゃいけない訳ではない。GitHubのプライベートリポジトリが有料なので[Bitbucket](https://bitbucket.org/)に無料でプライベートリポジトリを作った。会社なら、会社のプライベートリポジトリに `git-push` する。

```sh:Terminal
ssh-keygen
pbcopy < ~/.ssh/id_rsa.pub
# Bitbucketに公開鍵を追加
git remote add origin git@bitbucket.org:taka84u9/my-boxen.git
git push origin master
```

これで完了

### 作ったBoxenリポジトリを使う

他のマシンで実行するには最初の方で書いた *事前準備* をして、リポジトリを `git-clone` して、Boxenを実行して、.zshrcに追記するだけ。
このとき必要なら個人設定も書く。

```sh:Terminal
sudo mkdir -p /opt/boxen
sudo chown $USER:admin /opt/boxen
mkdir ~/src
git clone git@bitbucket.org:taka84u9/my-boxen.git ~/src/my-boxen
cd ~/src/my-boxen
script/boxen
# 暗号化を行わない場合は
# script/boxen --no-fde
```

```sh:.zshrc
[ -f /opt/boxen/env.sh ] && source /opt/boxen/env.sh
[ -f /opt/boxen/nvm/nvm.sh ] && source /opt/boxen/nvm/nvm.sh
```

## 合わせて読みたい

- [大人数でBoxen使うならboxen-webが断然便利 #Boxen #Mac - Qiita](http://qiita.com/items/2f7935f924b6e6c0732d)
- [Boxenをアップデートする方法 - Qiita](http://qiita.com/yuku_t/items/9312fd82a0fd291b683f)
- [Boxenを実行すると何が起こるのか - ✘╹◡╹✘](http://r7kamura.hatenablog.com/entry/2013/02/22/152133)