Gclient, VagrantおよびChefによる開発環境管理


[Vagrant](https://www.vagrantup.com/)で簡単に隔離された開発環境を構築できるようになって久しいが、その後も不満な点がいくつかあった。
最近になって一応満足できる解決策を見つけたので記述する。

細かな点やVagrantの使い方次第ですぐに解決できるものはさておき、主な不満点は下記のようなものだった。

* 設定やツールのカスタマイズ管理
  * 私の場合は開発環境には最新のRubyが入っていてほしいし、「いつもの」Vim設定やZsh設定が為されていてほしい。
* 関連リポジトリのチェックアウト
  * 関連する複数のソースリポジトリをチェックアウトしてきてそれらをまとめて1つのVagrant環境内で開発したいことがよくある。
    しかし、もはや`git clone`とかタイプすることすら面倒だし、環境を作り直すたびにどのリポジトリをチェックアウトすれば良いのか覚えておくのは嫌になる。
* 以上すべてのバージョン管理
  * 当然のことながら、`Vagrantfile`を含めて以上すべての設定はバージョン管理されていてほしい。私は設定を忘れる自信があるし、間違えて設定ファイルを上書きする自信もあるからだ。

# 方針

以上の問題を解決するために次のような方針を取った。

* 各種設定ファイル(主にdotfiles)をまとめたプライベートgitリポジトリを作る
* 設定ファイルをチェックアウトしてきて適切な箇所に配置し、更にツールをインストールする手順を[Chef](https://www.chef.io)で記述する
  * このcookbookは独立したgitリポジトリで管理する
  * このcookbookが依存するcookbookはBerkshelfで管理する。
* 関連リポジトリおよび上記cookbookをチェックアウトするための設定を[gclient](http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html)で書く
  * cookbookはberkshelfにチェックアウトさせても良かったが、気分でgclientに管理させることにした。
* 関連リポジトリはホストマシンにチェックアウトしておいてVagrantfileの設定でゲストマシンにマウントする

# 利用例

以上の方針の例が https://github.com/yugui/bazel-workspace である。[Bazel](https://github.com/bazelbuild/bazel)を追いかけつつ[Golang用ビルドルール](https://github.com/bazelbuild/rules_go)を開発するのに使っている。

このワークスペースで作業を開始するには次のステップを踏む。

ワークスペース自体をチェックアウトする
  
```console
$ git clone git@github.com:yugui/bazel-workspace.git
$ cd bazel-workspace
$ ls
Berksfile       Berksfile.lock  Vagrantfile     data_bags/      roles/
```

gclientで関連リポジトリをチェックアウトする

```console
$ gclient sync
Syncing projects: 100% (4/4), done.     
$ ls
Berksfile       Berksfile.lock  Vagrantfile     bazel/          cookbooks/      data_bags/      roles/          rules_go/       skydoc/
```

なおチェックアウトの設定ファイルである`.gclient`は次のようになっている。

```python:.gclient
solutions = [
    {
      "name": "bazel",
      "url": "https://bazel.googlesource.com/bazel@master",
    },
    {
      "name": "rules_go",
      "url": "https://github.com/bazelbuild/rules_go.git@master",
    },
    {
      "name": "skydoc",
      "url": "https://github.com/bazelbuild/skydoc.git@master",
    },
    {
      "name": "cookbooks",
      "url": "git@github.com:yugui/local-cookbooks.git@master",
    },
]
```

Vagrantで環境を作る

```console
$ vagrant up
...
```

1. あらかじめ[vagrant-berkshelf](https://github.com/berkshelf/vagrant-berkshelf)プラグインを入れてあるのでまずBerkshelfが走る。
   Berkshelfが設定用cookbookの依存関係を解決してくれる。
2. `Vagrantfile`の設定に従って関連リポジトリはゲストマシン内にマウントされる
3. 更にcookbookに従って、dotfilesがチェックアウトされ、設定ファイルが配置され、開発ツールがインストールされる。

```ruby:Vagrantfile
Vagrant.configure('2') do |config|
  config.vm.box      = 'ubuntu/trusty64'
  config.ssh.forward_agent = true
  config.vm.synced_folder "rules_go", "/src/rules_go"
  config.vm.synced_folder "bazel", "/src/bazel"
  config.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2222,
    auto_correct: true

  %w[ master ].each_with_index do |name, i|
    config.vm.define(name) do |c|
      c.vm.hostname = name
      c.vm.provision :chef_solo do |chef|
        chef.version = '12.10.40'
        chef.data_bags_path = "data_bags"
        chef.roles_path = %w[ roles ]

        chef.json = {}
        %w[ bazel ].each do |name|
          chef.add_role name
        end
      end
    end
  end
end
```

# まとめ
以上により、次の利点が得られた。

1. dotfilesリポジトリやcookbookは再利用できるので、`Vagrantfile`内のrunlistを弄るだけでいつでも事前定義済みの環境設定を組み合わせて好みの開発環境を作れる
   * `Vagrantfile`, `Berksfiles`と`.gclient`だけコピーして修正すれば異なる開発環境を簡単に作れる
2. 開発対象のリポジトリをチェックアウトする手順も構成管理されている
3. 開発対象のリポジトリはまずホストマシンにチェックアウトされているし、各種設定はChefで自動化されている。このためゲストVMが壊れても失われるものはない。ゲストVMは気軽に作りなおせるし、再現できる
4. 以上を実現するための`Vagrantfile`, `Berksfile`および`.gclient`自体もバージョン管理されている

# 補遺 - gclient

`gclient`はChromiumプロジェクトが利用しているメタチェックアウトツールで、`depot_tools`というツール群の１つである。これを利用するといくつものバージョン管理リポジトリからまとめてソースコードをチェックアウトしてくることができる。
機能としてはAndroidプロジェクトの`repo`にも近いが、`repo`とは異なりgit以外のシステムもサポートしている。

また、gclientにはチェックアウト時に任意のスクリプトを実行するhook機能がある。
これを利用するとチェックアウトされたソースをポストプロセスしたり、未サポートのバージョン管理システムに対応したりもできる。
たとえば、私はgolangのプロジェクトではリポジトリに対して`go generate`を掛けたり、[`gom`](https://github.com/mattn/gom)でgolangプロジェクトをチェックアウトしたりしている。