Born Too Late

Yuya's old tech blog.

互換性の無い gem を無理やり bundle install する

2012-01-02 21:43:28

2011-01-03 19:30 追記
rack-config は現在では rack 本体の gem に含まれており, わざわざインストール必要はありませんでした.
(GitHub で送っていた Pull request も閉じてます)
ですが, この記事で紹介している Bundler についての説明は特に問題ありません.

お正月休みということでちょっとした Sinatra 製の Web アプリケーションをいじって遊んでいます.

ローカルでは Pow で開発して, Heroku へのデプロイを考えているんですが, 設定をどう管理するか迷いました.
いろいろ調べた結果, .powenv とか使う方法とかあったのですが, .powenv は Heroku 上では使えません.
どうしたものかとさらに探した結果, rack-config という gem があったのでそれをラップして何とかすることにしました.
(以降はエントリの内容から脱線するので省略. いい感じの方法があれば Twitter とかで教えていただけると幸いです.)

さっそく Gemfile を書いて必要な gem を用意しましょう.
趣味の開発ですし, rack は最新バージョンを指定して, あわよくば地雷を踏み, パッチなど書いて Pull request を送りたい, というのが人情というものでしょう.

これで bundle install を実行すると, 以下のようにエラーで中断してしまいます.

[caption id="attachment_1632" align="aligncenter" width="414" caption="bundle install に失敗"]bundle install に失敗[/caption]

これは rack と rack-config の互換性による問題です.
rack-config は 2012 年 1 月 2 日現在で丸 3 年程メンテされていない状態で, 依存する rack のバージョンが ~> 0.4 と, かなり古い状態です.
(~> 0.4 というのは 0.4.0 以上 0.5.0 未満という意味です)

今回指定した rack 1.4.0 は, rack-config が依存するバージョンに含まれないため, 依存関係の解決に失敗し, エラーとなってしまいます.

といっても, これはメタ情報におけるバージョンの指定の問題で, 実際に互換性があるかどうかは別の問題です.
rack-config の実装は README よりも短くシンプルなもので, 目 rackup した限りでは間違いなく動きそうに見えます.

作者に依頼して依存関係の指定を修正してもらうという手もありますが, 今回はとりあえずの解決をするための方法を紹介します.

大まかな手順

  1. 元のプロジェクトを GitHub 上で fork してローカルに git clone する
  2. ローカルの tmp-repo ブランチ上で *.gemspec の依存関係の指定を修正する
  3. 自分のリポジトリに git push する
  4. Gemfile で自分のリポジトリを指定する

Bundler は RubyGems.org からのインストールだけでなく, Git リポジトリからのインストールにも対応しています.
それを利用し, オレオレ Rubygems パッケージリポジトリを作って, そこからインストールしよう, というものです.

今回はたまたま元のプロジェクトが GitHub 上にあったので fork するだけで済みましたが, GitHub 上に無い場合も, 手動でリポジトリさえ作れば, あとは同様の手順で実行できます.

1. 元のプロジェクトを GitHub 上で fork してローカルに git clone する

clone については省略しますが, ブランチを切るのがいいでしょう.
一時的なリポジトリということで, tmp-repo という名前にしてみました.

2. ローカルの tmp-repo ブランチ上で *.gemspec の依存関係の指定を修正する

*.gemspec というのは, Rubygems パッケージのメタ情報を定義するファイルです.
依存関係もこのファイルに記述されています.

今回の場合は rack-config.gemspec に以下のような修正を加えました.

rack-config は rack 0.4.0 以上 1.5.0 未満に依存する, ということになったので, rack 1.4.0 も含まれるようになりました.

3. 自分のリポジトリに git push する

特に説明は要らないと思いますが念のため.

4. Gemfile で自分のリポジトリを指定する

先ほどの Gemfile を以下のように書き換えました.

fork した自分のリポジトリの URL と, 今回のために作成した tmp-repo ブランチを指定しています.

あとは最初と同じように bundle install を実行するだけです.

[caption id="attachment_1653" align="aligncenter" width="630" caption="bundle install に成功"]bundle install に成功[/caption]

無理矢理 bundle install することに成功しました!

蛇足: オレはこう思う

