くりにっき

フルスタックキュアエンジニアです

CIマニアから見たGitHub Actions(Beta)の使い所

1ヶ月くらい使って勘所が見えてきたのでメモ

メリット

1リポジトリ辺り20並列までジョブを並列実行できる

これに尽きる。

CircleCIにしろTravis CIにしろorganization(user) *1単位で並列数が縛られているため、例えば同じuserの他のリポジトリでジョブが詰まっていると別リポジトリではqueueが詰まってジョブが実行されません。

しかしGitHub Actionsの場合リポジトリ単位で並列数が縛られているので、あるリポジトリで20並列ジョブ実行中だからといって他のリポジトリでジョブが詰まることがありません。

後の方でデメリットについて色々書いてるけど、このメリット1つだけで他のデメリットを帳消しにしてお釣りがくるくらいの破壊力はあります。(偏見)

ジョブ実行時はアクセストークンが勝手に設定されている

CIのジョブの中でGitHubAPIを使いたい場合、パーソナルアクセストークンを発行してsecretsとかにセットすると思うのですが、GitHub Actionsだと secrets.GITHUB_TOKEN に勝手にセットされているので発行する必要がないです。

特に会社系のorganizationの場合、誰かがジョブ実行用のパーソナルアクセストークンを発行してCIを設定してその後その人が退職してorganizationから外された後にCIが動かなくなるってことが割とよくあると思うのですが(僕は過去2~3回見てきました)、その心配がなくなるのは地味に嬉しいところ。

マトリクステストがやりやすい

Travis CIライクな記法が使えるのでマトリクスの軸が増えてもyamlが長大になりづらいというメリットがあります。

jobs:
  build:
    strategy:
      matrix:
        ruby:
          - v2.3.x
          - v2.4.x
          - v2.5.x
          - v2.6.x
        gemfile:
          - gemfiles/rails_4_0.gemfile
          - gemfiles/rails_4_1.gemfile
          - gemfiles/rails_4_2.gemfile
          - gemfiles/rails_5_0.gemfile
          - gemfiles/rails_5_1.gemfile
          - gemfiles/rails_5_2.gemfile
          - gemfiles/rails_6_0.gemfile

最初に書いた1リポジトリ辺り20並列の仕様もあるのでライブラリ作者的に嬉しい。

実際にGitHub Actionsに移行したプロダクト

時系列順に紹介

zatsu_monitor

github.com

元々はTravis CIを使ってたんですが、僕の他リポジトリで採用してる「gitのtagをpushしたらCI上でバイナリをビルドしてReleasesに自動アップロードするお手軽リリース」がなくて割と面倒でした。

そのうちTravis CIで実装しようかと思ってた矢先にGitHub Actionsが使えるようになったので移行してお手軽リリースを実装しました

activerecord-compatible_legacy_migration

github.com

僕がメンテしてるgemの中で2番目にマトリクス数がヤバかったやつ。(Travis CIで40個)

並列数は多いものの後述のindex_shotgunと違ってMySQLPostgreSQLのようなDBを使ってないのでGitHub Actionsで初マトリクステストやるには丁度よさそうな規模感と難易度と踏んで移行

index_shotgun

github.com

僕がメンテしてるgemの中で1番マトリクス数がヤバいやつ。

マトリクス数はTravis CIだけで50個くらいあって、歴史的経緯でCircleCIも併用してるのでそれも合わせると55個くらいあります。

なんでマトリクス数がこんなにヤバいのかというと「各Rubyのバージョン x 各activerecordのバージョン x 各DB(sqlite3, MySQL, PostgreSQL, Oracle)」でテストしてるため。

複数のCIを併用してる弊害として .circleci/config.yml だけ修正したのにTravis CI側でもジョブが実行されて30分くらい待たされるという事象が度々あったので、CIを1つにまとめたいとは思ってました。

実際にGitHub Actions対応を行ったのが下記PRですが、Railsで使えるdatabase.ymlをテストコードで使っているので普通のRailsアプリをGitHub ActionsでCIする時の設定は参考になると思います。

github.com

ちなみにGitHub Actions移行後はマトリクス数60個くらいですが、全部実行し終わるための所要時間は18分前後です。

デメリット

Betaクオリティなので大目に見てますが、大目に見た上でもここはつらいというところだけ書いてます

yamlのanchorが使えない

Railsのdatabase.ymlでよく見る

default: &default

development:
  <<: *default

的なやつ。

yamlファイルが一定規模以上になるとanchorとmergeを使って上記のようなリファクタリングをしてDRYにしたくなるのですが、GitHub Actionsだと「Anchors are not currently supported」というエラーになって使えなくてつらい。

f:id:sue445:20190909225037p:plain

anchorとmergeでリファクタリングできるのがYAMLの特徴だと思うんだけど、それが(まだ?)使えないのが残念。

マトリクステストだとSlack通知がつらい

GitHub ActionsにSlack通知機能が搭載されていなくてstepの中で自分でSlack APIを叩くかmarketplaceにあるSlack通知用のCheckを使うことになります。

しかしマトリクステストのstepsの中で直接Slack通知を行うと通知が大量にきてノイズになるので、全てのマトリクステストが終わった後に1回だけ成功か失敗かの通知を投げてほしいと思うのが人情ですが、それを実装するのが今のGitHub Actionsの仕様だと非常に面倒です。

上記要件を真面目に満たそうとするとこんな感じになります。

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        # 略

    steps:
      # 略

      - name: Slack Notification (not success)
        uses: homoluctus/slatify@v1.5
        if: "! success()"
        with:
          job_name: '*build*'
          type: ${{ job.status }}
          icon_emoji: ":octocat:"
          url: ${{ secrets.SLACK_WEBHOOK }}

  notify:
    needs: build
    runs-on: ubuntu-latest

    steps:
      - name: Slack Notification (success)
        uses: homoluctus/slatify@v1.5
        if: always()
        with:
          job_name: '*build*'
          type: ${{ job.status }}
          icon_emoji: ":octocat:"
          url: ${{ secrets.SLACK_WEBHOOK }}

needs を使えば前のジョブが全部終わった時に次のジョブを実行するというworkflowが組めるのでこれを使えばマトリクステストが全て終わった時にnotify ジョブで1回だけ通知を飛ばすということが簡単にできそうに見えます。

しかしneedsに書いたジョブが全て成功した時にしか実行されないので notify ジョブだけだとジョブ成功時の通知しか飛ばせません。

そのためマトリクステストの中で失敗時の通知を飛ばすような実装が必要になります。

GitHub ActionsのマトリクステストはTravis CIと違ってfail-fast(マトリクステストがどれか1つでも失敗したら他はスキップする)という仕様なのでジョブ失敗時の通知は1回しかこないように見えますが、同時に20並列もテストを実行してるとだいたい同時にテストが失敗するため失敗時の通知は複数飛んできます。

ジョブ失敗時はだいたい10個くらい同時に通知が飛るのですが、他にやりようがないので今は我慢してます、、、

f:id:sue445:20190909231009p:plain

合わせて読みたい

sue445.hatenablog.com

*1:ここで言ってるuserというのはジョブの実行者ではなくリポジトリの所有者の方です