rcmdnk's blog
Last update

travisbadge

GitHubを使っていて他の人のレポジトリを見てる時によく見る このbuildのバッジが欲しくて Travis CIを使ってみました。

Travis CI

Travis CIのCIはContinuous Integrationの略で、 プログラムのビルドやらテストを継続的に行っていく事を指す プログラマ用語だそうです。

自分でも大事なものはテスト用のスクリプトを書いたりMakefileにテストを書いておいたりして ちょっと変更してはテストして、みたいなことをしていましたが、 Travis CIを使うとGitHubにコードをPushするのをトリガーにして 指定したコマンドを行ってくれる、とのこと。

Travis CIのサービス自体はGitHub専用ですが、コード自体は GitHubに公開されていて 1 自分でGitHub以外で使うことも出来ます。

同じようなサービスには

Jenkins CICircleCI 等いくつかありますが、 GitHubで気軽に行うにはTravis CIが一番簡単そうだったので 今回はTravis CIで。

Travis CIはパブリックなレポジトリを使う限りではフリーです。 プライベートレポジトリを使いたい場合には課金して使うことも出来ます2

Travis CIでの設定

GitHubのアカウントでサインアップ出来るので、サインアップすると アカウントの項目にRepositoriesのタブがるのでそこへ行くと 自分のGitHubのレポジトリ一覧があります。

travisaccount

そこで、CIしたいレポジトリのスイッチをONにしてあげれば 次にレポジトリにPushがあった時からコマンドが実行されます。

travisrepositories

スパナのマークをクリックするとレポジトリ事の設定項目に行けます。

travissettings

ここでBuild pushesBuild pull requestsはそれぞれ Pushがあった時とプルリクエストがあった時にコマンドを走らせるかどうかです。

また、一番上のBuild Only if .travis.yml is presentは デフォルトではOFFになってますが、 これだと.travis.ymlファイルが無いブランチでも無理やりデフォルトのテストを実行しようとして、 特に必要ないならONにしておいた方が良いです。

レポジトリ側での設定: .travis.yml

上にちょっと出てきましたが、どのようなコマンドを実行するかは レポジトリの中に.travis.ymlというファイルを作成し、 ここに指示を書きます。

基本的な設定はこんな感じ。

.travis.yml
1
2
3
4
5
6
7
8
language: ruby
rvm:
  - 1.9.3
  - 2.1.0
script:
  - ruby --version
  - echo test1
  - echo test2

languageで言語を指定して、その言語のバージョン等を設定、 そしてscriptの項目で実際に行いたいコマンドを記述します。

実行されるコマンドの流れ

実際に実行されるコマンドのメインの記述は上にある scriptの部分です。 ここでは複数のコマンドを指定することが可能です。

シェルスクリプトの様な記述ですが、 if [ ... ]みたいなテストコマンド的な物は(“クォートしないと”)使えません。

test command

一方、&&||なんかは使えます。

And/Or test

勿論、シェルスクリプトを作って、そのシェルスクリプトの中で testコマンドでもなんでも使えるので、 それをTravisで実行すれば基本的に何でも出来ます。

追記: 2014/09/08

ちょっと下のtwall/jna のyml等を見ていて気づきましたが、コマンドを指定するときに クォートしてあげることでtestコマンドなども使えるようになります。

Double quote test

Single quote test

w/o quote

if test

こんな感じでシングルクォートでもダブルクォートでも クォートで囲うとその中のコマンドをシェルスクリプトとして評価してくれるのか 普通に実行してくれます。

また、シェルスクリプトとかのクォートと違って、 シングルでもダブルでも中に書く変数は展開されます。

シングルクォートの中でダブルクォートを使ったり、 ダブルクォートの中で\を使ってダブルクォートを使ったりすることも 出来ます。

これでもう、そのままシェルスクリプト的になんでも書けますね。

ただ、1つ注意があって、1つの行で最後がエラーで終わってしまうと そこで最終的にもエラーが上がってしまうので(installとかだとそこで止まってしまう)

- '[ "$val" = "aaa" ] && echo $val || :'

みたく、もし途中までの判定で偽だったとしてもそれが起こっても良い場合には 最後に||を入れて:(もしくはtrue)コマンドを入れて 正常終了(返り値 0)として行を終わらせる必要があります。

追記ここまで

また、scriptの前後で実行されるコマンドの定義として、

  • before_install
  • install
  • before_script
  • script
  • after_success or after_failure
  • after_script

と言うコマンドの定義があります 3。 上の項目に指定されたコマンドは上の順番に実行されていきます。 (after_successafter_failureに関してはscriptまでで 成功してる時、失敗してる時に実行されるコマンド。)

コマンドの流れ