今回はオレオレリポジトリを作ってとりあえずの解決を試みる方法を紹介しましたが, せっかくなら作者にも修正を出しておくとよりいいでしょう.
今後その gem を使用するときにいちいちオレオレリポジトリを指定するのは面倒ですし, 他の人にとってもその gem がメンテされた状態で使える方が便利です.

今回の rack-config についても, 実際は以下のような手順を経ています.

  1. 元のプロジェクトを GitHub 上で fork してローカルに git clone する
  2. ローカルの develop ブランチ上で Bundler を使ってテストできるように修正する
  3. GitHub 上で Pull request を送る
  4. develop ブランチから tmp-repo ブランチを作成する
  5. ローカルの tmp-repo ブランチ上で *.gemspec の依存関係の指定を修正する
  6. 自分のリポジトリに git push する
  7. Gemfile で自分のリポジトリを指定する

修正を依頼しつつ, それとは別のブランチをオレオレリポジトリにしています.
(この Pull request が取り入れられるのかはまだわかりませんが...)

ついでに Travis CI で 0.4.0 以上 1.5.0 未満の全ての rack でテストが通ることも確認済みです.

Travis CI 用の設定ファイルは以下のようなスクリプトで, .travis.ymlgemfiles を生成しました.
Travis CI では複数の gemfiles を指定することで, それぞれで bundle install し, 複数のバージョンの gem に対してテストを行うことができます.

なお, rack は 0.4.x のあといきなり 0.9.x にバージョンが飛んでいます.

これで, 見事に全てのバージョンでテストが通ることが確認できました.
(ここまで必死になることも無いとは思いますが...)

Ruby 用オートローダスクリプト自動生成ツール algerb リリース

2011-12-31 23:50:23

最近 PHP ばっかりやってましたが, 年末で時間もあったので久々に Ruby で作ってみました.
2011 年最後の日にして, 初の Rubygems パッケージリリースです.

これは何?

まぁタイトルの通りです.

Ruby には Kernel.#autoload というメソッドがあります.
これを利用すればいちいち手動で require しなくても, 対象となるモジュールやクラス名が参照されたタイミングで自動的に require してくれるというものです.

autoload は便利ですが, ライブラリ内のファイル数が増えてくるといちいち手動で書くのは大変になってくるので, じゃあ自動生成すればいいじゃない, ということで作ってみました.

使い方

プロジェクトの Gemfile に適当に設定します.

あとは bundler 経由でコマンドを実行するのみです.

これで ./lib ディレクトリ内を自動的に走査し, ./autoloader.rb というファイルを生成します.

これぐらいの規模だとそんなにありがたみは無いですが, 例えば Cucumber のような規模のプロジェクトで使用すると, 以下のようなファイルが生成されます.

ただ, これが実際に Cucumber プロジェクト内で正常に使えるかは試しておらず, よくわかりません.
理由は以下に.

仕組み

仕組みはすごく簡単で, ファイルパスからクラス・モジュール名をルールに従って変換し, それをもとに Ruby スクリプト化しているだけです.

例えば, ./lib/foo/bar/baz/foo_bar.rb というファイルがあるとすると, 中には Foo::Bar::Baz::FooBar というクラス (またはモジュール) があるに違い無い, という前提で生成しています.
ファイルの中身は一切参照していません.

よって, algerb が正常に動作する条件として, ファイルとクラス・モジュールの命名がルールに従っている必要があります.

課題

今のところひとつの ./lib ディレクトリしか対象にできないので, ./vendor ディレクトリ内のライブラリなどは相変わらず require する必要があります.

また, 1 ファイルに複数のクラス・モジュールが存在するなどといった場合にも対応できません.
これを実現するとなると, Ruby パーサなどを用いてもっとちゃんとファイル解析する必要があるでしょう.

あと, 情けない話なんですが, gem コマンド経由でインストールしたときに正常に動作しません...
gem install した時点で bundle install されてないので, 依存関係を解決することができずに, 起動すらできません.
これはただ単にやり方をしらないだけなので, 調べて直すつもりです.

ちょこちょこ修正するつもりではありますが, 普段 Ruby 開発をやっているわけではないので, どういう仕様にすれば役に立つのかよくわかってなかったりします.
Twitter などでご意見いただけると幸いです.

Travis CI で PHP 5.4 も CI する, PHPUnit も Behat もやる

2011-12-20 04:01:47

