TravisCIとCircleCIでちょっとずつ違うビルド環境の考え方の違い

tl;dr

TravisCI result CircleCI result
CI result

考え方

CircleCI

  • pushされたbranchをビルドする。

TravisCI

  • pushされたbranchをビルドする。
  • pull requestのbranchを、仮にmasterにmergeしてみて、ビルドする。

仮にmasterにmergeしてみて??

TravisCIの二つ目のがわかりにくくて、混乱する。 やっていることは正しいんだけど、挙動が直感から外れるので、 TravisCIヘビーに使っているユーザーにもわりと意味不明挙動扱いされやすい(要出典)

  • pull requestするとtravisのビルドがなぜか2個走る
  • (GitHub statusが最新結果しか取れなかった頃は)一度passしたはずのテストが、 なぜかあとからやってきたfailにかき消される passしたはずのテストはどこへ
  • .travis.yml でよく見る branches: only: - master ってなんだよ おれはmaster以外のテストもしたいぞ あれ、よくわからないが意図通りに動いてるぞ 何だこの設定

たとえばbranchを作った時のmasterと今のmasterが乖離していて、 pull requestのbranchのテストは全部通るけど、mergeしたらmasterが落ちる、 というのはよくあることだと思う。 そこを人間任せにするのか「ちゃんとmasterに追い付かせておいてね」、 仮にmergeしてみてtestするのか、は考え方による。 後者は便利に見えるが、なんで落ちたかよくわからない状況になることもありがち。

branchのpushでbuildされるとジャマ

となると単にbranchをpushしただけでbuildされるとジャマなので、

# .travis.yml
branches:
  only:
  - master

としたい。 これで、master branchへの変更、およびmasterに対してのpull requestに仮にmergeしてみて、が走る。 大きめのfeatureブランチを作っていて、 そのfeatureブランチに対するpull requestも仮にmergeしてみて、をしたい場合

# .travis.yml
branches:
  only:
  - master
  - feature/your-big-change

とすればよい。

なにかこのブランチが作られた時には、hookで何かしたい、などの場合は

# .travis.yml
branches:
  only:
  - master
  - /^cron_for_tachikoma\/.*/

とする。正規表現が使える。

pull request時の挙動

TravisCI

pull request作成時にhookで起動する。 条件を満たしていれば。

pull request済みのbranchに追加のcommitでも起動する。

CircleCI

pull request作成時にはhookで起動しない。 branchのpushを検知する側だけなので。

だから、pull requestに対して何かをしたい、例えばlintしてコメントをつけるとか、の場合、 さっさとpull requestをつくらないと、コメントをpostする先のpull requestがない状態で、 処理が終わってしまう。 branchをpushしてしばらくしてからpull request作成、 そのpull requestに追加でcommitしてpush、 この後者のpushのタイミングで初めてコメントが付くことになる。 ちょっと注意。

git操作での挙動

TravisCI

仮にmergeしてみる TravisCI式がいいことづくめか? というとそうでもない。

# TravisCI checkoutの挙動(logより)
$ git clone --depth=50 git://github.com/sanemat/ruby-example-rails-banana.git sanemat/ruby-example-rails-banana
$ cd sanemat/ruby-example-rails-banana
$ git fetch origin +refs/pull/2/merge:
$ git checkout -qf FETCH_HEAD

$ tig
13fac8f 2015-03-16 23:35 Murahashi Sanemat Kenichi M─┐ [HEAD] Merge c47af08fe6c62f6c95fa9dea440866d6348ac38
c47af08 2015-03-16 23:35 sanemat                   │ o Bundle update 20150316_23-34-12
37f6de7 2015-03-17 08:07 Murahashi Sanemat Kenichi o─┘ [master] {origin/HEAD} {origin/master} chore(tachiko
8b7d629 2015-03-17 07:58 Murahashi Sanemat Kenichi o chore(travis): add debug

$ git branch
* (detached from FETCH_HEAD)
  master

git 操作でなにかするアプリの場合、 あまりこれを想定した挙動になっていないことが多い。 それからたとえば、GitHubのapiと連携してpull requestにreview commentをつけたいので、 上のtigで言うc47af08を取りたい場合に、うってなる。 saddler-reporter-support-gitでは結構頑張って解決 している。 feature branchにmasterをmergeして追い付かせた時に正しく検出しなそう、とかある。

注) TravisCIに限れば、環境変数 TRAVIS_COMMITで該当コミットが取れる。

この「仮にmergeしてみる」は色々流派がありそうで、実際対TravisCIでは動いているけど、 他のパターンが有るときに全部解決してるかは自信ない。