最初、before_sriptなんかはscriptの中の各コマンドの実行前に 行われるのかな?とか思ってたんですが、そんなことはなく 単に上に定義された順で呼ばれて、その中のコマンドも順番に呼ばれていくだけです。 また、上のテストの中で見れるように scriptの中でも、前に行ったコマンドの影響が残ります。 (変数を設定したりファイルを作ったり。)

また、これらのコマンドの前に、まず指定のレポジトリをcloneしてきて そのレポジトリ内に移動し、さらに言語に対する初期設定が行われます。

ですので、コマンドは基本、レポジトリ内に居るとして実行されるので、 レポジトリトップにtest.sh等を置いておけば、 ./test.shで実行できる事になります。

上のコマンド定義群の中で、 before_installinstallで失敗した(return値がnon 0)場合にはerroredbefore_scriptscriptafter_scriptで失敗した場合にはfailed のフラッグが付きます。

現時点ではafter_successafter_failureでの成否は最終結果に 影響しません。

また、script以外の部分ではコマンドが失敗したら直ちにそこで終了いなりますが、 scriptの部分では1つのコマンドが失敗しても次のコマンドに移って実行し、 全てのコマンドを実行した上で次のステップに移ります。

ただし、scriptの中でexitを使ってしまうとそこで全てジョブを終了します。

exit (0)

exit non zero (3)

ちょっと特殊で、普通に0を返すexitだとfailed、 0以外を返すとerroredとフラッグが付きます。

また、試しにreturnとかを使ってみましたが、これは普通にコマンドが無いということで その後もコマンドは続いた上でfailedになりました。

return

インストール作業なんかは何も考えずにscriptの中で全て自分でやってしまっても 良い部分も多々ありますが、 デフォルトでinstall部分で実行される物があったりするので それらに注意して設定する必要があります。

Python なんかだと、installには

pip install -r requirements.txt

が設定されてるので、requirements.txtが用意されてないと エラーにはなりませんが、見つかりませんでしたと言うメッセージが出てしまいます。

installを自分で定義された場合はそれは上書きされます。

python: install定義

python: install定義なし

言語設定

まず、languageで言語を指定しますが、 この言語で指定した以外のものが一切実行できないわけではなくて、 ここで指定したものについては複数のバージョンの環境を作って 別々に実行することが出来る様になります。

Rubyの場合だとrvmの項目をいくつか設定することで それぞれのRubyのバージョンが入った環境下での別々のビルド結果が作られます。

Pythonだと

.travis.yml
1
2
3
4
5
6
7
8
9
language: python
python:
  - 2.7
  - 3.2
script:
  - python --version
  - ruby --version
  - echo test1
  - echo test2

な感じでpythonでバージョン指定を行います。 このバージョン指定はドキュメントで言語によって ダブルクォートで囲ったり囲ってなかったりしますが、 少なくともPythonやRubyに関しては何方でも大丈夫でした。

また、この例ではRubyも使ってますが、 上に書いたようにlanguageで指定した物以外でも普通に実行できます。

ただし、ここでrvmの項目を書いてバージョンを指定しても無視されて デフォルトのバージョンが使われます。 (rvmで複数項目書いてもそれについては1つしかコマンドも実行されない) 4

また、languageについては以下のページにあるものだけが使えて、 それぞれ設定項目が決まっています 5

たまにlanguagebash等と書いて簡単なシェルスクリプトのテストを行ってる レポジトリもありますが、 Bashはサポートされてません。

この様にサポートされてない言語が指定されると デフォルトがRubyに設定されて居るようで、language: rubyの状態として 実行される様です。

以下はrvm1.9.32.1.0に指定して、 languageをそれぞれrubyaaapythonとして実行した結果です。

rubyaaaについてはそれぞれのRubyのバージョンで2ジョブ、 pythonに関してはデフォルトのRuby1.9.3のみの1ジョブが実行されています。

language: rubyでのテスト

language: aaaでのテスト

language: pythonでのテスト

なので、どうしてもRubyのバージョン2つ、Pythonのバージョン2つでそれぞれ テストしたい、とかの場合には どちらかの言語を自分でインストールする様にコマンドを書いて それを使う様にすれば出来ないことは無いと思います。

環境設定のマトリックス設定で別々のジョブを作れるので そこでバージョン指定してインストール時に使ったりすれば 殆どlanguageと同じように出来るかと。

また、次に詳しく下記ますが、言語の中でobjective-cだけは特別で、 この設定の時だけOSが変わるので注意が必要です。

OS設定

コマンドが実行される環境のOSはデフォルトでは Ubuntu 12.04 LTS Server Edition 64 bitになります。

この環境下ではapt-getなどのコマンドが使えて、それらで 環境を整える事が可能です。