この記事は PHP5.4 Advent Calendar jp: 2011 の 20 日目です.

Travis CI とは

Travis CI は, Continuous Integration (CI: 継続的インテグレーション) を実行するクラウド環境です.
GitHub に push すると, Travis CI の VM 上に通知が行われ, GitHub リポジトリからのチェックアウトや, ユニットテストの実行が行われます.
ユニットテストの実行は成功/失敗の結果により通知が行われ, また, 履歴も Travis CI 上に残ります.

元々は Ruby 専用のサービスだったと思いますが, その後 Clojure や Node.js などをサポートし, 1 ヶ月ぐらい前に PHP までもサポートされました.

Travis CI を利用する PHP プロジェクト

これら以外にも Symfony 関連のライブラリやバンドルは多く登録されています.

ところで, 国産のものだと今のところは Diggin_Http_Charset ぐらいしか把握していません.
日本語の情報はまだほとんど無いに等しい状況なので, 日本国内の, 少なくとも PHP コミュニティにおいてはまだあまり認知されていないように思えます.

記事にするにはまだまだ調べが足りないのですが, とりあえず日本の PHP コミュニティにも広まって欲しいと思い, 紹介してみることにしました.

Travis CI で CI するまでの手順

何はともあれ, 実際に GitHub のリポジトリを登録してみましょう.

大まかに以下のような流れとなります.

  1. GitHub でのアカウント登録
  2. PHP プロジェクトのリポジトリを用意
  3. リポジトリ内に専用の設定ファイル .travis.yml を用意
  4. GitHub アカウントでの OAuth により Travis CI にサインイン
  5. Travis CI のアカウント情報を GitHub のリポジトリの Service Hooks に登録
  6. GitHub リポジトリに git push

まずは, Travis CI にサインインします.
GitHub のアカウントさえあれば, OAuth で簡単にサインインできます.

PHP のプロジェクトは何でもいいですが, とりあえずは PHPUnit のテストが書かれているものが必要です.
ちょっと試したいけど手頃なリポジトリが無い, という場合は, 以下のリポジトリを fork してやってみるのもいいでしょう.

Travis CI 用の設定ファイル .travis.yml ファイルは, とりあえず以下のようなものを用意しましょう.
これを, リポジトリ上のルートディレクトリに配置します.

設定項目は他にもたくさんあるので, 詳細は公式ドキュメントの Building a PHP Project を参照してください.

これで, git push 時に PHP5.3 と PHP5.4 の環境でテストが実行されます.
(そういえばこの記事は PHP5.4 Advent Calendar でした)

テストの実行には明示していませんが, デフォルトでは phpunit コマンドが実行されます.
PHPUnit ではデフォルトで phpunit.xml を設定ファイルとして読み込むので, これもリポジトリルートに置いておきましょう.
(ところで, よく phpunit.xml.dist という名前にしているものがありますけど, あれってどういう意味なんでしょう...)

リポジトリの Service Hooks の指定, つまりこれは push 等のイベントの発生時に Travis CI に通知を行う機能ですが, これは Travis CI 上の画面から簡単にできます.
フリックスイッチを ON にするだけです.

[caption id="attachment_1556" align="aligncenter" width="300" caption="リポジトリの選択"]リポジトリの選択[/caption]

ですが, このリポジトリ一覧は最大で 30 しか表示されない問題があるようで, 登録したいリポジトリが表示されない場合は, 手動で登録する必要があります.
手動での登録は GitHub のリポジトリから Admin -> Service Hooks -> Travis と進み, 必要な情報を入力します.

Domain と User は通常空のままでいいので, Token のみ Travis CI の画面上からコピーし, Active にチェックを入れた上で, Update Settings します.

これで Travis CI で CI を行う準備が整いました.
あとは, git push すればそのタイミングでビルドが実行されるので, Travis CI の画面上で確認しましょう.
また, GitHub の Service Hooks の画面で Test Hook ボタンを押すことでもビルドが始まります.
なお, このときは GitHub 上でデフォルトで表示される設定に鳴っているブランチ (デフォルトは master) に対してビルドが行われるようです.

Travis CI のワーカの稼働状況によってはなかなか結果が反映されないこともあるので, ご注意ください.

以下は, 細かい設定について説明します.

vendor ディレクトリへの対応

