CircleCIのOnly build pull requestsをoffにしてもプルリクエストを作ったらJobを実行したい


この記事は CircleCI Advent Calendar 2018 2日目の記事です。
前回は @miyajan さんの CircleCI Orbs 入門 でした。

TL;DR

  • CircleCI の Only build pull requestsoff にしていると、プルリクエストを作った時に動いて欲しい CircleCI の job を設定できない
  • そこで GitHub の webhook を利用して、プリリクエストが作られたら CircleCI の Job を実行する serverless アプリ を作って設定した
  • CircleCI の Only build pull requests を off にした理由 と、作った serverless アプリと設定方法 ついて説明する

Only build pull requests とは

CircleCI には Only build pull requests という設定があります。
名前の通り、プルリクエストが作らられている時だけ CircleCI のビルドが実行されます。

この設定を on にしておけば、無駄にビルドして CircleCI のキューを圧迫することもなくて良いのですが、私は次の理由で off に設定しています。

CircleCI の Only build pull requests を off にする理由

master ブランチのデプロイを自動化したい

master ブランチが更新されたらデプロイしたいのですが、master ブランチからプルリクエストを作ることはないので、 Only build pull requestson になっていると CicleCI のビルドが実行されません。

さっさとテストを実行しておきたい

CircleCI で UT を実行していますが、これが少々時間がかかってしまいます。
なので push したらさっさとテストを実行して、プルリクエストを作る頃にはある程度終わってくれている方が嬉しいです。

Only build pull requests を off にする弊害

では Only build pull requests を off にすると万事解決かというと、そうもいきません。
私は次の問題に直面しました。

キューが詰まる

push する度にジョブが実行されるのですから、当然、ビルドのキューが増えて詰まります。
これはお金の力で解決しました

reviewdog が働かない

reviewdog というツールがあります。
これは、 rubocopgolint などの静的解析ツールの結果を、GitHubのプルリクエストにコードへのコメントとして登録してくれるツールです。
私はこのツールを使って機械的なコードレビューを自動化しています。↓のような感じです。

ただ、GitHubの プルリクエスト にコメントを登録するツールですから、プルリクエストがなければ機能しません。
しかし Only build pull requests を off にしていると、プルリクエストを作る前に CircleCI のビルドが実行されますから reviewdog を実行するとエラーになってしまいます。

対策として GitHub のプルリクエストが作られたら CircleCI のジョブをAPI経由で実行する仕組みを作ることにしました。

プルリクエストが作られたら reviewdog だけ実行する serverless アプリケーション

というわけで、プルリクエストが作られたら reviewdog だけを実行する仕組みを serverless で作りました。
↓の図の黒い部分です。

作った serverless アプリの説明

https://github.com/yasuhiroki/serverless-gh-hook-circle-job に公開しています。
AWS を使う前提の serverless です。

CircleCI の API トークンを設定

また、CircleCIのジョブをAPI経由で実行するにはトークンが必要なので取得します。
トークンは User setting -> Personal API tokens から取得できます。

取得したら serverless.yml に記述して deploy します。

functions:
  lint:
    handler: index.handler
    description: hook circleci job by web hook of github pull-request
    events:
      - http:
          path: circleci
          method: post
    environment:
      CIRCLECI_TOKEN: # ここに取得したトークンを書く
      TARGET_JOB: eslint
      TARGET_REPO: yasuhiroki/serverless-gh-hook-circleci-job
      IGNORE_BRANCHES: master

生トークンを書くことにリスクがあるのであれば AWS KMS を使うと良いですね。
今回の実装では使っていませんが、いずれ対応したいと思います。 1

API Gateway Endpoint を GitHub の webhook に登録

作った API Gateway の URL を GitHub の webhook に登録します。

プルリクエストのイベントだけ Hook されれば良いので、そのように設定しておきましょう。

CircleCI の config.yml を設定

reviewdog は プルリクエストが作られた時にだけ実行してほしいので config.yml を調整します。
プルリクエストが作られていれば環境変数 CI_PULL_REQUEST に値が入るのでチェックすれば良いですね。

serverless_golang_lint:
  steps:
    - run:
        name: Run golint and reviewdog
        command: |
          test ${CI_PULL_REQUEST} || exit 0 # プルリクエストが作られていない時は skip
          golint ./... |  tee golint.txt || true
          cat golint.txt | reviewdog -f=golint -reporter=github-pr-review

まとめ

CircleCI の Only build pull requestsoff にした状態で、プルリクエストが作られた時に CircleCI のジョブを実行する方法を serverless で実現してみました。

この方法以外にも、プルリクエストを作った後に適当なcommitをpushして CircleCI のビルドを走らせる方法でも良いかもしれませんね。

CircleCI Advent Calendar の明日の記事は @tomonorimatsumura さんです


  1. 自社ではKMSを使った実装にしていますが、ハードコーディングされてるので公開できず...