OSを変更する方法は現在の所一つだけで、 上に書いたようにlanguageobjective-cにすること 6

これにするとOSはMacのOS X 10.9.2になります。 この環境下ではすでにHomebrewがインストールされていて brewコマンドで環境を整える事が可能です。

一時期、osという項目でlinuxosxとしてOSを選べたり、 両方でテストを行う事が可能になっていたみたいですが、 負荷が大きすぎたので現在はこの機能は少なくともフリーのアカウントでは停止されている様です 78

実際テストしてみるとこんな感じ。

language: objective-c

language: python

言語設定なし

The Build Environment に書いてあるTRAVIS_OS_NAMEという値は現在は空欄になっています。

ただ、有料の場合に使えるのか、それとも何か条件があるのか分かりませんが、 最近のジョブでもこのosの機能をきちんと使えてるものもあります。

twall/jna

試しに同じ様な物を作ってやってみましたが、やっぱりダメでした。

Test with .travis.yml like twall/jna

ので、現状Macの環境でテストをしたいときには取り敢えず 何をテストするでもlanguage: objective-cにしておく、 というのが定石です。

Macの中でさらに言語のバージョンを指定したい、とかの場合には、 仕方ないので自分で上に書いた複数言語で複数バージョンを使うみたいに 自分でインストールするコマンドを書けば一応使えることは使えます。

環境変数設定

Linux環境(言語Ruby)、OS X環境(Objective-C)でのそれぞれの デフォルトの環境変数は以下の様な物が最初に設定されています。

Linux環境

OS X環境

ここでもTRAVIS_OS_NAMEは定義されていますが空欄になっているのが分かります。

PATHを指定したり色々と環境変数を設定したい場合には before_scriptの中でexportしたりするか、もしくは envという項目を作ってその中で指定することも出来ます。

ただし、envはデフォルトではMatrix指定でそれぞれの項目ごとに ジョブを作るので注意が必要です。 envの指定した各項目ごとにジョブを作るので、 例えば

.travis.yml
1
2
3
4
env:
  - a=1
  - b=1
script: echo a=$a b=$b

みたいな事をすると、2つのジョブが出来て、最初の方では a=1 b=後のほうではa= b=1と言った結果になります。

Matrix指定と言っているのは、例えば

.travis.yml
1
2
3
4
5
6
7
8
language: python
python:
  - 2.7
  - 3.2
env:
  - a=1
  - b=1
script: echo a=$a b=$b

とすると、Pythonのバージョンが2つ、envの設定で2つのジョブが出来るので、

  • Python 2.7, env: a=1
  • Python 2.7, env: b=1
  • Python 3.2, env: a=1
  • Python 3.2, env: b=1

の4つのジョブが出来るからです。

他にもジョブを複数作る設定項目があれば掛け算でジョブの数が増えていきます。

envの設定で複数の項目を一度に指定したいときは、

env:
  - a=1 b=1

の様に空白で続けて書くことも出来ます。

また、全体で一つだけの設定で良ければ、globalという項目をenvの中で設定します。 この時にMatrix指定もしたいときにはmatrixという項目も作ってあげればOK。

env:
  global:
    - a=1
    - b=1
  matrix:
    - c=1
    - d=1

とすれば、

  • a=1, b=1, c=1
  • a=1, b=1, d=1

の2つの環境設定状態のジョブが出来る事になります。

env test

結果の通知手段

デフォルトでは結果が出た時の通知手段として

.travis.yml
1
2
3
4
5
notifications:
  email:
    recipients: <GitHub's user email>
    on_success: change
    on_failure: always

と言った感じの設定になっていて、 GitHubに登録してあるメールアドレスに結果の通知が来ます。

失敗した場合は毎回、成功した場合は、その前に失敗していた時に限り送られてきます。

on_successon_failureでは、それぞれalwaysneverchangeという 項目が設定できて、常にメールをするか、常にしないか、 もしくはその前の実行時とステータスが変わった時にみ送るか設定できます。

また、メール以外にも色々なサービスに直接通知を行うことも出来るみたいです。

あまり長い表示になる様なコマンドは実行できない

テストしてる時に沢山ログを吐くような物を作ったら

...
The log length has exeeded the limit of 4 Megabytes (this usually means that test suite is raising the some exception over and over).

The build has been terminated.

という表示とともに途中で終わってfailedになってしまいました。 (直前までは何も問題なく動いてた感じ。)

テストでも沢山ログを吐くような物も在るとは思いますが、 Travisの場合は4 MBまでということなので あまりに沢山ログを出すコマンドについてはファイルに出力するなどする必要があります。

インタラクティブなコマンドは避けるように

当然ですがインタラクティブに入力を求める様なコマンドは使えません。

