hnwの日記

とあるPHP拡張のCI事情

PHP Advent Calendar 2017の3日目です。公開が遅くなってしまいました、ごめんなさい。


筆者はphp-timecopというPHP拡張を5年間ほどメンテナンスしています。このPHP拡張はCで書かれているのですが、Travis CIやAppVeyorなど複数のクラウドCIサービスを組み合わせてテストを回しています。本稿では各サービスをどのように利用しているか、それぞれの使い分けや特徴などを紹介していきたいと思います。

背景

本題に入るまえに、なぜPHP拡張でCIが必要か、という話を紹介します。


C言語で書かれたプログラムはポータビリティが高いような印象を持つ人が多いかもしれませんが、実際はむしろ逆で、ポータブルに書くのが難しい言語の1つです。Cではシステムコールやライブラリ関数を直接呼び出すのが普通ですが、これらは環境によって存在しないこともありますし、細かい挙動が異なることも珍しくありません。また、コンパイラごとの方言も存在します。


こうした事情から、C言語で書かれているOSSでは環境の差を埋めるためにAutoconf/Automake/Libtoolを利用するのが定番ですが、それでも未知の環境でリンクに失敗することも珍しくありませんし、環境の差を検出するためのconfigureスクリプトがバグで停止するようなことも起こりうるので、各環境でテストしておくに越したことはありません。


さらに面倒なことに、PHP拡張ではPHP本体のC APIを呼び出したり本体で定義されている構造体を操作したりすることがあるので、PHPの特定バージョンでだけ動かない、というようなバグがありえます。


要するに、PHP拡張をメンテナンスするためには、様々なOS、コンパイラPHPバージョンの組み合わせでテストをし続ける必要があるのです。ひとことで言えば地獄のような状況 ポジティブな言いかたをすると、とてもCIしがいのある題材だと言えるでしょう。

各CIサービスの特徴と実際の使い方

php-timecopで現在利用しているCIサービスは下記の4つです。


いずれのサービスも無料プランで利用しています。筆者自身がこのPHP拡張でお金を得ているわけではないのでお金を払ってまでCIする気にはならないですし、各社さんOSSへの応援の意味も込めて公開リポジトリのCIを無料で提供していると思うので、ありがたく使わせてもらっています。


以下、個別のサービスについて紹介します。

Travis CI

言わずと知れた最大手クラウドCIサービスです。

  • LinuxUbuntu)とmacOSの2つのOS上でCIできる
  • PHP 5.4から7.2までの6バージョンがビルド済み、設定で簡単に切り替えられる(記事執筆時)
  • 無料プランでは最大5並列でジョブが走る


無料プランでもかなりの大盤振る舞いなので、OSSでCIしたい場合は最初の選択肢になるでしょう。


また、PHP拡張のCIに関していうとPHPの全マイナーバージョンの最新版が提供されているのが素晴らしい点です。PHPではマイナーバージョンアップのタイミングで内部構造の大変更が入ることが珍しくない(5.3→5.4など)ので、全マイナーバージョンでテストするのは非常に重要です。それが設定を少し書くだけで簡単に対応できるのは本当に助かります。


実際、筆者はLinux環境上で7つのPHPバージョン*1のテストを走らせています。


Travis CIではビルド済みのPHPバイナリがZTS(Zend Thread Safe)有効になっているのが特徴的です。大抵の人はPHPNTS(Non Thread Safe)で利用しているはずなので、ある意味テスト環境として不適切と言えなくもありません。一方で、ZTSでだけビルドが通らないようなバグもありうるので、個人的にはテスト環境としてありがたいと感じます。

AppVeyor

Windows環境のCIが必要ならほぼ唯一の選択肢*2と言えるでしょう。

  • Windows環境でのビルドに対応
    • Visual C++も各バージョンがインストール済み
    • chocolateyやMSYS2-pacmanがインストール済みなので追加パッケージのインストールも簡単


PHPWindows版はバージョンごとに要求するVisual C++のバージョンが異なるため、自分でCI環境を準備するのは比較的面倒です。その意味で、クラウドCIサービスが利用できるのは良いですね。


php-timecopでは現在PHP 5.4、PHP5.6、PHP 7.1の3バージョンでテストを行っています。

Wercker

最近Oracleに買収されたクラウドCIサービスです。「わーかー」と読むらしいですね。CI環境を整えた当時、Dockerコンテナの扱いが一番楽そうだったので利用することにしました。


php-timecopではCentOSと32bit Ubuntuの2環境でのテストを走らせるのに利用しています。


CentOSでのテストは、specファイルによるバイナリパッケージ作成の動作確認のために行っています。通常Linuxディストリビューションごとのテストを行う必要性はあまり無いと思いますが、パッケージングのテストや、バイナリパッケージのデプロイなどの必要性があればディストリビューションの数を増やす必要があるでしょう。


一方、32bit Ubuntuでのテストは拡張自体のテスト目的です。PHPは32bit環境と64bit環境とで整数のサイズが変わるなど言語レベルで影響があるので、32bit環境でのテストには十分意味があります。

CircleCI

クラウドCIサービスの中でも有名どころの1つです。最近大幅バージョンアップしてコンテナベースになったと聞きましたが、筆者はまだ古いバージョンを利用しています。


現状ではNTS環境でのテストの意味で走らせているだけで、あまり活用できていません。

実際にCIは有用か?

PHP拡張でのCIを回してみて個人的には非常に有用だと感じています。客観的証拠のようなものは挙げにくいのですが、メリットだと思われる内容を何点か紹介します。


たとえば、新機能を実装するときなど、大変更の際はCIのおかげで特定バージョンでのバグを未然に防げることは珍しくありません。これまでの経験としてはOSやコンパイラの差で怒られることはそれほど多くなく、PHPの特定バージョンで動かないバグが多いので、まずはTravis CIで複数PHPバージョンでのテストを走らせるのがオススメです。


また、しっかりテストできているとPull Requestを受け取ったときの安心感も大きいので、その意味でもオススメです。


PHP 5時代はZTS環境で必要な「おまじない」の付け忘れにCIで気づく、ということが頻繁にありましたが、PHP 7になっておまじないが減ったので、最近はあまりそんなことはありません。


変わったところでは、Windows上でテストが失敗すると思ったらWindowsPHPのバグ(仕様?)だった、ということがありました。(参照:「PHPのsleep関数とusleep関数の挙動を調べてみた」)

まとめ

php-timecopのCIでは複数サービスを活用しており、有用だと感じているという話を紹介しました。また、テストの軸が複数あることも紹介しました。


何個もCIサービスを使うのは若干趣味的に思われるかもしれませんが、LinuxWindowsの両OSに対応するためにTravis CIとAppVeyorの2サービス併用、くらいまでは十分実用的だと思います。


PHP拡張を公開しているけどテストしてないという方はphp-timecopのリポジトリから設定ファイルをコピーすれば簡単に導入できますので、ぜひお試しください。

*1:PHP 5.4から7.2まで6バージョン、およびmasterブランチ

*2:本来ならVSTSが本命だと思うんですが、イマイチ知名度が低い気がします…