pushされたbranchのビルド

Travisは必要最低限にチューンしてあるので、masterとかorigin/masterとかいない。 ちょっとだけ注意。

git checkout -b YOU_WANT_TO_CREATE origin/master

と手癖で打つとorigin/master無いので落ちる。

# TravisCI checkoutの挙動(logより)
$ git clone --depth=50 --branch=cron_for_tachikoma/56e24ecc-46bd-4d15-92fb-8d27fde41c8c git://github.com/sanemat/ruby-example-rails-banana.git sanemat/ruby-example-rails-banana
$ cd sanemat/ruby-example-rails-banana
$ git checkout -qf 8b7d6291495e947c7961e98dd9d89042eed2fd05

$ git branch
* (detached from 8b7d629)
  cron_for_tachikoma/56e24ecc-46bd-4d15-92fb-8d27fde41c8c

$ git branch -r
  origin/cron_for_tachikoma/56e24ecc-46bd-4d15-92fb-8d27fde41c8c
余談

checkout -b したいときは

git checkout -b YOU_WANT_TO_CREATE "${TRAVIS_BRANCH}"

か単に

git checkout -b YOU_WANT_TO_CREATE

とする。

CircleCI

CircleCIはおそらく事故防止のためにgit関連のログをwebからは表示しないので、 推測だけど

# CircleCI checkoutの挙動(推測)
$ git clone git://github.com/sanemat/ruby-example-rails-banana.git ruby-example-rails-banana
$ cd ruby-example-rails-banana
$ git checkout YOUR_TARGET_BRANCH

してるだけだと思う。 オーソドックス。 そして多分cloneは初回だけで、cacheしつつあとはfetchとcheckoutっぽい。 sshで入るとこんな感じなので。

$ git branch
  chore/use-tachikoma
  cron_for_github/530f0756-3664-4631-b5f4-eb53a842677a
* cron_for_tachikoma/bar-boo
  master
  pull/9
  tachikoma/update-20150222035312
  tachikoma/update-20150301035325

remote側も綺麗に全部見える(必要最低限ではなく富豪的)

$ git branch -r
  origin/HEAD -> origin/master
  origin/chore/use-tachikoma
  origin/cron_for_github/0e560ce0-6ae0-49b4-a8f9-26b335a752b1
  origin/cron_for_github/44f3c4e7-af74-4e29-bbbf-6c04e1b41048
  origin/cron_for_github/530f0756-3664-4631-b5f4-eb53a842677a
  origin/cron_for_github/5b9d9688-3844-42a4-b34a-3d519177e7b3
  origin/cron_for_github/foo-bar-baz
  origin/cron_for_tachikoma/148fbb23-dc21-461b-b207-5be874ea864f
  origin/cron_for_tachikoma/2212ad06-cdd2-4e77-8071-8552f1268d9e
  origin/cron_for_tachikoma/6344f6a9-2989-4352-9e5e-78ca1f71cc41
  origin/cron_for_tachikoma/bar-boo
  origin/master
  origin/pull/9
  origin/tachikoma/update-20150215035300
  origin/tachikoma/update-20150222035312
  origin/tachikoma/update-20150301035325
  origin/tachikoma/update-20150307_13-26-17
  origin/tachikoma/update-20150307_13-34-52
  origin/tachikoma/update-20150308_15-01-02
  origin/tachikoma/update-20150315035345
  origin/tachikoma/update-20150315_15-03-42

余談

これたまに git fetch origin --prune しないと膨れ上がりそう。 ログとかの都合上あったほうがいいのかもだから詳しくは調べてない。

まとめ

普通にやったらこうなる、を高速に解決しているのがCircleCI。 多分Jenkinsで自分でやったらこうなりそう、ただし低速。 ちゃんとやったらこうなる、を正しく解決しているのがTravisCI。 ただし、正しく理解しないと、適当に、じゃあこうかな? とやっても正解にたどり着けない。

drone.io, wercker, teamcity, codeship, あたりのメジャーどころは、 だいたいどっちかなんじゃないかな(未調査)、 カバーできてるんじゃないかな、カバーできてるといいなあ。 詳しい人に補足おねがいしたい!!

ローカルやCI上で動くはずのツールが、CIサービス上では動かないとき、 このへんの詰めが漏れてることが多いので e.g. pronto なぜか動かないときは、 ココらへんに気を配ってcontributeするとよいです。 なお、 checkstyle形式の結果をpull request review commentするsaddler などのpacksaddleプロダクトはその辺のCI間の差異を吸収して、使うときは考えなくていいように、作ってます。


参照