そのような場合には10分程待って反応が無いとfailする、と言う仕組みになってるようです。 (入力待で待つのはタダの無駄。)

プログラムを作る場合にも-yオプションとかで全てyesを選べたり、 入力が必要な物も引数で全て渡すことも可能なように作る必要があります。

sudoは使える

OS Xでpip installするとPermission denied等と言われます。

この場合にはsudoがパスワードなしで使えるので、 sudo pip install...と書いておけばOK。

バッジを貼る

設定が出来てテストなどを行えたら結果の表示をしたいわけですが、 バッジはTravis CIにある各レポジトリの右上のバッジ部分をクリックすると バッジのURLなどが出てきます 9

travisgetbadge

いくつか表示形式が選べるので、 GitHubのREADMEなんかに貼るにはMarkdownを選んで貼るります。

そうするとよく見るこのボタンがREADMEにあらわれてくれます。

travisbadge

ブランチ毎にバッジのURLが違うのでそのブランチの物を使ってください。

バッジのURLの形式は

https://travis-ci.org/<user>/<repository>.svg?branch=<branch>)

の様な形になっています。

TravisにおけるレポジトリのURLは

https://travis-ci.org/<user>/<repository>

です。

e.g.:

[![Build Status](https://travis-ci.org/rcmdnk/travis-test.svg?branch=master)](https://travis-ci.org/rcmdnk/travis-test)

travisコマンド

Travis CI用にtravisというgemが用意されていて、 これをインストールすると、travisというコマンドが使えるようになって コマンドラインから色々情報を読んだり設定したり出来るようになります。

travis-ci/travis.rb

ただ、ウェブでの設定が簡単なのと、 結果をローカルでCUIで色々見たいのであれば、 そもそもGitHubにPushする前にローカルでテストを動かして結果を見れば。。。 という気もするので余り使うことはないかな、と。

Pushした際にコマンドを走らせないようにする

一度設定してしまうとPushするたびにTravis CIでコマンドが走るわけですが、 このコマンド実行をスキップさせたい場合には Pushするコミットの中のメッセージに[ci skip]、もしくは[skip ci]という 文字列を入れます 10

Travis CIのページがやたら重くなった時の対処

色々Travis CIで遊んでいたら、 Firefoxでのページ表示がやたら重くなって殆ど実用できない位になってしまいました。

ビルド結果がやたら長いものを幾つか行った後のことです。

同じ端末からGoogle Chromeで見ると大丈夫なのでFirefoxの問題かな?と思ってたんですが、 他の端末のFirefoxでは問題なく見れました。

そこでキャッシュを消去してみるとすぐに問題なく表示される様に。

ということで、恐らくGoogle Chromeでも同じ事が起こる可能性もあるのかと思いますが、 やたら長い結果を出すTravis CIのジョブを沢山見て表示が遅くなってきたかな? と思ったらキャッシュの消去を行うと解決出来る事があります。

まとめ

ちょっと長くなりましたが、基本的には

  • Travis CIのウェブページでコマンドを実行したいGitHubのレポジトリを有効化する。
    • Settingで.travis.ymlが内部ランチは無視するようにしておいた方が吉。
  • レポジトリに.travis.ymlというファイルを作ってコマンドの指示を書く。
    • script:の項目に実行したいコマンドを書く。
      • コマンドは普通にコマンドラインから実行するような(シェルスクリプト的な)感じで書く。
        • ただしtest(if [ ])コマンド的な物はクォートすれば使える。
    • 基本Linux環境だが、Macで行いたいとき(OS X/iOSのアプリやHomebrew関係のテストなど)は 実際にどの言語を使うかに依らず、language: objective-cを指定する。
  • 後はPushするたびに勝手に実行される。
  • バッジをレポジトリのREADMEに貼って、眺めて満足する。

と言った感じ。

Sponsored Links
  1. travis-ci

  2. Pricing Travis CI

  3. Travis CI: The Lifecycle of a Travis CI Build

  4. Travis CI: The Build Environment

  5. Travis CI: Getting started

  6. travis-core/os.rb

  7. これを知らずに暫くどうしてもbrewコマンドが使えなくて困った。。。

  8. Travis CI: Testing Your Project on Multiple Operating Systems

    The Travis CI Blog: Multi-OS Feature Available

    Support Windows and OS X · Issue #216 · travis-ci/travis-ci

  9. ちょっと前までだとこの項目がセッティング項目内(右の歯車ボタン)に Status Imagesという名前でありましたが 今はそちらにはなく、バッジ部分をクリックすることで取得できます。

  10. Travis CI: How to skip a build

Sponsored Links

« Google Chromeのメニューバー通知を消す Coverallsを使ってみた: GitHubのレポジトリにバッジを貼りたかったから2 »

}