GitLab.comにはGitLab-CI/CDが一緒になっているので、ライブラリ・パッケージ開発にも重宝してるのですが、悩ましいのが「バージョンのサポート」です。
それでも、変に新しすぎる機能を突っ込んでやや古いぐらいのバージョンに対応できるのは避けたいので、なるべく多くのバージョンでのテストを行うのが望ましいですね。
Travis-CIだと、テスト時の言語バージョンを割と簡単に複数指定できるのですが、GitLab-CI/CDだとどれだけ簡素に書けるかを少し調べてました。
TL;DR
- YAMLのアンカーとエイリアスが普通に使えるので、使いましょう。
- 具体例は、 「テストのみの処理」を共通化する(
before_script
は使わない) へ
以降の例
- ちょっと個人的興味を兼ねて手を出している、「PHPのEOLでないバージョン全てで動くパッケージを作る」でサンプルコードを書いてます
- 基本的にはどんな言語でもいけるはず
- テストコードとかも全く出す予定もなく、基本的にはYAMLが続きます
(ここからが本文)
まずは最新バージョンのテストジョブを定義する
phpunit:
image: php:7.2-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/download/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php
普通ですね。
GitLab-CI/CDは他のCIサービスなどと同じような感じで、.gitlab-ci.yml
にジョブを定義すると、git push
をトリガーにジョブが走ります。 1
複数バージョンに雑に対応させる
一番最初のケースではPHP 7.2での動作を前提としていますが、PHP7.1での動作もちゃんと保証させたいとします。
とりあえず、雑にジョブを増やしてみましょう。
phpunit-7.2:
image: php:7.2-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/download/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-7.1:
image: php:7.1-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/download/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
コピペするとすごく手早く増やせます。
ジョブの名前をphpunit-(PHPのバージョン)
にしておくことで、分かりやすいようにしておきましょう。
これぐらいだと、特にそんなに面倒な印象はありませんね。
さて、このままPHP 7.0, 5.6まで範囲を広げると、こうなります。
phpunit-7.2:
image: php:7.2-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-7.1:
image: php:7.1-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-7.0:
image: php:7.0-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-5.6:
image: php:5.6-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
うーん、かなり冗長ですね。
これ以上なにもしないなら問題はそこまで大きくはないです。
ただ、特殊なPHP拡張も必要なケースがあった場合は、毎回4箇所編集しないと駄目なのでよくありません。2
ジョブで共通する処理はまとめる
before_script:
- apk add git
- curl https://getcomposer.org/composer.phar > composer.phar
- php composer.phar install
phpunit-7.2:
image: php:7.2-cli-alpine
script:
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-7.1:
image: php:7.1-cli-alpine
script:
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-7.0:
image: php:7.0-cli-alpine
script:
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-5.6:
image: php:5.6-cli-alpine
script:
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
GitLab-CI/CDでは、全ジョブの実行時に各ジョブのscript
より前に実行したい共通処理をbefore_script
上に書くことができます。
これによって、「テスト時に同じ処理を共通化して編集コストを減らす」ことが実現できます 3
テスト以外があったら?
GitLab-CI/CDはCIだけでなくCDも視野に入れています。
だからというわけではないのですが、
- パッケージのリリースもGitLab-CI/CDを止めたい
- ユニットテストの前に文法チェック(
php -l
やphan
などなど)をやってNGならパイプラインを止めたい
などなど、単純なテスト以外のジョブも混ざってきます。
となってくると、before_script
にいろいろ詰め込みすぎると「リリースには全く必要がないのに、composer install
をしないといけない」などという事態になって、よろしくありません。
「テストのみの処理」を共通化する(before_script
は使わない)
phpunit-7.2: &phpunit-latest
image: php:7.2-cli-alpine
script:
- apk add git
- curl https://getcomposer.org/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-7.1:
<<: *phpunit-latest
image: php:7.1-cli-alpine
phpunit-7.0:
<<: *phpunit-latest
image: php:7.0-cli-alpine
phpunit-5.6:
<<: *phpunit-latest
image: php:5.6-cli-alpine
YAMLには「アンカー」と「エイリアス」という仕様があり、「&
でアンカーを定義」「*
でエイリアスとしてアンカーのデータ構造をまるっと参照」が可能です。
これによって、<<: *(名前)
によるアンカー先のデータをそのまま使いまわせるため、**「script
を記述せずにジョブを増やす」**ことが可能になります。
こんなことも可能です。(PHPのバージョンは同じだけど、 composerのバージョンを変える例)
phpunit-7.2: &phpunit-latest
image: php:7.2-cli-alpine
variables:
COMPOSER_VERSION: 1.7.2
script:
- apk add git
- curl https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar > composer.phar
- php composer.phar install
- vendor/bin/phpunit --bootstrap vendor/autoload.php tests/
phpunit-7.2-composer-1.6.5:
<<: *phpunit-latest
variables:
COMPOSER_VERSION: 1.6.5
これによって、「Laravelの複数バージョンにも対応している何か」も比較的テストしやすくなります。4
まとめ
ライブラリ開発のテストをCIなどでやると、記述内容の重複が起こってしまうケースがあります。
特に「Webアプリケーションフレームワークの拡張」を目的としたライブラリなどを開発していて、
可能な限り守備範囲を広げようとすると、言語バージョンxFWバージョンの掛け算でパターン数が爆発します。
YAMLの機能をきちんと使うことで、DRYでいけそうなシーンで適切にDRYな設定が書けるので、よりスリムなYAMLを目指したいと思います。
余談
前段で作ってるパッケージについて、ローカルで最低限のテストをパスしたプロジェクトを作って、↑の.gitlab-ci.yml
でCIを流した結果がこちら。
(テストコードの側で)PHP7.2から使えるメソッド使ってたりしてて、いきなり複数バージョン対応の大変さを思い知らされました。 5
外部リンク/参考情報など
- Travis-CIにおける、言語指定のドキュメント(代表としてPHP)
- YAMLのエイリアスでAnsibleファイルの重複を減らす
(YAML アンカー エイリアス
でググってたまたまトップにあった記事) - YAMLのAnchorとAliasを使ってconfigをDRYに書く
(YAML アンカー エイリアス
でググってたまたま2番目にあった記事) - YAMLのアンカーについての仕様