Boxenを実行すると何が起こるのか

http://boxen.github.com/
https://github.com/boxen/boxen
https://github.com/boxen/our-boxen

スゴイスゴイと言われていて誰も使っていないBoxenだけど、昨日の朝コードを読んでみた。折角なので、boxenコマンドを実行すると何が起こるのかというのを、Rubyのメソッド単位のコールグラフという形で簡単に説明する。

処理の流れ

1. レポジトリをgit-pullで更新
2. 環境変数や前回実行時に保存しておいたJSONから設定を読み込み
3. 前処理
4. コマンドライン引数に応じて
4-A. puppet-applyを実行 → 後処理
4-B. その他便利コマンドを実行 → 終了

Puppetの扱い

librarian-puppetという仕組みが利用されていて、Puppetfile(=Puppet版Gemfile)にPuppetで入れたいものが定義されている。
Puppetfileで https://github.com/boxen/puppet-ruby 等を参照していて、puppet-applyするとこれらが入る。

コールグラフ

表記方法だけども、例えばCLI.runはBoxen::CLI.runを実行しているという意味で、CLI#runはCLIクラスのインスタンスに対しrunメソッドを呼んでいるという意味になる。インデントは、メソッドの内部で更にメソッドが呼ばれているということを表現している。

./our-boxen/script/boxen            | レポジトリ(=boxen/our-boxenに該当)を更新
  CLI.run                           | コマンドラインツールから呼ばれるエントリポイント
    Config.load                     | 保存しておいたJSONデータからconfigオブジェクトを作る
      Config#user                   | 自身のユーザ名を認識
      Keychain.new                  | KeyChain.appにアクセスする
      Keychain#password             | GitHubのパスワードをKeyChainから取得
      Config#password=              | configオブジェクトに保持
      Keychain#token                | GitHubのtokenをKeyChainから取得
      Config#token=                 | configオブジェクトに保持
    Flags.new                       | Flagsオブジェクトでコマンドラインオプションを管理
      Flags#parse                   | コマンドライン引数をパース
    Flags#apply                     | configオブジェクトにコマンドラインから得た情報をセット
    Preflight.run                   | Puppet実行前に実行したいタスクを実行
      Preflight.checks              | * Boxen::Preflight以下の名前空間に定義された奴が実行される
        Preflight::Creds#ok?        | * #ok?がfalseなら#runが呼ばれるという仕組み
        Preflight::Directories#ok?  | * 例えばHomebrewが入っているかとかを調べたり
        Preflight::Env#ok?          | * /etc/my.cnfが存在していたら警告を出したりする
        Preflight::EtcMyCnf#ok?     |
        Preflight::Homebrew#ok?     |
        Preflight::Identity#ok?     |
        Preflight::Os#ok?           |
        Preflight::Rvm#ok?          |
    Config.save                     | ここで現在の設定をJSONに保存しておく(次回実行時に使う)
    CLI.new                         | 実行用オブジェクトの用意
      Runner.new                    | * Runnerはフック等含めた実行全体に責任を持っている
        Puppeteer.new               | * Puppeteerは操作に責任を持つ
        Checkout.new                | * Checkoutはmanifest集の入っているGitレポジトリに責任を持つ
        Hook.all                    | * puppet-apply後に実行したいフックの定義を探す
    CLI#run                         | 実行開始
      Runner#run                    |
        Runner#process              |
          Runner#process_flags      | コマンドラインオプションを見て期待する処理を行う
            Flags#projects?         | --projects         : 全てのプロジェクトを一覧する
            Flags#disable_services? | --disable-services : Service(=LaunchCtl)を止める
            Service.list            | Boxenで定義されているServiceを探す
              Service.new           |
            Service#disable         | Serviceを止める
            Flags#enable_services?  | --enable-services : Serviceを動かす
            Service.list            |
              Service.new           |
          Runner#process_args       | コマンドライン引数を処理する
          Puppeteer.run             | puppet-apply 実行
            #Puppeteer#command      | 実行用コマンドを用意して実行
              #Puppeteer#flags      | * 実行用オプションを良い感じに用意
            Puppeteer::Status.new   | 終了ステータスのwrapper(puppet-applyは上手く行けば2を返すよ)
        Runner#report               | 終了後のフック処理を行う
          Runner#hooks              | 定義されたフックを探す
            Hook::GitHubIssue.new   | 失敗ならGitHubIssueをopenし、成功すればcloseする
            Hook::GitHubIssue#run   |
              Hook#perform?         | perform?がtrueだった場合、callを実行するという仕組み
              Hook#call             |
            Hook::Web.new           | WebHookを実行する(GitHubのPostReceiveHookみたいな感じ)
            Hook::Web.run           | 任意のURLに実行結果付きでPOSTリクエストしてくれる
              Hook#perform?         |
              Hook#call             |
    Puppeteer::Status#success?      | puppet-applyが成功していれば、
    Postflight.run                  | 実行後用に定義されたタスクを実行する
      Postflight.checks             |
        Postflight::Active#ok?      |
        Postflight::Env#ok?         | (おしまい)