開発しているプロジェクトが依存する外部ライブラリは, よく vendor というディレクトリに入れられています.
vendor ごとリポジトリに放り込むこともできますが, 通常は vendor は .gitignore に登録して, リポジトリには登録しないことが多いでしょう.

その場合, vendor ディレクトリに対象のライブラリを自動でインストールできる必要があります.

今回は, 最近開発中の Speciphy という BDD フレームワークを例に説明を行います.

今回は, Speciphy は PHPSpec に依存しているので, 事前に PHPSpec をインストールしておかないと, Travis CI 上でテストを実行することができません.
そこで, 以下のようなインストールスクリプトを, install-vendor.sh として用意します.

今回は, PHPSpec を pyrus.phar でインストールしています.
(pyrus.phar というのは次世代の pear コマンドです)
予め必要となる pear channel を登録しておくことで, 対話無しで全自動でインストールが完了します.

なお, Symfony2 だと git submodule を利用して vendor を管理していると聞きますが, これについてはあまり知らないので割愛します.
やり方はいろいろあると思うので, フレームワークに合わせたり, 自分の好きな方法を探してみてもいいでしょう.
(Composer を使うのも面白いですね)

そして, .travis.yml に, 先ほどのインストールスクリプトを, ビルドの前の準備として実行するよう, 追記しましょう.

pyrus による vendor 対応については以下の記事を参考にしました.

Behat によるテストも行う

デフォルトでは PHPUnit によるテストが実行されるのみですが, Behat によるテストも実行できるようにします.

テストとして実行されるコマンドは, .travis.yml で script という項目にコマンドを指定することで可能です.

ところで, ビルドの成功/失敗の判定は, コマンドの終了ステータスで行われているようです.
終了ステータスが 0 なら成功, それ以外なら失敗, という具合です.

つまり, 終了ステータスをきちんと指定しているテスティングフレームワークであれば, 基本的に何でも使えることになります.

そこで, テストを実行するためのスクリプトとして, 以下のような run-test.php を用意します.

環境変数 TESTING_FRAMEWORK が PHPUnit なら PHPUnit を実行し, Behat なら Behat を phar ファイルでダウンロードして実行する, というものです.
スクリプトの最後に終了ステータスを指定して exit しているので, テストの実行が成功/失敗のいずれでも適切に通知されます.

そして .travis.yml にも再度追記を行います.

今度は env という項目を追加しています.
これは, env を配列で複数指定することによって, その数だけテストを実行するというものです.

今回は PHP のバージョンに 5.3 と 5.4 の 2 種類, テスティングフレームワークに PHPUnit と Behat の 2 種類指定しているので, 2 x 2 で合計 4 パターンのビルドが行われます.
うまくいけば, 以下のような結果が得られます.
(設定は上記のものと多少異なります)

[caption id="attachment_1581" align="aligncenter" width="300" caption="Travis CI 実行結果"]Travis CI 実行結果[/caption]

PHP の Travis CI を支える技術

Travis CI では, テストを実行する際の環境の実行に phpenv が使われています.
また, PHP のビルドには当初 phpfarm が, そして今後は php-build に切り替わるようです.

どのように使われているかは, このあたりを見れば何となくわかると思います.
(ただし, master がデプロイされているとは限らないので, これが最新の状態とは限らないので注意は必要ですが)

phpenv と php-build については以前に以下のような記事を書いていますので, これらが参考になれば幸いです.

蛇足: オレはこう思う

Travis CI がどういう組織で作られているのかはまだよくわかってないのですが, GitHub のアカウントページを見る限りはそれぞれ所属はバラバラですし, IT ベンチャーとかではなく, コミュニティベースの活動による成果, ということなのでしょうか.

Travis CI の PHP 対応にあたっては, phpenvphp-build の作者である CHH さん, それらを Travis CI に取り入れるべく多くのパッチを提供した loicfrering さんを初め, 何人もの開発者が関わっているようです.

こんな風にありもののツールと, 多方面からの強力が実を結び, 今までには無い手軽さで CI 環境が手に入れられるようになったことは大変素晴らしいことだと思います.

Travis CI 自体もオープンソース活動をさらに加速させるものだと思いますし, これからの PHP コミュニティにも希望が持てると感じます.

フレームワークやライブラリを開発している皆さんは, 導入を検討してみてはいかがでしょうか.